package ec.gp.semantic.geometry;

import ec.EvolutionState;
import ec.app.semanticGP.func.classification.An;
import ec.app.semanticGP.func.classification.Cn;
import ec.app.semanticGP.func.classification.Eq;
import ec.app.semanticGP.func.classification.If;
import ec.app.semanticGP.func.logic.And;
import ec.gp.GPNode;
import ec.gp.semantic.func.SimpleNodeBase;
import ec.gp.semantic.utils.ClassifierInstructionJoiner;
import ec.gp.semantic.utils.InstructionJoiner;
import library.semantics.TestCase;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

/**
 * @author Tomasz Pawlak
 */
public class ClassifierInterpolator implements IInterpolator<Byte> {

	private final SimpleNodeBase[] newTermCache = new SimpleNodeBase[1 << 16];

	public List<SimpleNodeBase<?>> getBasePrograms(EvolutionState state, List<TestCase<Byte>> points) {
		List<SimpleNodeBase<?>> programs = new ArrayList<>(points.size());

		for (TestCase<Byte> tc : points) {

			Byte[] arguments = tc.getArguments();
			List<SimpleNodeBase<?>> toJoin = new ArrayList<SimpleNodeBase<?>>();
			SimpleNodeBase<?> newTerm;

			for (int i = 0; i < arguments.length; ++i) {

				int key = (i << 8) | arguments[i];
				assert key < this.newTermCache.length;
				if (this.newTermCache[key] == null) {
					newTerm = new Eq();
					newTerm.children = new GPNode[]{An.get((byte) i), Cn.get(arguments[i])};

					this.newTermCache[key] = newTerm;
				}

				toJoin.add(this.newTermCache[key]);
			}

			newTerm = InstructionJoiner.join(toJoin, And.class, 2);

			If _if = new If();
			_if.children = new GPNode[]{newTerm, Cn.get(tc.getValue()), Cn.get(Cn.UNKNOWN_CLASS)};
			programs.add(_if);
		}

		return programs;
	}

	@Override
	public SimpleNodeBase<Byte> getProgram(EvolutionState state, List<TestCase<Byte>> points) {
		List<SimpleNodeBase<?>> programs = this.getBasePrograms(state, points);
		SimpleNodeBase<?> program = ClassifierInstructionJoiner.join(programs);

		return (SimpleNodeBase<Byte>) program;
	}
}
