package gp;

/**
 * @author Maximus
 *
 */
public class RegrIndividual extends GPTree implements InterfaceIndividual {
	static String term[] = { "1", "x", "pi" };
	static String unary[] = { "exp", "sin", "cos" };
	static String binary[] = { "+", "-", "*", "/" };

	int maxHeight = 5;
	int initHeight = 3;
	int modeCreate = MODE_GROW;
	int modeMutate = MODE_GROW;

	public RegrIndividual(GPTree parent, int h, int mode) {
		super(parent, h, mode);
	}

	public RegrIndividual(RegrIndividual y) {
		super(y);
	}

	public RegrIndividual(int h, int mode) {
		super(null, h, mode);
	}

	@Override
	public RegrIndividual clone() {
		return new RegrIndividual(this);
	}

	@Override
	public double evaluate() {
		double sum = 0;
		for (int i = -100; i < 101; i++) {
			Math.pow(2, getY(i) - getF(i));
		}
		return sum;
	}

	@Override
	public void crossover(InterfaceIndividual y) {
		super.crossover((GPTree) y, maxHeight);

	}

	@Override
	public void mutate(double pMut) {
		super.mutate(pMut, maxHeight, modeMutate);
	}

	@Override
	public String getStringRepresentation() {
		return super.toString();
	}

	@Override
	public void defaultInit() {
		initRandomTree(null, initHeight, modeCreate);

	}

	@Override
	public GPTree makeRandomSubtree(int h, int mode) {
		this.arity = ran.nextInt(3);
		switch (arity) {
		case 0:
			type = ran.nextInt(3);
		case 1:
			type = ran.nextInt(3);
			left = new RegrIndividual(this, h - 1, mode);
		case 2:
			type = ran.nextInt(4);
			left = new RegrIndividual(this, h - 1, mode);
			right = new RegrIndividual(this, h - 1, mode);
		}
		height = maxHeight - height;
		nodeCount += 1;// Ka wie ich das bestimmen soll.

		return this;
	}

	@Override
	public GPTree copyTree(GPTree tree) {
		return new RegrIndividual((RegrIndividual) tree);
	}

	@Override
	public double evalTreeAt(Object o) {
		RegrIndividual a = (RegrIndividual) o;
		double sum = 0;
		for (int i = -100; i < 101; i++) {
			Math.pow(2, a.getY(i) - a.getF(i));
		}
		return sum;
	}

	@Override
	public int getTermCount() {
		return term.length;
	}

	@Override
	public int getUnaryCount() {
		return unary.length;
	}

	@Override
	public int getBinaryCount() {
		return binary.length;
	}

	@Override
	public String getTermSymbol(int i) {
		return term[i];
	}

	@Override
	public String getUnarySymbol(int i) {
		return unary[i];
	}

	@Override
	public String getBinarySymbol(int i) {
		return binary[i];
	}

	private double getY(double x) {
		return x + Math.pow(2, x) + Math.pow(3, x) + Math.pow(4, x);
	}

	private double getF(double x) {
		RegrIndividual l = (RegrIndividual) left;
		RegrIndividual r = (RegrIndividual) right;

		switch (arity) {
		case 0:
			switch (getTermSymbol(type)) {
			case "1":
				return 1;
			case "x":
				return x;
			case "pi":
				return Math.PI;
			}
		case 1:
			switch (getUnarySymbol(type)) {
			case "exp":
				return Math.exp(l.getF(x));
			case "sin":
				return Math.sin(l.getF(x));
			case "cos":
				return Math.cos(l.getF(x));

			}
		case 2:
			switch (getBinarySymbol(type)) {
			case "+":
				return l.getF(x) + r.getF(x);
			case "-":
				return l.getF(x) - r.getF(x);
			case "*":
				return l.getF(x) * r.getF(x);
			case "/":
				return l.getF(x) / r.getF(x);

			}

		}
		throw new RuntimeException("wrong arity" + arity);

	}
}
