package ec.app.semanticGP.func.logic;

import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map.Entry;

import library.semantics.BitSet;
import library.semantics.TestCase;
import ec.EvolutionState;
import ec.gp.GPNode;
import ec.gp.semantic.BooleanSemantics;
import ec.gp.semantic.ISemanticProblem;
import ec.gp.semantic.ISemantics;
import ec.gp.semantic.func.NullaryNode;
import ec.gp.semantic.func.SimpleNodeBase;

public class AveragerSlim extends NullaryNode<Boolean> {

	private static final GPNode[] NO_CHILDREN = new GPNode[0];

	private final IdentityHashMap<SimpleNodeBase<Boolean>, Boolean> children = new IdentityHashMap<SimpleNodeBase<Boolean>, Boolean>();

	public AveragerSlim(EvolutionState state, SimpleNodeBase<Boolean> parent1, SimpleNodeBase<Boolean> parent2,
			boolean coefficient) {
		assert parent1 != null;
		assert parent2 != null;

		this.state = state;
		super.children = NO_CHILDREN;

		Boolean currentCoefficient;

		if (parent1 instanceof AveragerSlim) {
			for (Entry<SimpleNodeBase<Boolean>, Boolean> p1e : ((AveragerSlim) parent1).children.entrySet()) {
				currentCoefficient = (Boolean) this.children.get(p1e.getKey());

				if (currentCoefficient != null) {
					currentCoefficient |= (p1e.getValue() & coefficient);
				} else {
					currentCoefficient = p1e.getValue() & coefficient;
				}

				this.children.put(p1e.getKey(), currentCoefficient);
			}
		} else {
			currentCoefficient = (Boolean) this.children.get(parent1);
			if (currentCoefficient != null) {
				currentCoefficient |= coefficient;
			} else {
				currentCoefficient = coefficient;
			}
			this.children.put(parent1, currentCoefficient);
		}

		coefficient = !coefficient;

		if (parent2 instanceof AveragerSlim) {
			for (Entry<SimpleNodeBase<Boolean>, Boolean> p2e : ((AveragerSlim) parent2).children.entrySet()) {
				currentCoefficient = (Boolean) this.children.get(p2e.getKey());

				if (currentCoefficient != null) {
					currentCoefficient |= p2e.getValue() & coefficient;
				} else {
					currentCoefficient = p2e.getValue() & coefficient;
				}

				this.children.put(p2e.getKey(), currentCoefficient);
			}
		} else {
			currentCoefficient = (Boolean) this.children.get(parent2);
			if (currentCoefficient != null) {
				currentCoefficient |= coefficient;
			} else {
				currentCoefficient = coefficient;
			}
			this.children.put(parent2, currentCoefficient);
		}

		/*super.children = new GPNode[this.children.size()];
		int chIdx = 0;
		for (SimpleNodeBase<Boolean> ch : this.children.keySet()) {
			super.children[chIdx++] = ch;
		}*/

		// DEBUG
		/*{
			Averager avg = new Averager(parent1, parent2, !coefficient);
			assert this.execute().equals(avg.getSemantics());
		}*/
	}

	@Override
	protected ISemantics execute(ISemantics... arguments) {
		ISemanticProblem<Boolean> problem = (ISemanticProblem<Boolean>) this.state.evaluator.p_problem;
		List<TestCase<Boolean>> cases = problem.getFitnessCases();

		BitSet semantics = new BitSet(cases.size());
		BitSet entrySem;
		boolean coeff;
		BitSet coeffSet = new BitSet(cases.size());
		BitSet tmp;

		for (Entry<SimpleNodeBase<Boolean>, Boolean> entry : this.children.entrySet()) {

			entrySem = (BitSet) entry.getKey().getSemantics().getValue();
			coeff = (boolean) entry.getValue();
			coeffSet.set(0, cases.size(), coeff);

			tmp = ((BitSet) entrySem.clone());
			tmp.and(coeffSet);
			semantics.or(tmp);
		}

		return new BooleanSemantics(semantics);
	}

	@Override
	public Boolean[] invert(Boolean output, int missingArgIdx, Boolean... restOfArguments) {
		throw new UnsupportedOperationException("Not implemented");
	}

	@Override
	public String toString() {
		/*StringBuilder builder = new StringBuilder();
		for (Entry<SimpleNodeBase<Boolean>, Boolean> entry : this.children.entrySet()) {
			builder.append(String.format("%d & (%s) | ", entry.getValue() ? 1 : 0, entry.getKey().toStringForHumans()));
		}

		if (builder.length() > 0) {
			builder.delete(builder.length() - 2, builder.length());
		}

		return builder.toString();*/
		return "AVG";
	}

}
