package ec.gp.semantic.initialization;

import ec.EvolutionState;
import ec.gp.*;
import ec.gp.semantic.ISemantics;
import ec.gp.semantic.func.SimpleNodeBase;
import ec.gp.semantic.geometry.IConvexHullDetector;
import ec.gp.semantic.geometry.L2ConvexHullDetector;
import ec.util.Parameter;
import ec.util.ParameterDatabase;

/**
 * Competent Initialization for L1 metric as described in Tomasz Pawlak "Competent Algorithms for Geometric Semantic
 * Genetic Programming"
 * A node builder that iteratively generates new individuals and accepts them depending on they expands an L1 convex
 * hull built upon semantics of already existent individuals in population or not.
 *
 * @author Tomasz Pawlak
 */
public class CI extends SDI {

	private final static Parameter DEFAULT_BASE = new Parameter("CI");
	private final static String TRIES = "tries";
	private final static String DETECTOR = "detector";

	private IConvexHullDetector detector;

	protected int tries = 10;


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

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

		ParameterDatabase db = state.parameters;
		this.tries = db.getIntWithDefault(base.push(TRIES), defaultBase().push(TRIES), this.tries);
		this.detector = (IConvexHullDetector) db.getInstanceForParameter(base.push(DETECTOR), defaultBase().push(DETECTOR), IConvexHullDetector.class);
	}

	@Override
	public GPNode newRootedTree(EvolutionState state, GPType type, int thread, GPNodeParent parent, GPFunctionSet set, int argposition,
								int requestedSize) {

		SimpleNodeBase<?> candidate = null;
		ISemantics candidateSemantics = null;

		// a special case, population is empty
		if (this.detector.isEmpty()) {
			candidate = (SimpleNodeBase<?>) super.newRootedTree(state, type, thread, parent, set, argposition, requestedSize);
			this.detector.addSupportPoint(candidate.getSemantics());
			return candidate;
		}

		for (int i = 0; i < this.tries; ++i) {

			candidate = (SimpleNodeBase<?>) super.newRootedTree(state, type, thread, parent, set, argposition, requestedSize);
			candidateSemantics = candidate.getSemantics();

			if (!this.detector.isInConvexHull(candidateSemantics)) {
				break;
			} else if (i != this.tries - 1) {
				super.retract(candidate);
			}
		}

		this.detector.addSupportPoint(candidateSemantics);
		assert candidate != null;
		return candidate;
	}
}
