package ec.app.semanticGP.func.logic;

import java.util.List;

import ec.gp.GPNode;
import ec.gp.semantic.evaluation.EvaluationMode;
import ec.gp.semantic.evaluation.IChangeableEvaluationMode;
import library.semantics.BitSet;
import library.semantics.TestCase;
import ec.EvolutionState;
import ec.gp.semantic.BooleanSemantics;
import ec.gp.semantic.ISemanticProblem;
import ec.gp.semantic.ISemantics;
import ec.gp.semantic.func.NullaryNode;
import ec.util.Parameter;

public class InputN extends NullaryNode<Boolean> {
	private final static GPNode[] EMPTY_CHILDREN = new GPNode[0];
	private final static InputN[] CACHE = new InputN[256];
	
	protected final byte n;
	
	protected InputN(byte n) {
		this.n = n;
		this.children = EMPTY_CHILDREN;
	}
	
	public static InputN get(byte n) {
		int idx = n - Byte.MIN_VALUE; // handle negative values
		if (CACHE[idx] == null) {
			CACHE[idx] = new InputN(n);
		}
		return CACHE[idx];
	}
	
	@Override
	public boolean nodeEquals(GPNode node) {
		if (!(node instanceof InputN)) {
			return false;
		}
		
		InputN other = (InputN) node;
		return super.nodeEquals(node) && this.n == other.n;
	}
	
	@Override
	public int nodeHashCode() {
		return super.nodeHashCode() ^ this.n;
	}
	
	@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 :
				"InputN.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.";
		
		InputN cached = InputN.get(this.n);
		if (cached != this) {
			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());
		for (int i = 0; i < cases.size(); ++i) {
			semantics.set(i, cases.get(i).getArguments()[this.n]);
		}
		
		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 :
				"InputN.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 ("in" + this.n).intern();
	}
	
	/*@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;
	}*/
}
