package ec.gp.semantic.breed.deprecated;

import ec.BreedingPipeline;
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.EvoState;
import ec.gp.semantic.ISemantics;
import ec.gp.semantic.ISemanticProblem;
import ec.gp.semantic.func.SimpleNodeBase;
import ec.gp.semantic.library.ILibrary;
import ec.util.Parameter;

public abstract class MetricMutation extends MutationPipeline {

	private static final Parameter DEFAULT_BASE = GPBreedDefaults.base().push("metricMutation");
	protected static final String RANDOM = "random";

	protected ILibrary<?> library;

	protected double[][] transform;
	protected double[][] transformedOptimum;
	protected ISemanticProblem problem = null;

	protected boolean random = false;

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

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

		this.library = ((EvoState) state).getLibrary();
		this.problem = (ISemanticProblem) state.evaluator.p_problem;

		this.transform = new double[state.breedthreads][];
		this.transformedOptimum = new double[state.breedthreads][];
		for (int i = 0; i < state.breedthreads; ++i) {
			this.transform[i] = new double[((double[])problem.getTargetSemantics().getValue()).length];
			this.transformedOptimum[i] = new double[this.transform[i].length];
		}

		this.random = state.parameters.getBoolean(base.push(RANDOM), this.defaultBase().push(RANDOM), this.random);
	}

	@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

		//GPInitializer initializer = ((GPInitializer) state.initializer);

		// 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;

			// validity result...
			boolean res = false;

			// prepare the nodeselector
			nodeselect.reset();

			// pick a node

			GPNode p1 = null; // the node we pick
			GPNode p2 = null;

			for (int x = 0; x < numTries; x++) {
				// pick a node in individual 1
				p1 = nodeselect.pickNode(state, subpopulation, thread, i, i.trees[t]);

				// generate a tree swap-compatible with p1's position

				// create new tree
				p2 = this.getSubtree(state, thread, ((SimpleNodeBase<?>) i.trees[t].child).getSemantics(),
						((SimpleNodeBase<?>) p1).getSemantics(), this.maxDepth - p1.atDepth());

				assert verifyPoints(p2, p1);

				// p2 = builder.newRootedTree(state, p1.parentType(initializer), thread, p1.parent,
				// i.trees[t].constraints(initializer).functionset, p1.argposition, size);

				// check for depth and swap-compatibility limits
				res = verifyPoints(p2, p1); // p2 can fit in p1's spot -- the order is important!

				// did we get something that had both nodes verified?
				if (res)
					break;
			}

			GPIndividual j;

			if (sources[0] instanceof BreedingPipeline)
			// it's already a copy, so just smash the tree in
			{
				j = i;
				if (res) // we're in business
				{
					p2.parent = p1.parent;
					p2.argposition = p1.argposition;
					if (p2.parent instanceof GPNode)
						((GPNode) (p2.parent)).children[p2.argposition] = p2;
					else
						((GPTree) (p2.parent)).child = p2;
					j.evaluated = false; // we've modified it
				}
			} else // need to clone the individual
			{
				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 && res) // 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(p2, p1);
						j.trees[x].child.parent = j.trees[x];
						j.trees[x].child.argposition = 0;
						j.evaluated = false;
					} // 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;
	}

	protected abstract GPNode getSubtree(final EvolutionState state, final int thread, final ISemantics rootSemantics,
			final ISemantics intermediateSemantics, final int maxHeight);

}
