package ec.app.semanticGP.func.numeric;

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

import library.semantics.TestCase;
import ec.EvolutionState;
import ec.gp.GPNode;
import ec.gp.semantic.DoubleSemantics;
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<Double> {

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

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

	public AveragerSlim(EvolutionState state, SimpleNodeBase<Double> parent1, SimpleNodeBase<Double> parent2,
			double coefficient) {
		assert 0.0 <= coefficient && coefficient <= 1.0;
		assert parent1 != null;
		assert parent2 != null;

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

		Double currentCoefficient;

		if (parent1 instanceof AveragerSlim) {
			for (Entry<SimpleNodeBase<Double>, Double> p1e : ((AveragerSlim) parent1).children.entrySet()) {
				currentCoefficient = (Double) 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 = (Double) this.children.get(parent1);
			if (currentCoefficient != null) {
				currentCoefficient += coefficient;
			} else {
				currentCoefficient = coefficient;
			}
			this.children.put(parent1, currentCoefficient);
		}

		coefficient = 1.0 - coefficient;

		if (parent2 instanceof AveragerSlim) {
			for (Entry<SimpleNodeBase<Double>, Double> p2e : ((AveragerSlim) parent2).children.entrySet()) {
				currentCoefficient = (Double) 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 = (Double) 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<Double> ch : this.children.keySet()) {
			super.children[chIdx++] = ch;
		}*/
	}

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

		double[] semantics = new double[cases.size()];
		double[] entrySem;
		double coeff;
		double coeffSum = 0.0;

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

			entrySem = (double[]) entry.getKey().getSemantics().getValue();
			coeff = (double) entry.getValue();
			coeffSum += coeff;

			for (int i = 0; i < semantics.length; ++i) {
				semantics[i] += entrySem[i] * coeff;
			}
		}

		assert Math.abs(coeffSum - 1.0) < 1E-10;

		return new DoubleSemantics(semantics);
	}

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

	@Override
	public String toString() {
		/*StringBuilder builder = new StringBuilder();
		for (Entry<SimpleNodeBase<Double>, Double> entry : this.children.entrySet()) {
			builder.append(String.format("%.4f * (%s) + ", entry.getValue(), entry.getKey().toStringForHumans()));
		}

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

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