/* 
GeoGebra - Dynamic Mathematics for Everyone
http://www.geogebra.org

This file is part of GeoGebra.

This program is free software; you can redistribute it and/or modify it 
under the terms of the GNU General Public License as published by 
the Free Software Foundation.

 */

/*
 * AlgoMidPoint.java
 *
 * Created on 24. September 2001, 21:37
 */

package org.geogebra.common.kernel.algos;

import java.math.BigInteger;
import java.util.HashMap;
import java.util.HashSet;

import org.geogebra.common.euclidian.EuclidianConstants;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoPoint;
import org.geogebra.common.kernel.geos.GeoSegment;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.kernel.prover.AbstractProverReciosMethod;
import org.geogebra.common.kernel.prover.NoSymbolicParametersException;
import org.geogebra.common.kernel.prover.polynomial.PPolynomial;
import org.geogebra.common.kernel.prover.polynomial.PVariable;

/**
 *
 * @author Markus
 */
public class AlgoMidpointSegment extends AlgoElement
		implements SymbolicParametersAlgo, SymbolicParametersBotanaAlgo {

	private GeoSegment segment; // input
	private GeoPoint M; // output
	private GeoPoint P; // endpoints of segment
	private GeoPoint Q; // endpoints of segment

	private PPolynomial[] polynomials;
	private PVariable[] botanaVars;
	private PPolynomial[] botanaPolynomials;

	/**
	 * Creates new AlgoMidpointSegment
	 * 
	 * @param cons
	 *            construction
	 * @param segment
	 *            segment
	 */
	AlgoMidpointSegment(Construction cons, GeoSegment segment) {
		super(cons);
		this.segment = segment;

		// create new Point
		M = new GeoPoint(cons);
		setInputOutput();

		// compute M = (P + Q)/2
		compute();
	}

	@Override
	public Commands getClassName() {
		return Commands.Midpoint;
	}

	@Override
	public int getRelatedModeID() {
		return EuclidianConstants.MODE_MIDPOINT;
	}

	// for AlgoElement
	@Override
	protected void setInputOutput() {
		input = new GeoElement[1];
		input[0] = segment;

		super.setOutputLength(1);
		super.setOutput(0, M);
		setDependencies(); // done by AlgoElement
	}

	/**
	 * Created for LocusEqu
	 * 
	 * @return startpoint
	 */
	public GeoPoint getP() {
		return P;
	}

	/**
	 * Created for LocusEqu
	 * 
	 * @return endpoint
	 */
	public GeoPoint getQ() {
		return Q;
	}

	/**
	 * @return midpoint
	 */
	public GeoPoint getPoint() {
		return M;
	}

	// calc midpoint
	@Override
	public final void compute() {

		if (!segment.isDefined()) {
			M.setUndefined();
			return;
		}
		P = segment.getStartPoint();
		Q = segment.getEndPoint();
		boolean pInf = P.isInfinite();
		boolean qInf = Q.isInfinite();

		if (!pInf && !qInf) {
			// M = (P + Q) / 2
			M.setCoords((P.inhomX + Q.inhomX) / 2.0d,
					(P.inhomY + Q.inhomY) / 2.0d, 1.0);
		} else if (pInf && qInf) {
			M.setUndefined();
		} else if (pInf) {
			M.setCoords(P);
		} else {
			// qInf
			M.setCoords(Q);
		}
	}

	@Override
	final public String toString(StringTemplate tpl) {
		// Michael Borcherds 2008-03-30
		// simplified to allow better Chinese translation
		return getLoc().getPlainDefault("MidpointOfA", "Midpoint of %0",
				segment.getLabel(tpl));

	}

	@Override
	public SymbolicParameters getSymbolicParameters() {
		return new SymbolicParameters(this);
	}

	@Override
	public void getFreeVariables(HashSet<PVariable> variables)
			throws NoSymbolicParametersException {
		if (P != null && Q != null) {
			P.getFreeVariables(variables);
			Q.getFreeVariables(variables);
			return;
		}
		throw new NoSymbolicParametersException();
	}

	@Override
	public int[] getDegrees(AbstractProverReciosMethod a)
			throws NoSymbolicParametersException {
		if (P != null && Q != null) {
			int[] degreeP = P.getDegrees(a);
			int[] degreeQ = Q.getDegrees(a);

			int[] result = new int[3];
			result[0] = Math.max(degreeP[0] + degreeQ[2],
					degreeQ[0] + degreeP[2]);
			result[1] = Math.max(degreeP[1] + degreeQ[2],
					degreeQ[1] + degreeP[2]);
			result[2] = degreeP[2] + degreeQ[2];
			return result;
		}
		throw new NoSymbolicParametersException();
	}

	@Override
	public BigInteger[] getExactCoordinates(
			HashMap<PVariable, BigInteger> values)
			throws NoSymbolicParametersException {
		if (P != null && Q != null) {
			BigInteger[] pP = P.getExactCoordinates(values);
			BigInteger[] pQ = Q.getExactCoordinates(values);
			BigInteger[] coords = new BigInteger[3];
			coords[0] = pP[0].multiply(pQ[2]).add(pQ[0].multiply(pP[2]));
			coords[1] = pP[1].multiply(pQ[2]).add(pQ[1].multiply(pP[2]));
			coords[2] = pP[2].multiply(pQ[2]).multiply(BigInteger.valueOf(2));
			return coords;
		}
		throw new NoSymbolicParametersException();
	}

	@Override
	public PPolynomial[] getPolynomials() throws NoSymbolicParametersException {
		if (polynomials != null) {
			return polynomials;
		}
		if (P != null && Q != null) {
			PPolynomial[] pP = P.getPolynomials();
			PPolynomial[] pQ = Q.getPolynomials();
			polynomials = new PPolynomial[3];
			polynomials[0] = pP[0].multiply(pQ[2]).add(pQ[0].multiply(pP[2]));
			polynomials[1] = pP[1].multiply(pQ[2]).add(pQ[1].multiply(pP[2]));
			polynomials[2] = pP[2].multiply(pQ[2]).multiply(new PPolynomial(2));
			return polynomials;
		}
		throw new NoSymbolicParametersException();
	}

	@Override
	public PVariable[] getBotanaVars(GeoElementND geo) {
		return botanaVars;
	}

	@Override
	public PPolynomial[] getBotanaPolynomials(GeoElementND geo)
			throws NoSymbolicParametersException {
		if (botanaPolynomials != null) {
			return botanaPolynomials;
		}

		if (P == null || Q == null) {
			throw new NoSymbolicParametersException();
		}

		if (botanaVars == null) {
			botanaVars = new PVariable[2];
			botanaVars[0] = new PVariable(kernel);
			botanaVars[1] = new PVariable(kernel);
		}

		botanaPolynomials = SymbolicParameters.botanaPolynomialsMidpoint(P, Q,
				botanaVars);
		return botanaPolynomials;

	}

}
