package ec.gp.semantic.breed;

import ec.EvolutionState;
import ec.Individual;
import ec.gp.GPIndividual;
import ec.gp.GPNode;
import ec.gp.GPTree;
import ec.gp.breed.GPBreedDefaults;
import ec.gp.koza.MutationPipeline;
import ec.gp.semantic.ISemanticProblem;
import ec.gp.semantic.ISemantics;
import ec.gp.semantic.func.SimpleNodeBase;
import ec.gp.semantic.inverters.SemanticInverter;
import ec.gp.semantic.inverters.SemanticInverter.ErrorNodePair;
import ec.util.Parameter;

import java.lang.reflect.Constructor;

/**
 * Globally Geometric Semantic Mutation. The variant that chooses the best locus for replacement.
 *
 * @author Tomasz Pawlak
 */
@Deprecated
public class RDOBest extends MutationPipeline {

    private static Parameter DEFAULT_BASE = GPBreedDefaults.base().push("RDOBest");
    private SemanticInverter<?> inverter;

    @Override
    public Parameter defaultBase() {
        return DEFAULT_BASE;
    }

    @Override
    public void setup(EvolutionState state, Parameter base) {
        super.setup(state, base);

        try {
            Class<?> clazz = (Class<?>) state.parameters.getClassForParameter(base.push("inverter"), defaultBase()
                    .push("inverter"), SemanticInverter.class);
            Constructor<?> ctor = clazz.getConstructor(EvolutionState.class);
            this.inverter = (SemanticInverter<?>) ctor.newInstance(state);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int produce(int min, int max, int start, int subpopulation, Individual[] inds, EvolutionState state,
                       int thread) {
        // grab individuals from our source and stick 'em right into inds.
        // we'll modify them from there
        int n = sources[0].produce(min, max, start, subpopulation, inds, state, thread);

        // should we bother?
        if (!state.random[thread].nextBoolean(likelihood))
            return reproduce(n, start, subpopulation, inds, state, thread, false); // DON'T produce children from source -- we already did

        // now let's mutate 'em
        for (int q = start; q < n + start; q++) {
            GPIndividual i = (GPIndividual) inds[q];

            if (tree != TREE_UNFIXED && (tree < 0 || tree >= i.trees.length))
                // uh oh
                state.output
                        .fatal("GP Mutation Pipeline attempted to fix tree.0 to a value which was out of bounds of the array of the individual's trees.  Check the pipeline's fixed tree values -- they may be negative or greater than the number of trees in an individual");

            int t;
            // pick random tree
            if (tree == TREE_UNFIXED)
                if (i.trees.length > 1)
                    t = state.random[thread].nextInt(i.trees.length);
                else
                    t = 0;
            else
                t = tree;

            // prepare the nodeselector
            ISemantics parentSemantics = ((SimpleNodeBase<?>) i.trees[t].child).getSemantics();
            ISemanticProblem provider = (ISemanticProblem) state.evaluator.p_problem;
            inverter.invertTree(i.trees[t], provider.getTargetSemantics(), parentSemantics);

            // pick a node
            ErrorNodePair replacement = (ErrorNodePair) inverter.chooseNodeWithSmallestError((SimpleNodeBase<?>) i.trees[t].child,
                    this.maxDepth - 1 /* root is at depth 1 */);

            GPIndividual j = (GPIndividual) (i.lightClone());

            // Fill in various tree information that didn't get filled in there
            j.trees = new GPTree[i.trees.length];

            // at this point, p1 or p2, or both, may be null.
            // If not, swap one in.  Else just copy the parent.
            for (int x = 0; x < j.trees.length; x++) {
                if (x == t) // we've got a tree with a kicking cross position!
                {
                    j.trees[x] = (GPTree) (i.trees[x].lightClone());
                    j.trees[x].owner = j;
                    j.trees[x].child = i.trees[x].child.cloneReplacingNoSubclone(replacement.replacingNode,
                            replacement.currentNode);
                    j.trees[x].child.parent = j.trees[x];
                    j.trees[x].child.argposition = 0;
                    j.evaluated = false;
                    ((SimpleNodeBase<?>) j.trees[x].child).resetSemanticsRecursive();
                } // it's changed
                else {
                    j.trees[x] = (GPTree) (i.trees[x].lightClone());
                    j.trees[x].owner = j;
                    j.trees[x].child = (GPNode) (i.trees[x].child.clone());
                    j.trees[x].child.parent = j.trees[x];
                    j.trees[x].child.argposition = 0;
                }
            }

            // add the new individual, replacing its previous source
            inds[q] = j;
        }
        return n;
    }

}
