package ec.gp.semantic.breed;

import ec.EvolutionState;
import ec.Individual;
import ec.gp.GPIndividual;
import ec.gp.GPInitializer;
import ec.gp.GPNode;
import ec.gp.GPTree;
import ec.gp.koza.CrossoverPipeline;
import ec.gp.semantic.func.SimpleNodeBase;
import ec.gp.semantic.geometry.GeometricHelpers;
import ec.gp.semantic.statistics.ExtendedStatistics;
import ec.gp.semantic.statistics.Stopwatch;
import ec.util.Parameter;

/**
 * Crossover that returns given parents as offspring.
 */
public class DummyCrossover extends CrossoverPipeline {

    private ExtendedStatistics statistics;
    private Stopwatch selectionStopwatch = new Stopwatch();
    private int statsForGeneration = -1;

    @Override
    public void setup(EvolutionState state, Parameter base) {
        super.setup(state, base);
        this.statistics = (ExtendedStatistics) state.statistics;
    }

    @Override
    public int produce(int min, int max, int start, int subpopulation, Individual[] inds, EvolutionState state, int thread) {
        // how many individuals should we make?
        int n = typicalIndsProduced();
        if (n < min) n = min;
        if (n > max) n = max;

        // should we bother?
        if (!state.random[thread].nextBoolean(likelihood))
            return reproduce(n, start, subpopulation, inds, state, thread, true);  // DO produce children from source -- we've not done so already


        if (this.statsForGeneration != state.generation) {
            this.selectionStopwatch.reset();
            this.statsForGeneration = state.generation;
        }

        for (int q = start; q < n + start; /* no increment */)  // keep on going until we're filled up
        {
            this.selectionStopwatch.start();

            // grab two individuals from our sources
            if (sources[0] == sources[1])  // grab from the same source
                sources[0].produce(2, 2, 0, subpopulation, parents, state, thread);
            else // grab from different sources
            {
                sources[0].produce(1, 1, 0, subpopulation, parents, state, thread);
                sources[1].produce(1, 1, 1, subpopulation, parents, state, thread);
            }

            this.selectionStopwatch.stop();

            // at this point, parents[] contains our two selected individuals


            // add the individuals to the population
            inds[q] = parents[0];
            q++;

            GeometricHelpers.logGeometricity(1,
                    (SimpleNodeBase<?>) parents[0].trees[0].child,
                    (SimpleNodeBase<?>) parents[1].trees[0].child,
                    (SimpleNodeBase<?>) ((GPIndividual) inds[q - 1]).trees[0].child,
                    parents[0],
                    parents[1],
                    (GPIndividual) inds[q - 1],
                    state);

            if (q < n + start && !tossSecondParent) {
                inds[q] = parents[1];
                q++;

                GeometricHelpers.logGeometricity(1,
                        (SimpleNodeBase<?>) parents[0].trees[0].child,
                        (SimpleNodeBase<?>) parents[1].trees[0].child,
                        (SimpleNodeBase<?>) ((GPIndividual) inds[q - 1]).trees[0].child,
                        parents[0],
                        parents[1],
                        (GPIndividual) inds[q - 1],
                        state);
            }
        }

        this.statistics.getIteration().put("selectionTime", (double)this.selectionStopwatch.getTimeElapsed() * 1.0E-9);

        return n;
    }
}
