/*
 * Population.class
 * Part of package project
 *
 * Author: Philip Bradley
 *
 */

package project;


import java.util.Vector;
import java.util.Random;
import java.util.Enumeration;


public class Population {

  private Vector populationVector;
  private double lowestCost;
  private double highestCost;
  private double totalCost;
  private int maxPopSize;
  private Random numberGen;
  
  public Population(int capacity) {
    populationVector = new Vector(capacity);	
    maxPopSize = capacity;
    numberGen = new Random();
    highestCost = 0;
    lowestCost = Double.MAX_VALUE;
  }
  
  public boolean replace(Solution s) {
    int populationSize = populationVector.size();
    int index = 0;
    double currentCost = 0;
    boolean replaceOccured = false;
    
    if (populationVector.isEmpty()) {
      populationVector.addElement(s);
      totalCost = s.getCost();
      lowestCost = s.getCost();
      highestCost = s.getCost();
      replaceOccured = true;
    } else {
      if (s.getCost() < highestCost || (populationSize < maxPopSize)) {
	// Check for Solution of equal cost
	int i = populationSize;
	double cost = s.getCost();
	boolean foundEqualSolution = false;
	
	while ((i-- > 0) && (!foundEqualSolution)) 
	  foundEqualSolution = (cost == ((Solution)populationVector.elementAt(i)).getCost());
	
	if (!foundEqualSolution) {
	  
	  if (populationSize == maxPopSize) {
	    totalCost -= highestCost;
	    populationVector.removeElementAt(--populationSize);
	  }
	  
	  index = 0;
	  currentCost = ((Solution)populationVector.firstElement()).getCost();
	  while ((s.getCost() > currentCost) && (index < (populationSize - 1))) {
	    index++;
	    currentCost = ((Solution)populationVector.elementAt(index)).getCost();
	  }
	  
	  if (s.getCost() > currentCost) {
	    populationVector.addElement(s);
	  } else {
	    populationVector.insertElementAt(s,index);
	  }
	  
	  // Recalculate highest and lowest cost
	  lowestCost = ((Solution)populationVector.firstElement()).getCost();
	  highestCost = ((Solution)populationVector.lastElement()).getCost();
	  
	  totalCost += s.getCost();
	  replaceOccured = true;
	}
      }
    }
    return(replaceOccured);
  }
  
  public Solution[] getParents() throws Exception {
    
    int solutionIndex;	
    int randomIndex;
    int populationSize;
    double totalFitness;
    double currentFitness;
    Solution[] parents = new Solution[2];
    
    populationSize = populationVector.size();	
    
    if (populationSize != 0) {
      if (populationSize == 1) {
	solutionIndex = 0;
      } else {
	totalFitness = (populationSize * ((4*highestCost - lowestCost)/3)) - totalCost;
	randomIndex = (Math.abs(numberGen.nextInt()) % (int)totalFitness);
	
	solutionIndex = 0;
	currentFitness = fitness((Solution)populationVector.elementAt(solutionIndex));
	while (randomIndex > currentFitness) {
	  currentFitness += fitness((Solution)populationVector.elementAt(solutionIndex++));
	}
      }
      parents[0] = (Solution)populationVector.elementAt(solutionIndex);
      parents[1] = (Solution)populationVector.elementAt(++solutionIndex % populationSize);
      return(parents);
    } else {
      throw (new Exception("Population is empty"));
    }
  }
  
  public double fitness(Solution s) {
    return((((4 * highestCost) - lowestCost)/3) - s.getCost());
  }
  
  public int size() {
    return(populationVector.size());	
  }
  
  public Solution getBestSolution() {
    return((Solution)populationVector.elementAt(0));
  }
  
  public Solution getWorstSolution() {
    return((Solution)populationVector.lastElement());
  }
  
  public double getHighestCost() {
    return(highestCost);
  }

  public double getLowestCost() {
    return(lowestCost);
  }
  
  private void printCosts() {
    // For testing purposes
    
    Enumeration e = populationVector.elements();
    while (e.hasMoreElements()) {
      System.out.print(((Solution)e.nextElement()).getCost() + " ");
    }
    System.out.println("");
  }
  
  public static void main(String[] args) {
    
    Population p = new Population(10);
    GeometricGraph g = GeometricGraph.readFromFile("data/G200.0.2");
    Solution s;
    Solution[] parents = new Solution[2];
    double totalFitness = 0;
    
    try {
      for (int i=0; i<20; i++) {
	s = new Solution(g, 2);
	p.replace(s);
	System.out.println("Size: " + p.size() + " Best: " + p.lowestCost + " Worst: " + p.highestCost);
	p.printCosts();
	parents = p.getParents();
	System.out.println(" Parent cost: " + parents[0].getCost());	
	System.out.println(" Parent cost: " + parents[1].getCost());
      }
      
      /**
	*	System.out.println("Total Cost: " + p.totalCost);
	*	double tmp = 0;
	*	for (int i=0; i<p.size(); i++) {
	*		System.out.println(((Solution)p.populationVector.elementAt(i)).getCost());
	*		tmp += ((Solution)p.populationVector.elementAt(i)).getCost();
	*	}
	*	System.out.println("Total Cost: " + tmp);
	*	
	*	totalFitness = (p.populationVector.size() * ((4*p.highestCost - p.lowestCost)/3)) - p.totalCost;
	*	System.out.println("Total Fitness: " + totalFitness);
	*	tmp = 0;
	*	for (int i=0; i<p.size(); i++) {
	*		System.out.println(p.fitness((Solution)p.populationVector.elementAt(i)));
	*		tmp += p.fitness((Solution)p.populationVector.elementAt(i));
	*	}
	*	System.out.println("Calculated by summing individuals: " + tmp);
	*/
      
    } catch (Exception e) {
      System.out.println("Error: " + e.toString() + " " + e.getMessage());
    }
  }
}

