package ec.app.semanticGP.func.logic;

import java.util.*;

import ec.gp.semantic.evaluation.EvaluationMode;
import ec.gp.semantic.evaluation.IChangeableEvaluationMode;
import library.semantics.*;
import library.semantics.BitSet;
import ec.*;
import ec.app.semanticGP.func.*;
import ec.gp.*;
import ec.gp.semantic.*;
import ec.gp.semantic.func.*;

public class Constant extends NullaryNode<Boolean> implements IConstant<Boolean> {
	private final static GPNode[] EMPTY_CHILDREN = new GPNode[0];
	private final static Constant TRUE = new Constant(true);
	private final static Constant FALSE = new Constant(false);

	private final boolean value;

	protected Constant(boolean value) {
		this.value = value;
		this.children = EMPTY_CHILDREN;
	}

	public static Constant get(boolean value) {
		return value ? TRUE : FALSE;
	}

	@Override
	protected ISemantics execute(ISemantics... arguments) {
		// HACK: see assertion
		
		assert !(this.state.evaluator.p_problem instanceof IChangeableEvaluationMode) ||
				((IChangeableEvaluationMode) this.state.evaluator.p_problem).getEvaluationMode() == EvaluationMode.TrainingSet :
				"Constant.execute() is a hack to not recalculate semantics for input nodes as these are immutable. " +
						"However current implementation assumes that Boolean problems use only training set. If that " +
						"no longer holds, one needs to distinguish between training and test set semantics.";
		
		Constant cached = Constant.get(this.value);
		if (cached != this) {
			assert cached.getSemantics().isConstant();
			return cached.getSemantics();
		}
		
		// End of HACK
		
		ISemanticProblem<Boolean> problem = (ISemanticProblem<Boolean>) this.state.evaluator.p_problem;
		List<TestCase<Boolean>> cases = problem.getFitnessCases();

		BitSet semantics = new BitSet(cases.size());
		semantics.set(0, semantics.length(), value);
		return new BooleanSemantics(semantics);
	}
	
	@Override
	public void resetSemantics() {
		assert !(this.state.evaluator.p_problem instanceof IChangeableEvaluationMode) ||
				((IChangeableEvaluationMode) this.state.evaluator.p_problem).getEvaluationMode() == EvaluationMode.TrainingSet :
				"Constant.resetSemantics() is a hack to not recalculate semantics for input nodes as these are immutable. " +
						"However current implementation assumes that Boolean problems use only training set. If that no " +
						"longer holds, one needs to distinguish between training and test set semantics.";
	}
	
	@Override
	public String toString() {
		return this.value ? "1" : "0";
	}

	@Override
	public int nodeHashCode() {
		return this.getConstant().hashCode();
	}

	@Override
	public boolean nodeEquals(GPNode node) {
		if (!(node instanceof IConstant))
			return false;

		IConstant<Boolean> other = (IConstant<Boolean>) node;

		assert (this.getConstant() == other.getConstant() && this.nodeHashCode() == node.nodeHashCode())
				|| this.getConstant() != other.getConstant();

		return this.getConstant() == other.getConstant();
	}

	@Override
	public Boolean getConstant() {
		return this.value;
	}
	
	/*@Override
	public Object clone() {
		// this object is immutable, we don't need to copy its memory
		return this;
	}

	@Override
	public GPNode lightClone() {
		// this object is immutable, we don't need to copy its memory
		return this;
	}

	@Override
	public int atDepth() {
		// we cannot determine it, since all clones are the same object
		return Integer.MIN_VALUE;
	}*/
}
