package ec.gp.syntactic.simplification.numeric;

import ec.EvolutionState;
import ec.app.semanticGP.func.IConstant;
import ec.app.semanticGP.func.numeric.Constant;
import ec.app.semanticGP.func.numeric.Mul;
import ec.app.semanticGP.func.numeric.Sum;
import ec.gp.GPNode;
import ec.gp.syntactic.simplification.IRule;
import ec.util.Parameter;

class SumMulMulRule implements IRule {

	private EvolutionState state;

	@Override
	public void setup(EvolutionState state, Parameter base) {
		this.state = state;
	}

	@Override
	public boolean match(GPNode subtree) {
		if (!(subtree instanceof Sum))
			return false;

		if (!(subtree.children[0] instanceof Mul && subtree.children[1] instanceof Mul))
			return false;

		return (
				subtree.children[0].children[0] instanceof IConstant
						&& subtree.children[1].children[0] instanceof IConstant
						&& subtree.children[0].children[1].rootedTreeEquals(subtree.children[1].children[1])
				)
				||
				(
				subtree.children[0].children[0] instanceof IConstant
						&& subtree.children[1].children[1] instanceof IConstant
						&& subtree.children[0].children[1].rootedTreeEquals(subtree.children[1].children[0])
				)
				||
				(subtree.children[0].children[1] instanceof IConstant
						&& subtree.children[1].children[1] instanceof IConstant
						&& subtree.children[0].children[0].rootedTreeEquals(subtree.children[1].children[0])
				)
				||

				(subtree.children[0].children[1] instanceof IConstant
						&& subtree.children[1].children[0] instanceof IConstant
						&& subtree.children[0].children[0].rootedTreeEquals(subtree.children[1].children[1])
				);

	}

	@Override
	public GPNode replace(GPNode subtree) {
		assert match(subtree) : "The given tree is not a form of (const1 * p) + (const2 * p)";

		double v1;
		double v2;
		Constant constant;
		Mul mul = new Mul();

		if (subtree.children[0].children[0] instanceof IConstant
				&& subtree.children[1].children[0] instanceof IConstant) {

			v1 = ((IConstant<Double>) subtree.children[0].children[0]).getConstant();
			v2 = ((IConstant<Double>) subtree.children[1].children[0]).getConstant();
			constant = new Constant(v1 + v2);

			mul.children = new GPNode[] { constant, subtree.children[0].children[1] };

		} else if (subtree.children[0].children[0] instanceof IConstant
				&& subtree.children[1].children[1] instanceof IConstant) {

			v1 = ((IConstant<Double>) subtree.children[0].children[0]).getConstant();
			v2 = ((IConstant<Double>) subtree.children[1].children[1]).getConstant();
			constant = new Constant(v1 + v2);

			mul.children = new GPNode[] { constant, subtree.children[0].children[1] };

		} else if (subtree.children[0].children[1] instanceof IConstant
				&& subtree.children[1].children[1] instanceof IConstant) {

			v1 = ((IConstant<Double>) subtree.children[0].children[1]).getConstant();
			v2 = ((IConstant<Double>) subtree.children[1].children[1]).getConstant();
			constant = new Constant(v1 + v2);

			mul.children = new GPNode[] { constant, subtree.children[0].children[0] };

		} else {
			assert (subtree.children[0].children[1] instanceof IConstant && subtree.children[1].children[0] instanceof IConstant);

			v1 = ((IConstant<Double>) subtree.children[0].children[1]).getConstant();
			v2 = ((IConstant<Double>) subtree.children[1].children[0]).getConstant();
			constant = new Constant(v1 + v2);

			mul.children = new GPNode[] { constant, subtree.children[0].children[0] };
		}

		return mul;
	}

}
