/*
 * @(#) Generator.java 2003/03/22
 */

import java.util.Arrays;

/**
 * Implements one iteration of the Genetic Algorithm. Select, crossover
 * mutate, evaluate.
 * @author Sushil J. Louis
 */
public class Generator implements java.io.Serializable {

    /**
     * Generation that is being evolved/executed.
     */
    public int generation;


    /**
     * Implements how an individual is chosen from population.
     */
    Selector selector;

    /**
     * Implements both crossover and mutation.
     */
    Recombiner recombiner;

    /**
     * The application specific evaluation function that has the method eval.
     */
    CigarApp app;
   
    /**
     * Constructor. Simply stores references to its parameters locally.
     * 
     * @param s A {@link Selector} that takes a population and 
     * returns the index of the selected individual
     * @param r A {@link Recombiner} that implements crossover and mutation
     * @param a A {@link CigarApp} that evaluates the newly formed individuals
     */
    public Generator (Selector s, Recombiner r, CigarApp a){
	this.selector = s;
	this.recombiner = r;
	this.app = a;

    }

    /**
     * Implements one generation, note that the population size must be even.
     * This implements the following loop <br> 
     * select two parents <br>
     * crossover <br>
     * mutate <br>
     * Then it evaluates the newly created offspring and makes the next
     * generation by choosing the best {@link Population}.popsize individuals 
     * from the combined parent offspring population.
     * 
     * @param pop An evaluated GA population. Assumes that a statistician has
     * calculated all statistics for this population. That is, make sure  that 
     * statistics have been calculated for {@link Population.currentPop} BEFORE
     * this method is invoked.
     * @param gen the current generation
     */
    public void execute(Population pop, int gen) throws ApplicationException {
	generation = gen;
	int parent1, parent2;
	
	for(int i = pop.popSize; i < pop.popSize * pop.lambda; i += 2){
	    parent1 = selector.select(pop);
	    parent2 = selector.select(pop);
	    recombiner.recombine(pop, parent1, parent2, i, i + 1);
	}
	pop.evaluate(app, pop.popSize, pop.popSize * pop.lambda);
	chooseBest(pop);
	return;
    }


    private void chooseBest(Population pop) {
	Arrays.sort(pop.currentPop);
	shuffle(pop);
	return;
    }

    private void shuffle(Population p){
	for(int i = 0; i < p.popSize/2; i++){
	    swap(GARandom.rnd(0, p.popSize-1), 
		 GARandom.rnd(0, p.popSize-1), p);
	}
    }

    private void swap(int from, int to, Population p){
	Individual tmp;
	tmp = p.currentPop[from];
	p.currentPop[from] = p.currentPop[to];
	p.currentPop[to] = tmp;
    }


    private void evalPopulation(Population p, int start, int end)
	throws ApplicationException {
	Individual member;

	for(int i = start; i < end; i++){
	    member = p.currentPop[i];
	    member.fitness = app.eval(member);
	}
    }

}
