/*
 * Population.java 2003/03/15
 *
 *
*/



import  java.util.Properties;

/*
 * The genetic algorithm's population. Needs to manage the genetic
 * algorithm's data, initialize the ga, and report initial statistics.
 * Implementing java.io.Serializable makes it possible to save a
 * population's state and restore it later. Easily.
 */
public class Population implements java.io.Serializable {

    public double max, min, avg;

    public int maxi, mini;
    
    public double bestObjective, worstObjective, avgObjective;
    public double sumFitness;

    public int bigGen = -1, bigIndex = -1;
    public double bigMax = -1.0;

    public Individual[] currentPop;

    public int chromosomeLength;

    public int maxGen;
    public int popSize;
    public int generation;
    public int lambda;

    public String phenotypeOutputFilename;
    public String outputFilename;
    public String baseFilename;
    public String errorOutputFilename;
    public String fitnessOutputFilename;
    public String genotypeOutputFilename;

    /*
     * Initializes population with values from properties
     * object. Allocates population, and generate output filenames.
     *
     * @param props the {@link Properties} object that contains our
     * key-value pairs of parameters
     * @param clen the length of the chromosome. This should have been
     * calculated by the application {@link CigarApp}
     */
    public Population(Properties props, int clen, int maxGen) {
	popSize = Integer.parseInt(props.getProperty("PopulationSize", "100"));
	lambda  = Integer.parseInt(props.getProperty("Lambda", "2"));
	this.maxGen = maxGen;

	allocatePopulation(popSize, lambda, clen);


	
		


    }

    /*
     * Default population, lambda, and chromosome length for the
     * default problem of one-max
     */
    public Population() {
	allocatePopulation(100, 2, 100);
    }

    private void allocatePopulation(int popsize, int lambda, int len) {
	currentPop = new Individual[popsize * lambda];
	for(int i = 0; i < popsize * lambda; i++){
	    currentPop[i] = new Individual(len);
	    // constructor randomly initializes chromosome
	}
    }

    /*
     * Evaluates the members of a population. <code> start </code> and
     * <code> end </code> specify the start-index and end-index, in
     * the current population array of individuals to be evaluated.
     *
     * @param app the application object
     * @param start start index in population array (inclusive)
     * @param end   end index in population array (exclusive)
     *
     * @throws ApplicationException if there is a problem in the evaluation
     */
    public void evaluate(CigarApp app, int start, int end) 
	throws ApplicationException {

	if(start < 0 || end > lambda*popSize) {
	    System.err.println("Start: " + start + " End: " + end);
	    throw new ApplicationException("Population evaluation indices out of bounds");
	}
	for(int i = start; i < end; i++){
	    currentPop[i].fitness = app.eval(currentPop[i]);
	}
    }

    /*
     * Calls application specific initialization method to get
     * application specific objects ready to go (reading in data for
     * example) on every individual and then evaluates all individuals
     * in the current population. Gathers statistics, then reports on
     * progress
     *
     * @param app the application object
     * @param stat A {@link Statistician} that knows how to gather
     * population statistics
     * @param reporter A {@link Reporter} that knows how to output
     * statistics in formats required by our analysis scripts.
     *
     */
    public void initialize(CigarApp app, Statistician stat, Reporter reporter) 
	throws ApplicationException {
	app.initialize(this);
	for(int i = 0; i < this.popSize; i++){
	    app.appInitChromosome(currentPop[i], this);
	}
	evaluate(app, 0, this.popSize);
	stat.statistics(this);
	app.initPhenoPrint(this, phenotypeOutputFilename); 
	// only the application knows how to print the phenotype

	


	reporter.report(this, app, 0);
    }
    
}
