/*

Copyright 2006 Rene Grothmann, modified by Eric Hakenholz

This file is part of C.a.R. software.

    C.a.R. is a 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, version 3 of the License.

    C.a.R. is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

 */


 package rene.zirkel.expression;

import java.util.Enumeration;

import rene.util.MyVector;
import rene.gui.Global;
import rene.zirkel.construction.Construction;
import rene.zirkel.construction.ConstructionException;
import rene.zirkel.construction.DepList;
import rene.zirkel.objects.AngleObject;
import rene.zirkel.objects.ConstructionObject;
import rene.zirkel.objects.EquationXYObject;
import rene.zirkel.objects.Evaluator;
import rene.zirkel.objects.FunctionObject;
import rene.zirkel.objects.InsideObject;
import rene.zirkel.objects.PointObject;
import rene.zirkel.objects.PrimitiveLineObject;
import rene.zirkel.objects.SimulationObject;
import rene.zirkel.objects.TrackObject;
import rene.zirkel.objects.UserFunctionObject;
import rene.zirkel.objects.VectorObject;

/*
 This file contains classes for computing arithmetic expression. Those
 classes might be useful elsewhere. However, variables evaluate to sizes of
 construction objects or values of expressions here.
 */
/**
 * This class holds information abut the current position in the scanned text.
 */
class ExpressionText {

	char A[];
	int N;
	Construction C;
	ConstructionObject O;
	DepList DL;
	String Var[];
	boolean NoCircles;


	public ExpressionText(final String S, final Construction c,
			final ConstructionObject o, final DepList dl,
			final boolean nocircles) {
		A = S.toCharArray();
		N = 0;
		C = c;
		O = o;
		DL = dl;
		NoCircles = nocircles;
	}

	public void setVar(final String t[]) {
		Var = t;
	}

	public boolean isVar(final String var) {
		if (Var == null) {
			return false;
		}
		for (final String element : Var) {
			if (element.equals(var)) {
				return true;
			}
		}
		return false;
	}

	public char next(final boolean quotes) {
		if (!quotes) {
			skipblanks();
		}
		if (N < A.length) {
			return A[N];
		} else {
			return (char) 0;
		}
	}

	public char next() {
		return next(false);
	}

	public char nextnext() {
		skipblanks();
		if (N + 1 < A.length) {
			return A[N + 1];
		} else {
			return (char) 0;
		}
	}

	public void advance(final boolean quotes) {
		if (!quotes) {
			skipblanks();
		}
		N++;
	}

	public void advance() {
		advance(false);
	}

	public Construction getConstruction() {
		return C;
	}

	public ConstructionObject getObject() {
		return O;
	}

	public DepList getDepList() {
		return DL;
	}

	public void skipblanks() {
		while (N < A.length && A[N] == ' ') {
			N++;
		}
	}

	public boolean nocircles() {
		return NoCircles;
	}
}

/**
 * basic = (sum) basic = elementary
 */
class BasicExpression {

	BasicExpression E;
	static final int MINUS = -1, PLUS = 1;

	public static BasicExpression scan(final ExpressionText t)
	throws ConstructionException {
		if (t.next() == '-') {
			t.advance();
			return scan(t, MINUS);
		}
		if (t.next() == '+') {
			t.advance();
			return scan(t, PLUS);
		}
		if (t.next() == '(') {
			return new BracketExpression(scanBracket(t));
		}
		return ElementaryExpression.scan(t);
	}

	public static BasicExpression scan(final ExpressionText t, final int sign)
	throws ConstructionException {
		if (sign == MINUS) {
			if (t.next() == '(') {
				return new MinusExpression(scanBracket(t));
			}
			return new MinusExpression(ElementaryExpression.scan(t));
		}
		if (t.next() == '(') {
			return scanBracket(t);
		}
		return ElementaryExpression.scan(t);
	}

	public static BasicExpression scanBracket(final ExpressionText t)
	throws ConstructionException {
		t.advance();
		final BasicExpression E = TopExpression.scan(t);
		if (t.next() == ')') {
			t.advance();
		} else {
			throw new ConstructionException(Global.name("exception.bracket"));
		}
		return E;
	}

	public double getValue() throws ConstructionException {
		throw new ConstructionException("");
	}

	public void translate() {
	}

	public boolean isNumber() {
		return false;
	}

	public void reset() {
	}

	public boolean isLogical() {
		return false;
	}
}

/**
 * double = 34.45879 double = +34.45879 double = -34.45879
 */
class DoubleExpression extends BasicExpression {

	double X;

	public DoubleExpression(final double x) {
		X = x;
	}

	public static BasicExpression scan(final ExpressionText t)
	throws ConstructionException {
		boolean Punkt = false, Digit = false, Expo = false;
		String num = "";
		char c = t.next();
		if ((c == '+') || (c == '-')) {
			num += c;
			t.advance();
		}

		while (true) {
			c = t.next();
			if (c >= '0' && c <= '9') {
				num += c;
				t.advance();
				Digit = true;
			} else if (c == '.') {
				if (Punkt) {
					throw new ConstructionException(Global.name("exception.dot"));
				}
				Punkt = true;
				num += c;
				t.advance();
			} else if ((Digit) && (c == 'E')) {
				if (Expo) {
					throw new ConstructionException(
					"Exponential notation error");
				}
				Expo = true;
				num += c;
				t.advance();
				c = t.next();
				if ((c == '+') || (c == '-')) {
					num += c;
					t.advance();
				}
			} else {
				break;
			}
		}

		if (!Digit) {
			throw new ConstructionException(Global.name("exception.nodigit"));
		}

		return new DoubleExpression(Double.valueOf(num).doubleValue());
	}

	@Override
	public double getValue() throws ConstructionException {
		return X;
	}

	@Override
	public String toString() {
		String s = "" + X;
		if (s.endsWith(".0")) {
			s = s.substring(0, s.length() - 2);
		}
		return s;
	}

	@Override
	public void translate() {
	}

	@Override
	public boolean isNumber() {
		return true;
	}
}

/**
 * object = @name object = @"name"
 */
class FindObjectExpression extends ObjectExpression implements Translator {

	String Name;
	String Var = null;
	Construction C;
	ConstructionObject O = null; // found object

	public FindObjectExpression(final String name, final Construction c) {
		super(null);
		Name = name;
		C = c;
	}

	@Override
	public ConstructionObject getObject() {
		if (C == null) {
			return null;
		}
		if (O != null && O.getConstruction() == C && O.isInConstruction()
				&& O.getName().equals(Name)) {
			return O;
		}
		return O = C.find(Name);
	}

	@Override
	public double getValue() throws ConstructionException {
		if (C == null) {
			throw new InvalidException("");
		}
		if (O != null && O.getConstruction() == C && O.isInConstruction()
				&& O.getName().equals(Name)) {
			return O.getValue();
		}
		O = C.find(Name);
		if (O == null) {
			throw new InvalidException("");
		}
		return O.getValue();
	}

	public static BasicExpression scan(final ExpressionText t,
			final boolean quotes) throws ConstructionException {
		if (!quotes && t.next() == '\"') {
			t.advance();
			final BasicExpression E = scan(t, true);
			if (t.next() != '\"') {
				throw new ConstructionException(Global.name("exception.quotes"));
			}
			t.advance();
			return E;
		}
		final StringBuffer b = new StringBuffer();
		while (true) {
			final char c = t.next(quotes);
			if (!(Character.isLetterOrDigit(c) || c == '\\' || c == '\''
				|| (quotes && c != '\"' && c != 0) || (quotes && c == ' '))) {
				break;
			}
			b.append(c);
			t.advance(quotes);
		}
		if (!quotes && t.next() == '(') {
			return FunctionExpression.scan(t, b.toString());
		}
		final String s = b.toString();
		return new FindObjectExpression(s, t.getConstruction());
	}

	public static BasicExpression scan(final ExpressionText t)
	throws ConstructionException {
		return scan(t, false);
	}

	@Override
	public void translate() {
		C = C.getTranslation();
		C.addTranslator(this);
	}

	@Override
	public String toString() {
		if (Var != null) {
			return Var;
		}
		return "@" + ObjectExpression.quote(Name);
	}

	/**
	 * Used to translate expressions with @P in macros
	 */
	@Override
	public void laterTranslate(final Construction from) {
		if (C == null) {
			return;
		}
		final ConstructionObject o = from.find(Name);
		if (o == null || o.getTranslation() == null) {
			from.addError(ConstructionObject.text1(Global.name("warning.macrodefinition"), Name));
			return;
		}
		Name = o.getTranslation().getName();
		O = null;
	}
}

/**
 * object = name object = "name"
 */
class ObjectExpression extends BasicExpression {

	ConstructionObject O;
	String Var = null;

	public ObjectExpression(final ConstructionObject o) {
		O = o;
	}

	@Override
	public double getValue() throws ConstructionException {
		if (!O.valid()) {
			throw new InvalidException("");
		}
		if (Var != null && (O instanceof FunctionObject)) {
			return ((FunctionObject) O).getValue(Var);
		}
		if (Var != null && (O instanceof UserFunctionObject)) {
			return ((UserFunctionObject) O).getValue(Var);
		}
		if (Var != null && (O instanceof EquationXYObject)) {
			return ((EquationXYObject) O).getValue(Var);
		}
		return O.getValue();
	}

	public ConstructionObject getObject() {
		return O;
	}

	public static BasicExpression scan(final ExpressionText t,
			final boolean quotes) throws ConstructionException {
		if (!quotes && t.next() == '\"') {
			t.advance();
			final BasicExpression E = scan(t, true);
			if (t.next() != '\"') {
				throw new ConstructionException(Global.name("exception.quotes"));
			}
			t.advance();
			return E;
		}
		final StringBuffer b = new StringBuffer();
		while (true) {
			final char c = t.next(quotes);
			if (!(Character.isLetterOrDigit(c) || c == '\\' || c == '\''
				|| (quotes && c != '\"' && c != 0) || (quotes && c == ' '))) {
				break;
			}
			b.append(c);
			t.advance(quotes);
		}
		if (!quotes && t.next() == '(') {
			return FunctionExpression.scan(t, b.toString());
		}
		final String s = b.toString();
		if (s.equals("pi")) {
			return new ConstantExpression("pi", Math.PI);
		}
		if (s.equals("valid")) {
			return new ValidExpression(true);
		}
		if (s.equals("invalid")) {
			return new ValidExpression(false);
		}
		if (s.equals("this")) {
			t.getDepList().add(t.getObject());
			return new ObjectExpression(t.getObject());
		}
		if (s.equals("windoww")) {
			return new ConstructionExpression(t.getConstruction(),
					ConstructionExpression.WIDTH);
		}
		if (s.equals("windowh")) {
			return new ConstructionExpression(t.getConstruction(),
					ConstructionExpression.HEIGHT);
		}
		if (s.equals("windowcx")) {
			return new ConstructionExpression(t.getConstruction(),
					ConstructionExpression.CENTERX);
		}
		if (s.equals("windowcy")) {
			return new ConstructionExpression(t.getConstruction(),
					ConstructionExpression.CENTERY);
		}
		if (s.equals("pixel")) {
			return new ConstructionExpression(t.getConstruction(),
					ConstructionExpression.PIXEL);
		}
		if (t.isVar(s)) {
			final ObjectExpression oe = new ObjectExpression(t.getObject());
			oe.Var = s;
			return oe;
		}
		ConstructionObject o = t.getConstruction().findInclusive(s,
				t.getObject());
		if (o == null) {
			o = t.getConstruction().find(s);
			if (o == null) {
				if (t.getConstruction().loading()) {
					return new FindObjectExpression(s, t.getConstruction());
				} else {
					throw new ConstructionException(Global.name("exception.notfound")
							+ " (" + s + ")");
				}
			}
			// System.out.println("---");
			// System.out.println(o.getName()+" "+t.getObject().getName());
			// System.out.println(t.getConstruction().dependsOn(o,t.getObject()));
			if (t.getConstruction().dependsOn(o, t.getObject())) {
				if (t.nocircles()) {
					throw new ConstructionException(ConstructionObject.text1(
							Global.name("exception.depends"), s));
				}
				return new FindObjectExpression(s, t.getConstruction());
			}
			t.getConstruction().needsOrdering();
		}
		t.getDepList().add(o);
		return new ObjectExpression(o);
	}

	public static BasicExpression scan(final ExpressionText t)
	throws ConstructionException {
		if (t.next() == '@') {
			t.advance();
			return FindObjectExpression.scan(t);
		}
		return scan(t, false);
	}

	@Override
	public void translate() {
		O = O.getTranslation();
	}

	@Override
	public String toString() {
		if (Var != null) {
			return Var;
		}
		return quote(O.getName());
	}

	static public String quote(final String name) {
		boolean quotes = false;
		for (int i = 0; i < name.length(); i++) {
			if (!Character.isLetterOrDigit(name.charAt(i))) {
				quotes = true;
				break;
			}
		}
		if (quotes) {
			return "\"" + name + "\"";
		} else {
			return name;
		}
	}
}

/**
 * @author Rene
 *
 *         Evaluates a user funktion (a FunctionObject). The Y-expression of
 *         this function is used.
 *
 */
class UserFunctionExpression extends BasicExpression {

	ConstructionObject F;
	BasicExpression E[];
	double X[];

	public UserFunctionExpression(final ConstructionObject f,
			final BasicExpression e[]) {
		F = f;
		E = e;
		X = new double[E.length];
	}

	@Override
	public double getValue() throws ConstructionException {
		for (int i = 0; i < E.length; i++) {
			X[i] = E[i].getValue();
		}
		if (F instanceof FunctionObject) {
			return ((FunctionObject) F).evaluateF(X);
		}
		return ((UserFunctionObject) F).evaluateF(X);
	}

	@Override
	public void translate() {
		for (final BasicExpression element : E) {
			element.translate();
		}
		if (F instanceof FunctionObject) {
			F = (FunctionObject) F.getTranslation();
		} else {
			F = (UserFunctionObject) F.getTranslation();
		}
	}

	@Override
	public void reset() {
		for (final BasicExpression element : E) {
			element.reset();
		}
	}

	@Override
	public String toString() {
		String ex = E[0].toString();
		for (int i = 1; i < E.length; i++) {
			ex = ex + "," + E[i].toString();
		}
		return F.getName() + "(" + ex + ")";
	}
}

class SimulationExpression extends BasicExpression {

	ConstructionObject SO;
	ConstructionObject Container, F;
	BasicExpression E;
	Construction C;

	public SimulationExpression(final ConstructionObject so,
			final ConstructionObject c, final BasicExpression e,
			final ConstructionObject f) {
		SO = so;
		Container = c;
		E = e;
		F = f;
		C = c.getConstruction();
	}

	@Override
	public double getValue() throws ConstructionException {
		if (C.BlockSimulation) {
			throw new ConstructionException(Global.name("exception.simmulation.block"));
		}
		try {
			final double aa = E.getValue();
			C.BlockSimulation = true;
			((SimulationObject) SO).setSimulationValue(aa);
			F.getConstruction().validate(F, SO);
			final double res = F.getValue();
			((SimulationObject) SO).resetSimulationValue();
			F.getConstruction().validate(F, SO);
			C.BlockSimulation = false;
			return res;
		} catch (final Exception e) {
			C.BlockSimulation = false;
			throw new ConstructionException("");
		}
	}

	@Override
	public void translate() {
		E.translate();
		SO = SO.getTranslation();
		Container = Container.getTranslation();
		F = F.getTranslation();
	}

	@Override
	public void reset() {
		E.reset();
	}

	@Override
	public String toString() {
		return "simulate(" + SO.getName() + "," + E.toString() + ","
		+ F.getName() + ")";
	}
}

/**
 * function = sin(parameter) etc. function = x(point) function = y(point)
 */
class FunctionExpression extends BasicExpression {

	int F;
	BasicExpression E[];
	int NParams;

	public FunctionExpression(final int f, final BasicExpression e) {
		F = f;
		E = new BasicExpression[1];
		E[0] = e;
		NParams = 1;
	}

	public FunctionExpression(final int f, final BasicExpression e,
			final BasicExpression ee) {
		F = f;
		E = new BasicExpression[2];
		E[0] = e;
		E[1] = ee;
		NParams = 2;
	}

	public FunctionExpression(final int f, final BasicExpression e,
			final BasicExpression ee, final BasicExpression eee) {
		F = f;
		E = new BasicExpression[3];
		E[0] = e;
		E[1] = ee;
		E[2] = eee;
		NParams = 3;
	}

    /**
     * @author Vincent Robert, 2nde SI 3, LPO Roland-Garros
     * pgcd de deux nombres
     * @param x entier premier nombre
     * @param y entier second nombre
     * @return le pgcd
     */
    public int pgcd(int x, int y){
        int a=Math.abs(x);
        int b=Math.abs(y);
        int c;
        while(b>0){
            c=a%b;
            a=b;
            b=c;
        }
        return a;
    }

	final static String Functions[] = { "sin", "cos", "tan", "arcsin",
		"arccos", "arctan", "sqrt", "exp", "log", "round", "x", "y",
		"floor", "ceil", "d", "a", "angle180", "angle360", "abs", "scale",
		"sign", "d", "sum", "if", "deg", "rad", "integrate", "zero",
		"diff", "min", "max", "length", "rsin", "rcos", "rtan", "rarcsin",
		"rarccos", "rarctan", "sinhyp", "coshyp", "z", "simulate",
		"inside", "random", "old", "ratan2", "atan2", "tanhyp", "Argsinh",
                "Argcosh", "Argtanh", "div","mod", "gcd", "lcm", "x3D", "y3D", "z3D", "d3D"};
	final static int NX = 10, NY = 11, ND = 14, NA = 15, NS = 19, NSUM = 22,
	NIF = 23, NINT = 26, NZERO = 27, NDIFF = 28, NMIN = 29, NMAX = 30,
	NLENGTH = 31, NZ = 40, NSIM = 41, NINSIDE = 42, NRANDOM = 43,
	NOLD = 44, NRATAN2=45, NATAN2=46, NDIV=51, NMOD=52, NGCD=53, NLCM=54, NX3D=55, NY3D=56, NZ3D= 57, ND3D=58 ;

	public static BasicExpression scan(final ExpressionText t, final String name)
	throws ConstructionException {
		int f = -1;
		for (int i = 0; i < Functions.length; i++) {
			if (name.equals(Functions[i])) {
				f = i;
				break;
			}
		}
		if (f < 0) {
			boolean forward = false;
			ConstructionObject o = t.getConstruction()
			.find(name, t.getObject());
			if (o == null) {
				o = t.getConstruction().find(name);
				forward = true;
			}
			if (o != null
					&& (o instanceof FunctionObject || o instanceof UserFunctionObject)
					&& !(o == t.getObject())
					&& !t.getConstruction().dependsOn(o, t.getObject())) {
				if (t.next() != '(') {
					throw new ConstructionException(Global.name("exception.parameter"));
				}
				t.advance();
				final MyVector ex = new MyVector();
				while (true) {
					final BasicExpression e = TopExpression.scan(t);
					ex.addElement(e);
					if (t.next() == ')') {
						break;
					}
					if (t.next() != ',') {
						throw new ConstructionException(Global.name("exception.parameter"));
					}
					t.advance();
				}
				t.advance();
				t.getDepList().add(o);
				if (forward) {
					t.getConstruction().needsOrdering();
				}
				final BasicExpression exp[] = new BasicExpression[ex.size()];
				ex.copyInto(exp);
				return new UserFunctionExpression(o, exp);
			}
			throw new ConstructionException(Global.name("exception.function")
					+ " (" + name + ")");
		}
		if (t.next() != '(') {
			throw new ConstructionException(Global.name("exception.parameter"));
		}
		t.advance();
		BasicExpression e = TopExpression.scan(t);
		if (f == NX || f == NY || f == NZ) {
			if (e instanceof FindObjectExpression) {
				e = new FunctionExpression(f, e);
			} else if (e instanceof ObjectExpression
					&& ((ObjectExpression) e).getObject() instanceof PointObject) {
				e = new FunctionExpression(f, e);
			} else if (e instanceof ObjectExpression
					&& ((ObjectExpression) e).getObject() instanceof PrimitiveLineObject) {
				e = new FunctionExpression(f, e);
			} else {

				throw new ConstructionException(Global.name("exception.parameter")
						+ " (" + Functions[f] + ")");
			}	
		} 
		else if (f == NX3D || f == NY3D || f == NZ3D) {
			if (e instanceof FindObjectExpression) {
				e = new FunctionExpression(f, e);
			} else if (e instanceof ObjectExpression
					&& ((ObjectExpression) e).getObject() instanceof PointObject) {
				e = new FunctionExpression(f, e);
			} else if (e instanceof ObjectExpression
					&& ((ObjectExpression) e).getObject() instanceof PrimitiveLineObject) {
				e = new FunctionExpression(f, e);
			} else {

				throw new ConstructionException(Global.name("exception.parameter")
						+ " (" + Functions[f] + ")");
			}
		} 
		else if (f == ND) {
			if (t.next() != ',') {
				e = new DExpression(e);
			} else {
				t.advance();
				final BasicExpression ee = TopExpression.scan(t);
				if (((e instanceof ObjectExpression && ((ObjectExpression) e)
						.getObject() instanceof PointObject) || e instanceof FindObjectExpression)
						&& ((ee instanceof ObjectExpression && ((ObjectExpression) ee)
								.getObject() instanceof PointObject) || ee instanceof FindObjectExpression)) {
					e = new FunctionExpression(f, e, ee);
				} else {
					throw new ConstructionException(Global.name("exception.parameter")
							+ " (" + Functions[f] + ")");
				}
			}
		} 
		else if (f == ND3D) {
			if (t.next() != ',') {
				e = new D3DExpression(e);
			} else {
				t.advance();
				final BasicExpression ee = TopExpression.scan(t);
				if (((e instanceof ObjectExpression && ((ObjectExpression) e)
						.getObject() instanceof PointObject) || e instanceof FindObjectExpression)
						&& ((ee instanceof ObjectExpression && ((ObjectExpression) ee)
								.getObject() instanceof PointObject) || ee instanceof FindObjectExpression)) {
					e = new FunctionExpression(f, e, ee);
				} else {
					throw new ConstructionException(Global.name("exception.parameter")
							+ " (" + Functions[f] + ")");
				}
			}
		}
		else if (f == NOLD) {
			if (t.next() != ',') {
				e = new OldExpression(e);
			} else {
				t.advance();
				final BasicExpression ee = TopExpression.scan(t);
				e = new OldExpression(e, ee);
			}
		} else if (f == NA) {
			if (t.next() != ',') {
				throw new ConstructionException(Global.name("exception.parameter"));
			}
			t.advance();
			final BasicExpression ee = TopExpression.scan(t);
			if (t.next() != ',') {
				throw new ConstructionException(Global.name("exception.parameter"));
			}
			t.advance();
			final BasicExpression eee = TopExpression.scan(t);
			if (((e instanceof ObjectExpression && ((ObjectExpression) e)
					.getObject() instanceof PointObject) || e instanceof FindObjectExpression)
					&& ((ee instanceof ObjectExpression && ((ObjectExpression) ee)
							.getObject() instanceof PointObject) || ee instanceof FindObjectExpression)
							&& ((eee instanceof ObjectExpression && ((ObjectExpression) eee)
									.getObject() instanceof PointObject) || eee instanceof FindObjectExpression)) {
				e = new FunctionExpression(f, e, ee, eee);
			} else {
				throw new ConstructionException(Global.name("exception.parameter")
						+ " (" + Functions[f] + ")");
			}

		} else if (f == NS) {
			if (t.next() != ',') {
				throw new ConstructionException(Global.name("exception.parameter"));
			}
			t.advance();
			final BasicExpression ee = TopExpression.scan(t);
			if (t.next() != ',') {
				throw new ConstructionException(Global.name("exception.parameter"));
			}
			t.advance();
			final BasicExpression eee = TopExpression.scan(t);
			e = new FunctionExpression(f, e, ee, eee);
		} else if (f == NSUM) {
			if (t.next() != ',') {
				e = new CumSumExpression(e);
			} else {
				t.advance();
				final BasicExpression ee = TopExpression.scan(t);
				e = new CumSumExpression(e, ee);
			}
		} else if (f == NRANDOM) {
			if (t.next() != ',') {
				e = new RandomExpression(e);
			} else {
				t.advance();
				final BasicExpression ee = TopExpression.scan(t);
				e = new RandomExpression(e, ee);
			}
		} else if (f == NIF) {
			if (t.next() != ',') {
				throw new ConstructionException(Global.name("exception.parameter"));
			}
			t.advance();
			final BasicExpression ee = TopExpression.scan(t);
			if (t.next() != ',') {
				throw new ConstructionException(Global.name("exception.parameter"));
			}
			t.advance();
			final BasicExpression eee = TopExpression.scan(t);
			e = new IfExpression(e, ee, eee);
		} else if (f == NINT || f == NZERO || f == NMIN || f == NMAX
				|| f == NLENGTH || f==NRATAN2 || f==NATAN2
                                || f == NDIV || f== NMOD || f == NGCD || f == NLCM) {
			if (!(e instanceof ObjectExpression)||t.isVar(e.toString())) {
				if (f == NMAX || f == NMIN || f==NRATAN2 || f==NATAN2
                                        || f == NDIV || f == NMOD || f == NGCD || f == NLCM) {
					if (t.next() != ',') {
						throw new ConstructionException(Global.name("exception.parameter"));
					}
					t.advance();
					final BasicExpression ee = TopExpression.scan(t);
					e = new FunctionExpression(f, e, ee);
				} else {
					throw new ConstructionException(Global.name("exception.parameter")
							+ " (" + Functions[f] + ")");
				}
			} else {
				final boolean function = ((ObjectExpression) e).getObject() instanceof Evaluator
				|| ((ObjectExpression) e).getObject() instanceof TrackObject;
				if ((f == NINT || f == NLENGTH) && t.next() == ')') {
					if (!function) {
						throw new ConstructionException(Global.name("exception.parameter")
								+ " (" + Functions[f] + ")");
					}
					e = new FunctionExpression(f, e);
				} else if (f == NLENGTH) {
					throw new ConstructionException(Global.name("exception.parameter"));
				} else {
					if (t.next() != ',') {
						throw new ConstructionException(Global.name("exception.parameter"));
					}
					t.advance();
					final BasicExpression ee = TopExpression.scan(t);
					if (function) {
						if (t.next() != ',') {
							throw new ConstructionException(Global.name("exception.parameter"));
						}
						t.advance();
						final BasicExpression eee = TopExpression.scan(t);
						e = new FunctionExpression(f, e, ee, eee);
					} else if (f == NMIN || f == NMAX || f == NATAN2
                                                || f == NRATAN2 || f == NDIV || f == NMOD
                                                || f == NGCD || f == NLCM) {
						e = new FunctionExpression(f, e, ee);
					} else {
						throw new ConstructionException(Global.name("exception.parameter")
								+ " (" + Functions[f] + ")");
					}
				}
			}
		} else if (f == NDIFF) {
			if (!(e instanceof ObjectExpression)
					|| !(((ObjectExpression) e).getObject() instanceof Evaluator)) {
				throw new ConstructionException(Global.name("exception.parameter")
						+ " (" + Functions[f] + ")");
			}
			if (t.next() != ',') {
				throw new ConstructionException(Global.name("exception.parameter"));
			}
			t.advance();
			final BasicExpression ee = TopExpression.scan(t);
			e = new FunctionExpression(f, e, ee);
		} else if (f == NSIM) {
			if (t.next() != ',') {
				throw new ConstructionException(Global.name("exception.parameter"));
			}
			t.advance();
			final BasicExpression ee = TopExpression.scan(t);
			if (t.next() != ',') {
				throw new ConstructionException(Global.name("exception.parameter"));
			}
			t.advance();
			final BasicExpression eee = TopExpression.scan(t);
			if ((e instanceof ObjectExpression && ((ObjectExpression) e)
					.getObject() instanceof SimulationObject)
					|| !(eee instanceof ObjectExpression)) {
				final ConstructionObject SO = ((ObjectExpression) e)
				.getObject();
				if (t.getConstruction().dependsOn(SO, t.getObject())) {
					throw new ConstructionException(Global.name("exception.parameter")
							+ " (" + Functions[f] + ")");
				}
				e = new SimulationExpression(
						((ObjectExpression) e).getObject(), t.getObject(), ee,
						((ObjectExpression) eee).getObject());
			} else {
				throw new ConstructionException(Global.name("exception.parameter")
						+ " (" + Functions[f] + ")");
			}

		} else if (f == NINSIDE) {
			if (t.next() != ',') {
				throw new ConstructionException(Global.name("exception.parameter"));
			}
			t.advance();
			final BasicExpression ee = TopExpression.scan(t);
			if (((e instanceof ObjectExpression && ((ObjectExpression) e)
					.getObject() instanceof PointObject) || e instanceof FindObjectExpression)
					&& ((ee instanceof ObjectExpression && ((ObjectExpression) ee)
							.getObject() instanceof InsideObject) || ee instanceof FindObjectExpression)) {
				e = new FunctionExpression(f, e, ee);
			} else {
				throw new ConstructionException(Global.name("exception.parameter")
						+ " (" + Functions[f] + ")");
			}
		} else {
			e = new FunctionExpression(f, e);
		}
		if (t.next() != ')') {
			throw new ConstructionException(Global.name("exception.parameter"));
		}
		t.advance();
		return e;
	}

	@Override
	public double getValue() throws ConstructionException {
		PointObject P, PP, PPP;
		double cc;
		switch (F) {
		case NX:
			cc = getPointLineX(0);
			return cc;
		case NY:
			cc = getPointLineY(0);
			return cc;
		case NX3D:
			cc = getPointLineX3D(0);
			return cc;
		case NY3D:
			cc = getPointLineY3D(0);
			return cc;
		case NZ3D:
			cc = getPointLineZ3D(0);
			return cc;
		case NZ:
			P = getPoint(0);
			if (!P.valid()) {
				throw new InvalidException(Global.name("exception.invalid"));
			}
			return P.getZ();
		case ND:
			P = getPoint(0);
			PP = getPoint(1);
			if (!P.valid() || !PP.valid()) {
				throw new InvalidException(Global.name("exception.invalid"));
			}
			double dx = P.getX() - PP.getX();
			double dy = P.getY() - PP.getY();
			return Math.sqrt(dx * dx + dy * dy);
		case ND3D:
			P = getPoint(0);
			PP = getPoint(1);
			if (!P.valid() || !PP.valid()) {
				throw new InvalidException(Global.name("exception.invalid"));
			}
			double dx3D = P.getX3D() - PP.getX3D();
			double dy3D = P.getY3D() - PP.getY3D();
			double dz3D = P.getZ3D() - PP.getZ3D();
			return Math.sqrt(dx3D * dx3D + dy3D * dy3D + dz3D * dz3D);
		case NA:
			P = getPoint(0);
			PP = getPoint(1);
			PPP = getPoint(2);
			if (!P.valid() || !PP.valid() || !PPP.valid()) {
				throw new InvalidException(Global.name("exception.invalid"));
			}
			dx = P.getX() - PP.getX();
			dy = P.getY() - PP.getY();
			final double dx1 = PPP.getX() - PP.getX();
			final double dy1 = PPP.getY() - PP.getY();
			double a = Math.atan2(dx, dy) - Math.atan2(dx1, dy1);
			a = a / Math.PI * 180;
			if (a < 0) {
				a += 360;
			}
			if (a > 360) {
				a -= 360;
			}
			return a;
		case NS:
			final double x = E[0].getValue(),
			xa = E[1].getValue(),
			xb = E[2].getValue();
			if (x < xa || x >= xb || xb <= xa) {
				throw new InvalidException(Global.name("exception.invalid"));
			}
			return (x - xa) / (xb - xa);
		case NINT:
			if (((ObjectExpression) E[0]).getObject() instanceof Evaluator) {
				final Evaluator F = (Evaluator) (((ObjectExpression) E[0])
						.getObject());

				if (E.length > 1) {
					final double aa = E[1].getValue(), bb = E[2].getValue();
					return Romberg.compute(F, aa, bb, 10, 1e-10, 10);
				} else if (F instanceof FunctionObject) {
					return ((FunctionObject) F).getIntegral();
				}
			} else {
				final TrackObject TO = (TrackObject) (((ObjectExpression) E[0])
						.getObject());
				if (E.length > 1) {
					final double aa = E[1].getValue(), bb = E[2].getValue();
					return TO.getSum(aa, bb);
				} else {
					return TO.getSum();
				}
			}
		case NLENGTH:
			if (((ObjectExpression) E[0]).getObject() instanceof FunctionObject) {
				final FunctionObject F = (FunctionObject) (((ObjectExpression) E[0])
						.getObject());
				return F.getLength();
			} else {
				final TrackObject TO = (TrackObject) (((ObjectExpression) E[0])
						.getObject());
				return TO.getLength();
			}
		case NZERO:
			Evaluator F = (Evaluator) (((ObjectExpression) E[0]).getObject());
			double aa = E[1].getValue(),
			bb = E[2].getValue();
			return Secant.compute(F, aa, bb, 1e-10);
		case NDIFF:
			F = (Evaluator) (((ObjectExpression) E[0]).getObject());
			aa = E[1].getValue();
			return (F.evaluateF(aa + 1e-7) - F.evaluateF(aa - 1e-7)) / 2e-7;
		case NMIN:
			if (NParams == 2) {
				return Math.min(E[0].getValue(), E[1].getValue());
			} else {
				F = (Evaluator) (((ObjectExpression) E[0]).getObject());
				aa = E[1].getValue();
				bb = E[2].getValue();
				return ConvexMin.computeMin(F, aa, bb, 1e-10);
			}
		case NMAX:
			if (NParams == 2) {
				return Math.max(E[0].getValue(), E[1].getValue());
			} else {
				F = (Evaluator) (((ObjectExpression) E[0]).getObject());
				aa = E[1].getValue();
				bb = E[2].getValue();
				return ConvexMin.computeMax(F, aa, bb, 1e-10);
			}
		case NINSIDE:
			P = getPoint(0);
			final InsideObject IO = (InsideObject) (((ObjectExpression) E[1])
					.getObject());
			return IO.containsInside(P);
		}
		double x = E[0].getValue();
		switch (F) {
		case 0:
			return Math.sin(x / 180 * Math.PI);
		case 1:
			return Math.cos(x / 180 * Math.PI);
		case 2:
			return Math.tan(x / 180 * Math.PI);
		case 3:
			return Math.asin(x) / Math.PI * 180;
		case 4:
			return Math.acos(x) / Math.PI * 180;
		case 5:
			return Math.atan(x) / Math.PI * 180;
		case 6:
			return Math.sqrt(x);
		case 7:
			return Math.exp(x);
		case 8:
			return Math.log(x);
		case 9:
			return Math.round(x);
		case 12:
			return Math.floor(x);
		case 13:
			return Math.ceil(x);
		case 16:
			x = x / 360;
			x -= Math.floor(x);
			x = x * 360;
			if (x < 180) {
				return x;
			} else {
				return (x - 360);
			}
		case 17:
			x /= 360;
			x -= Math.floor(x);
			return x * 360;
		case 18:
			return Math.abs(x);
		case 20:
			if (x > 0) {
				return 1;
			} else if (x == 0) {
				return 0;
			} else {
				return -1;
			}
		case 24:
			return (x / Math.PI * 180);
		case 25:
			return (x / 180 * Math.PI);
		case 32:
			return Math.sin(x);
		case 33:
			return Math.cos(x);
		case 34:
			return Math.tan(x);
		case 35:
			return Math.asin(x);
		case 36:
			return Math.acos(x);
		case 37:
			return Math.atan(x);
		case 38:
			return (Math.exp(x) - Math.exp(-x)) / 2;
		case 39:
			return (Math.exp(x) + Math.exp(-x)) / 2;
                case 47:
                        return (Math.exp(x) - Math.exp(-x))/(Math.exp(x) + Math.exp(-x));
                case 48:
                        return Math.log(x+Math.sqrt(x*x+1));
                case 49:
                        return Math.log(x+Math.sqrt(x*x-1));
                case 50:
                        return Math.log((1+x)/(1-x))/2.0;
                case 45://these two functions by S. Reyrolle
                        if(NParams==2){
                           return Math.atan2(x,E[1].getValue());
                        } else{
                           throw new ConstructionException(Global.name("exception.parameter"));
                        }
                case 46:
                        if(NParams==2){
                           return Math.atan2(x,E[1].getValue())/ Math.PI * 180;
                        }else{
                           throw new ConstructionException(Global.name("exception.parameter"));
                        }
                case NDIV:
                        if(NParams==2){
                            return Math.floor(x/E[1].getValue());
                        }else{
                            throw new ConstructionException(Global.name("exception.parameter"));
                        }
                case NMOD:
                        if(NParams==2){
                            return x%(E[1].getValue());
                        } else {
                            throw new ConstructionException(Global.name("exception.parameter"));
                        }
                case NGCD:
                        if(NParams==2){
                            return pgcd((int) x,(int) E[1].getValue());
                        } else {
                            throw new ConstructionException(Global.name("exception.parameter"));
                        }
                case NLCM:
                        if(NParams==2){
                            return x*E[1].getValue()/pgcd((int) x,(int) E[1].getValue());

                        } else {
                            throw new ConstructionException(Global.name("exception.parameter"));
                        }
		}
		throw new ConstructionException("");
	}

	public double getPointLineX(final int n) throws ConstructionException {
		PointObject p;
		PrimitiveLineObject l;
                VectorObject s;
		try {
			p = (PointObject) ((ObjectExpression) E[n]).getObject();
			if (!p.valid()) {
				throw new ConstructionException("exception.invalid");
			}
			return p.getX();
		} catch (final Exception e) {
                    try{
                        s=(VectorObject) ((ObjectExpression) E[n]).getObject();
                        if (!s.valid()){
                            throw new ConstructionException("exception.invalid");
                        }
                        return s.getDeltaX();

                        } catch (final Exception eee) {
                    }
			try {
				l = (PrimitiveLineObject) ((ObjectExpression) E[n]).getObject();
				if (!l.valid()) {
					throw new ConstructionException("exception.invalid");
				}
				return l.getDX();
			} catch (final Exception ee) {
				throw new ConstructionException("exception.notfound");
			}
		}
	}

	public double getPointLineY(final int n) throws ConstructionException {
		PointObject p;
		PrimitiveLineObject l;
                VectorObject s;
		try {
			p = (PointObject) ((ObjectExpression) E[n]).getObject();
			if (!p.valid()) {
				throw new ConstructionException("exception.invalid");
			}
			return p.getY();
		} catch (final Exception e) {
                    try{
                        s=(VectorObject) ((ObjectExpression) E[n]).getObject();
                        if (!s.valid()){
                            throw new ConstructionException("exception.invalid");
                        }
                        return s.getDeltaY();

                        } catch (final Exception eee) {
                    }
			try {
				l = (PrimitiveLineObject) ((ObjectExpression) E[n]).getObject();
				if (!l.valid()) {
					throw new ConstructionException("exception.invalid");
				}
				return l.getDY();
			} catch (final Exception ee) {
				throw new ConstructionException("exception.notfound");
			}
		}
	}
	
	public double getPointLineX3D(final int n) throws ConstructionException {
		PointObject p;
		PrimitiveLineObject l;
                VectorObject s;
		try {
			p = (PointObject) ((ObjectExpression) E[n]).getObject();
			if (!p.valid()) {
				throw new ConstructionException("exception.invalid");
			}
			return p.getX3D();
		} catch (final Exception e) {
                    try{
                        s=(VectorObject) ((ObjectExpression) E[n]).getObject();
                        if (!s.valid()){
                            throw new ConstructionException("exception.invalid");
                        }
                        return s.getDeltaX3D();

                        } catch (final Exception eee) {
                    }
			try {
				l = (PrimitiveLineObject) ((ObjectExpression) E[n]).getObject();
				if (!l.valid()) {
					throw new ConstructionException("exception.invalid");
				}
				return l.getDX3D();
			} catch (final Exception ee) {
				throw new ConstructionException("exception.notfound");
			}
		}
	}
	
	public double getPointLineY3D(final int n) throws ConstructionException {
		PointObject p;
		PrimitiveLineObject l;
                VectorObject s;
		try {
			p = (PointObject) ((ObjectExpression) E[n]).getObject();
			if (!p.valid()) {
				throw new ConstructionException("exception.invalid");
			}
			return p.getY3D();
		} catch (final Exception e) {
                    try{
                        s=(VectorObject) ((ObjectExpression) E[n]).getObject();
                        if (!s.valid()){
                            throw new ConstructionException("exception.invalid");
                        }
                        return s.getDeltaY3D();

                        } catch (final Exception eee) {
                    }
			try {
				l = (PrimitiveLineObject) ((ObjectExpression) E[n]).getObject();
				if (!l.valid()) {
					throw new ConstructionException("exception.invalid");
				}
				return l.getDY3D();
			} catch (final Exception ee) {
				throw new ConstructionException("exception.notfound");
			}
		}
	}
	
	public double getPointLineZ3D(final int n) throws ConstructionException {
		PointObject p;
		PrimitiveLineObject l;
                VectorObject s;
		try {
			p = (PointObject) ((ObjectExpression) E[n]).getObject();
			if (!p.valid()) {
				throw new ConstructionException("exception.invalid");
			}
			return p.getZ3D();
		} catch (final Exception e) {
                    try{
                        s=(VectorObject) ((ObjectExpression) E[n]).getObject();
                        if (!s.valid()){
                            throw new ConstructionException("exception.invalid");
                        }
                        return s.getDeltaZ3D();

                        } catch (final Exception eee) {
                    }
			try {
				l = (PrimitiveLineObject) ((ObjectExpression) E[n]).getObject();
				if (!l.valid()) {
					throw new ConstructionException("exception.invalid");
				}
				return l.getDZ3D();
			} catch (final Exception ee) {
				throw new ConstructionException("exception.notfound");
			}
		}
	}
	
	public PointObject getPoint(final int n) throws ConstructionException {
		PointObject p;
		try {
			p = (PointObject) ((ObjectExpression) E[n]).getObject();
		} catch (final Exception e) {
			throw new ConstructionException("exception.notfound");
		}
		if (!p.valid()) {
			throw new ConstructionException("exception.invalid");
		}
		return p;
	}

	@Override
	public void translate() {
		for (int i = 0; i < NParams; i++) {
			E[i].translate();
		}
	}

	@Override
	public void reset() {
		for (int i = 0; i < NParams; i++) {
			E[i].reset();
		}
	}

	@Override
	public String toString() {
		String s = Functions[F] + "(";
		for (int i = 0; i < NParams; i++) {
			if (i > 0) {
				s = s + ",";
			}
			s = s + E[i];
		}
		return s + ")";
	}
}

class OldExpression extends FunctionExpression {

	double oldvalue1 = Double.NaN, oldvalue2 = Double.NaN;

	public OldExpression(final BasicExpression e) {
		super(NOLD, e);
	}

	public OldExpression(final BasicExpression e, final BasicExpression ee) {
		super(NOLD, e, ee);
	}

	@Override
	public double getValue() throws ConstructionException {
		final double x = E[0].getValue();
		final double p = (NParams > 1) ? E[1].getValue() : 0;
		if (Double.isNaN(oldvalue1)) {
			oldvalue1 = x;
		}
		if (Double.isNaN(oldvalue2)) {
			oldvalue2 = x;
		}
		if (p == 1) {
			oldvalue1 = oldvalue2;
			oldvalue2 = x;
		} else {
			if (x != oldvalue2) {
				oldvalue1 = oldvalue2;
				oldvalue2 = x;
			}
		}
		return oldvalue1;
	}

	@Override
	public void reset() {
		super.reset();
	}
}

class DExpression extends FunctionExpression {

	double Old = 0;
	double Old1 = 0;
	boolean start = false;

	public DExpression(final BasicExpression e) {
		super(21, e);
	}
	
	@Override
	public double getValue() throws ConstructionException {
		if (E[0] instanceof ObjectExpression) {
			final ConstructionObject o = ((ObjectExpression) E[0]).getObject();
			if (o instanceof PointObject) {
				final PointObject p = (PointObject) o;
				if (p.dontUpdate()) {
					return 0;
				}
				final double x = p.getX(), y = p.getY();
				if (start) {
					final double res = (x - Old) * (x - Old) + (y - Old1)
					* (y - Old1);
					Old = x;
					Old1 = y;
					return Math.sqrt(res);
				}
				Old = x;
				Old1 = y;
				start = true;
				return 0;
			} else if (o instanceof AngleObject) {
				final AngleObject a = (AngleObject) o;
				final double x = a.getValue();
				if (start) {
					double res = x - Old;
					if (res < -180) {
						res += 180;
					}
					if (res > 180) {
						res -= 180;
					}
					Old = x;
					return res;
				}
				Old = x;
				start = true;
				return 0;
			}
		}
		final double x = E[0].getValue();
		double res;
		if (start) {
			res = x - Old;
		} else {
			res = 0;
			start = true;
		}
		Old = x;
		return res;
	}

	@Override
	public void reset() {
		start = false;
		super.reset();
	}
}

class D3DExpression extends FunctionExpression {
	
	double Old = 0;
	double Old1 = 0;
	double Old2 = 0;
	boolean start = false;

	public D3DExpression(final BasicExpression e) {
		super(58, e);
	}
	
	@Override
	public double getValue() throws ConstructionException {
		if (E[0] instanceof ObjectExpression) {
			final ConstructionObject o = ((ObjectExpression) E[0]).getObject();
			if (o instanceof PointObject) {
				final PointObject p = (PointObject) o;
				if (p.dontUpdate()) {
					return 0;
				}
				final double x3D = p.getX3D(), y3D = p.getY3D(), z3D = p.getZ3D();
				if (start) {
					final double res = (x3D - Old) * (x3D - Old) + (y3D - Old1) * (y3D - Old1) + (z3D - Old2) * (z3D - Old2);
					Old = x3D;
					Old1 = y3D;
					Old2 = y3D;
					return Math.sqrt(res);
				}
				Old = x3D;
				Old1 = y3D;
				Old2 = z3D;
				start = true;
				return 0;
			} else if (o instanceof AngleObject) {
				final AngleObject a = (AngleObject) o;
				final double x = a.getValue();
				if (start) {
					double res = x - Old;
					if (res < -180) {
						res += 180;
					}
					if (res > 180) {
						res -= 180;
					}
					Old = x;
					return res;
				}
				Old = x;
				start = true;
				return 0;
			}
		}
		final double x = E[0].getValue();
		double res;
		if (start) {
			res = x - Old;
		} else {
			res = 0;
			start = true;
		}
		Old = x;
		return res;
	}

	@Override
	public void reset() {
		start = false;
		super.reset();
	}
}

class CumSumExpression extends FunctionExpression {

	double sum = 0;

	public CumSumExpression(final BasicExpression e, final BasicExpression ee) {
		super(22, e, ee);
	}

	public CumSumExpression(final BasicExpression e) {
		super(22, e);
	}

	@Override
	public double getValue() throws ConstructionException {
		if (NParams > 1) {
			try {
				final double x = E[1].getValue();
				if (x < 0) {
					reset();
					return sum;
				}
			} catch (final Exception e) {
				sum = 0;
				return sum;
			}
		}
		sum += E[0].getValue();
		return sum;
	}

	@Override
	public void reset() {
		sum = 0;
		super.reset();
	}
}

class RandomExpression extends FunctionExpression {

	double rand = Double.NaN;

	public RandomExpression(final BasicExpression e, final BasicExpression ee) {
		super(NRANDOM, e, ee);
	}

	public RandomExpression(final BasicExpression e) {
		super(NRANDOM, e);
	}

	@Override
	public double getValue() throws ConstructionException {
		if (NParams > 1) {
			try {
				final double r = E[0].getValue();
				final double p = E[1].getValue();

				if ((p == 1) || (Double.isNaN(rand))) {
					rand = r * Math.random();
				}
			} catch (final Exception e) {
				rand = 0;
				return rand;
			}
		} else {
			final double r = E[0].getValue();
			rand = r * Math.random();
		}
		return rand;
	}

	@Override
	public void reset() {
		rand = 0;
		super.reset();
	}
}

class IfExpression extends FunctionExpression {

	public IfExpression(final BasicExpression e, final BasicExpression ee,
			final BasicExpression eee) {
		super(23, e, ee, eee);
	}

	@Override
	public double getValue() throws ConstructionException {
		double v = 0;
		try {
			v = E[0].getValue();
		} catch (final NoValueException e) {
			if (e.isValid()) {
				return E[1].getValue();
			} else {
				return E[2].getValue();
			}
		} catch (final ConstructionException e) {
			return E[2].getValue();
		}
		if (E[0].isLogical()) {
			if (v == 0) {
				return E[2].getValue();
			} else {
				return E[1].getValue();
			}
		}
		return E[1].getValue();
	}
}

/**
 * constant = pi
 */
class ConstantExpression extends BasicExpression {

	double X;
	String S;

	public ConstantExpression(final String s, final double x) {
		X = x;
		S = s;
	}

	@Override
	public double getValue() throws ConstructionException {
		return X;
	}

	@Override
	public void translate() {
	}

	@Override
	public String toString() {
		return S;
	}
}

/**
 * constant depending on construction
 */
class ConstructionExpression extends BasicExpression {

	int Type;
	Construction C;
	final static int CENTERX = 0, CENTERY = 1, WIDTH = 2, HEIGHT = 3,
	PIXEL = 4;

	public ConstructionExpression(final Construction c, final int type) {
		Type = type;
		C = c;
	}

	@Override
	public double getValue() throws ConstructionException {
		if (C == null) {
			return 0;
		}
		switch (Type) {
		case WIDTH:
			return C.getW();
		case CENTERX:
			return C.getX();
		case CENTERY:
			return C.getY();
		case HEIGHT:
			return C.getH();
		case PIXEL:
			return C.getPixel();
		}
		return 0;
	}

	@Override
	public void translate() {
		C = C.getTranslation();
	}

	@Override
	public String toString() {
		switch (Type) {
		case WIDTH:
			return "windoww";
		case CENTERX:
			return "windowcx";
		case CENTERY:
			return "windowcy";
		case HEIGHT:
			return "windowh";
		case PIXEL:
			return "pixel";
		}
		return "";
	}
}

/**
 * constant = valid,invalid
 */
class ValidExpression extends BasicExpression {

	boolean V;

	public ValidExpression(final boolean valid) {
		V = valid;
	}

	@Override
	public double getValue() throws ConstructionException {
		if (V) {
			return 0;
		} else {
			throw new ConstructionException("");
		}
	}

	@Override
	public void translate() {
	}

	@Override
	public String toString() {
		if (V) {
			return "valid";
		} else {
			return "invalid";
		}
	}
}

/**
 * Bracket = (Basic)
 */
class BracketExpression extends ElementaryExpression {

	BasicExpression E;

	public BracketExpression(final BasicExpression e) {
		E = e;
	}

	@Override
	public double getValue() throws ConstructionException {
		return E.getValue();
	}

	@Override
	public void translate() {
		E.translate();
	}

	@Override
	public String toString() {
		return "(" + E.toString() + ")";
	}

	@Override
	public boolean isNumber() {
		return E.isNumber();
	}

	@Override
	public void reset() {
		E.reset();
	}
}

/**
 * element = double element = object (with value)
 */
abstract class ElementaryExpression extends BasicExpression {

	public static BasicExpression scan(final ExpressionText t)
	throws ConstructionException {
		try {
			final BasicExpression E = DoubleExpression.scan(t);
			return E;
		} catch (final Exception e) {
		}
		try {
			final BasicExpression E = ObjectExpression.scan(t);
			return E;
		} catch (final ConstructionException e) {
			throw e;
		} catch (final Exception e) {
		}
		throw new ConstructionException(Global.name("exception.elementary"));
	}

	@Override
	abstract public boolean isNumber();

	@Override
	abstract public double getValue() throws ConstructionException;
}

/**
 * E^E
 */
class PotenzExpression extends BasicExpression {

	BasicExpression E1, E2;

	public PotenzExpression(final BasicExpression e1, final BasicExpression e2) {
		E1 = e1;
		E2 = e2;
	}

	public static BasicExpression scan(final ExpressionText t)
	throws ConstructionException {
		final BasicExpression E1 = BasicExpression.scan(t);
		if (t.next() == '^' || (t.next() == '*' && t.nextnext() == '*')) {
			if (t.next() == '*') {
				t.advance();
			}
			t.advance();
			return scan(t, E1);
		}
		return E1;
	}

	public static BasicExpression scan(final ExpressionText t,
			final BasicExpression E) throws ConstructionException {
		final BasicExpression E1 = BasicExpression.scan(t);
		if (t.next() == '^' || (t.next() == '*' && t.nextnext() == '*')) {
			if (t.next() == '*') {
				t.advance();
			}
			t.advance();
			return scan(t, new PotenzExpression(E, E1));
		}
		return new PotenzExpression(E, E1);
	}

	@Override
	public double getValue() throws ConstructionException {
		return Math.pow(E1.getValue(), E2.getValue());
	}

	@Override
	public void translate() {
		E1.translate();
		E2.translate();
	}

	@Override
	public String toString() {
		return E1 + "^" + E2;
	}

	@Override
	public void reset() {
		E1.reset();
		E2.reset();
	}
}

/**
 * -E
 */
class MinusExpression extends ElementaryExpression {

	BasicExpression E;

	public MinusExpression(final BasicExpression e) {
		E = e;
	}

	@Override
	public double getValue() throws ConstructionException {
		return -E.getValue();
	}

	public static BasicExpression scan(final ExpressionText t)
	throws ConstructionException {
		if (t.next() != '-') {
			return PotenzExpression.scan(t);
		}
		t.advance();
		final BasicExpression E1 = PotenzExpression.scan(t);
		return new MinusExpression(E1);
	}

	@Override
	public void translate() {
		E.translate();
	}

	@Override
	public String toString() {
		return "-" + E.toString();
	}

	@Override
	public boolean isNumber() {
		return E.isNumber();
	}

	@Override
	public void reset() {
		E.reset();
	}
}

/**
 * E/E
 */
class QuotientExpression extends BasicExpression {

	BasicExpression E1, E2;

	public QuotientExpression(final BasicExpression e1, final BasicExpression e2) {
		E1 = e1;
		E2 = e2;
	}

	public static BasicExpression scan(final ExpressionText t,
			final BasicExpression E) throws ConstructionException {
		final BasicExpression E1 = MinusExpression.scan(t);
		if (t.next() == '/') {
			t.advance();
			return scan(t, new QuotientExpression(E, E1));
		} else if (t.next() == '*' && t.nextnext() != '*') {
			t.advance();
			return ProductExpression.scan(t, new QuotientExpression(E, E1));
		}
		return new QuotientExpression(E, E1);
	}

	@Override
	public double getValue() throws ConstructionException {
		return E1.getValue() / E2.getValue();
	}

	@Override
	public void translate() {
		E1.translate();
		E2.translate();
	}

	@Override
	public String toString() {
		return E1 + "/" + E2;
	}

	@Override
	public void reset() {
		E1.reset();
		E2.reset();
	}
}

/**
 * E*E
 */
class ProductExpression extends BasicExpression {

	BasicExpression E1, E2;

	public ProductExpression(final BasicExpression e1, final BasicExpression e2) {
		E1 = e1;
		E2 = e2;
	}

	public static BasicExpression scan(final ExpressionText t)
	throws ConstructionException {
		final BasicExpression E1 = MinusExpression.scan(t);
		if (t.next() == '*') {
			t.advance();
			return scan(t, E1);
		} else if (t.next() == '/') {
			t.advance();
			return QuotientExpression.scan(t, E1);
		}
		return E1;
	}

	public static BasicExpression scan(final ExpressionText t,
			final BasicExpression E) throws ConstructionException {
		final BasicExpression E1 = MinusExpression.scan(t);
		if (t.next() == '*' && t.nextnext() != '*') {
			t.advance();
			return scan(t, new ProductExpression(E, E1));
		} else if (t.next() == '/') {
			t.advance();
			return QuotientExpression.scan(t, new ProductExpression(E, E1));
		}
		return new ProductExpression(E, E1);
	}

	@Override
	public double getValue() throws ConstructionException {
		return E1.getValue() * E2.getValue();
	}

	@Override
	public void translate() {
		E1.translate();
		E2.translate();
	}

	@Override
	public String toString() {
		return E1 + "*" + E2;
	}

	@Override
	public void reset() {
		E1.reset();
		E2.reset();
	}
}

/**
 * E-E
 */
class DifferenceExpression extends BasicExpression {

	BasicExpression E1, E2;

	public DifferenceExpression(final BasicExpression e1,
			final BasicExpression e2) {
		E1 = e1;
		E2 = e2;
	}

	public static BasicExpression scan(final ExpressionText t,
			final BasicExpression E) throws ConstructionException {
		final BasicExpression E1 = ProductExpression.scan(t);
		if (t.next() == '+') {
			t.advance();
			return SumExpression.scan(t, new DifferenceExpression(E, E1));
		} else if (t.next() == '-') {
			t.advance();
			return scan(t, new DifferenceExpression(E, E1));
		}
		return new DifferenceExpression(E, E1);
	}

	@Override
	public double getValue() throws ConstructionException {
		return E1.getValue() - E2.getValue();
	}

	@Override
	public void translate() {
		E1.translate();
		E2.translate();
	}

	@Override
	public String toString() {
		return E1 + "-" + E2;
	}

	@Override
	public void reset() {
		E1.reset();
		E2.reset();
	}
}

/**
 * E+E
 */
class SumExpression extends BasicExpression {

	BasicExpression E1, E2;

	public SumExpression(final BasicExpression e1, final BasicExpression e2) {
		E1 = e1;
		E2 = e2;
	}

	public static BasicExpression scan(final ExpressionText t)
	throws ConstructionException {
		final BasicExpression E1 = ProductExpression.scan(t);
		if (t.next() == '+') {
			t.advance();
			return scan(t, E1);
		} else if (t.next() == '-') {
			t.advance();
			return DifferenceExpression.scan(t, E1);
		}
		return E1;
	}

	public static BasicExpression scan(final ExpressionText t,
			final BasicExpression E) throws ConstructionException {
		final BasicExpression E1 = ProductExpression.scan(t);
		if (t.next() == '+') {
			t.advance();
			return scan(t, new SumExpression(E, E1));
		} else if (t.next() == '-') {
			t.advance();
			return DifferenceExpression.scan(t, new SumExpression(E, E1));
		}
		return new SumExpression(E, E1);
	}

	@Override
	public double getValue() throws ConstructionException {
		return E1.getValue() + E2.getValue();
	}

	@Override
	public void translate() {
		E1.translate();
		E2.translate();
	}

	@Override
	public String toString() {
		return E1 + "+" + E2;
	}

	@Override
	public void reset() {
		E1.reset();
		E2.reset();
	}
}

/**
 * Diverse Vegleiche.
 */
class CompareExpression extends BasicExpression {

	BasicExpression E1, E2;
	int Operator;
	final static int LESS = 1, LESSEQUAL = 2, GREATER = 3, GREATEREQUAL = 4,
	EQUAL = 5, ABOUTEQUAL = 6;

	public CompareExpression(final BasicExpression e1,
			final BasicExpression e2, final int op) {
		E1 = e1;
		E2 = e2;
		Operator = op;
	}

	public static BasicExpression scan(final ExpressionText t)
	throws ConstructionException {
		final BasicExpression E1 = SumExpression.scan(t);
		final char c = t.next();
		int op = 0;
		if (c == '<') {
			t.advance();
			if (t.next(true) == '=') {
				op = LESSEQUAL;
				t.advance();
			} else {
				op = LESS;
			}
		} else if (c == '>') {
			t.advance();
			if (t.next(true) == '=') {
				op = GREATEREQUAL;
				t.advance();
			} else {
				op = GREATER;
			}
		} else if (c == '=') {
			t.advance();
			if (t.next(true) == '=') {
				t.advance();
			}
			op = EQUAL;
		} else if (c == '~') {
			t.advance();
			if (t.next(true) == '=') {
				t.advance();
			}
			op = ABOUTEQUAL;
		} else {
			return E1;
		}
		final BasicExpression E2 = SumExpression.scan(t);
		return new CompareExpression(E1, E2, op);
	}

	@Override
	public double getValue() throws ConstructionException {
		switch (Operator) {
		case LESS:
			return logical(E1.getValue() < E2.getValue());
		case LESSEQUAL:
			return logical(E1.getValue() <= E2.getValue());
		case GREATER:
			return logical(E1.getValue() > E2.getValue());
		case GREATEREQUAL:
			return logical(E1.getValue() >= E2.getValue());
		case EQUAL:
			// return logical(E1.getValue()==E2.getValue());
			return logical(Math.abs(E1.getValue() - E2.getValue()) < 1e-14);
		case ABOUTEQUAL:
			return logical(Math.abs(E1.getValue() - E2.getValue()) < 1e-10);
		}
		return 0.0;
	}

	public double logical(final boolean flag) {
		if (flag) {
			return 1.0;
		} else {
			return 0.0;
		}
	}

	@Override
	public void translate() {
		E1.translate();
		E2.translate();
	}

	@Override
	public String toString() {
		switch (Operator) {
		case LESS:
			return E1 + "<" + E2;
		case LESSEQUAL:
			return E1 + "<=" + E2;
		case GREATER:
			return E1 + ">" + E2;
		case GREATEREQUAL:
			return E1 + ">=" + E2;
		case EQUAL:
			return E1 + "==" + E2;
		case ABOUTEQUAL:
			return E1 + "~=" + E2;
		}
		return "";
	}

	@Override
	public void reset() {
		E1.reset();
		E2.reset();
	}

	@Override
	public boolean isLogical() {
		return true;
	}
}

/**
 * ! CE
 */
class NotExpression extends BasicExpression {

	BasicExpression E1;

	public NotExpression(final BasicExpression e1) {
		E1 = e1;
	}

	public static BasicExpression scan(final ExpressionText t)
	throws ConstructionException {
		if (t.next() != '!') {
			return CompareExpression.scan(t);
		}
		t.advance();
		final BasicExpression E1 = CompareExpression.scan(t);
		return new NotExpression(E1);
	}

	@Override
	public double getValue() throws ConstructionException {
		if (E1.getValue() == 0.0) {
			return 1.0;
		} else {
			return 0.0;
		}
	}

	@Override
	public void translate() {
		E1.translate();
	}

	@Override
	public String toString() {
		return "!" + E1;
	}

	@Override
	public void reset() {
		E1.reset();
	}

	@Override
	public boolean isLogical() {
		return true;
	}
}

/**
 * /* E & E
 */
class AndExpression extends BasicExpression {

	BasicExpression E1, E2;

	public AndExpression(final BasicExpression e1, final BasicExpression e2) {
		E1 = e1;
		E2 = e2;
	}

	public static BasicExpression scan(final ExpressionText t)
	throws ConstructionException {
		final BasicExpression E1 = NotExpression.scan(t);
		if (t.next() == '&') {
			t.advance();
			if (t.next(true) == '&') {
				t.advance();
			}
			return scan(t, E1);
		}
		return E1;
	}

	public static BasicExpression scan(final ExpressionText t,
			final BasicExpression E) throws ConstructionException {
		final BasicExpression E1 = NotExpression.scan(t);
		if (t.next() == '&') {
			t.advance();
			return scan(t, new AndExpression(E, E1));
		}
		return new AndExpression(E, E1);
	}

	@Override
	public double getValue() throws ConstructionException {
		if (E1.getValue() != 0.0 && E2.getValue() != 0.0) {
			return 1.0;
		} else {
			return 0.0;
		}
	}

	@Override
	public void translate() {
		E1.translate();
		E2.translate();
	}

	@Override
	public String toString() {
		return E1 + "&&" + E2;
	}

	@Override
	public void reset() {
		E1.reset();
		E2.reset();
	}

	@Override
	public boolean isLogical() {
		return true;
	}
}

/**
 * E | E
 */
class OrExpression extends BasicExpression {

	BasicExpression E1, E2;

	public OrExpression(final BasicExpression e1, final BasicExpression e2) {
		E1 = e1;
		E2 = e2;
	}

	public static BasicExpression scan(final ExpressionText t)
	throws ConstructionException {
		final BasicExpression E1 = AndExpression.scan(t);
		if (t.next() == '|') {
			t.advance();
			if (t.next(true) == '|') {
				t.advance();
			}
			return scan(t, E1);
		}
		return E1;
	}

	public static BasicExpression scan(final ExpressionText t,
			final BasicExpression E) throws ConstructionException {
		final BasicExpression E1 = AndExpression.scan(t);
		if (t.next() == '|') {
			t.advance();
			if (t.next(true) == '|') {
				t.advance();
			}
			return scan(t, new OrExpression(E, E1));
		}
		return new OrExpression(E, E1);
	}

	@Override
	//suppression de l'évaluation paresseuse par Patrice pour un ExecuteMacro Expression-compatible
	public double getValue() throws ConstructionException {
		try {
			if (E1.getValue() == 1.0) return 1.0;
		} catch (Exception e) {}
		try {
			if (E2.getValue() == 1.0) return 1.0;
		} catch (Exception e) {}
		if (E1.getValue() != 1.0 && E2.getValue() != 1.0) {return 0.0;} else {return 1.0;}
	
                }
	
	@Override
	public void translate() {
		E1.translate();
		E2.translate();
	}

	@Override
	public String toString() {
		return E1 + "||" + E2;
	}

	@Override
	public void reset() {
		E1.reset();
		E2.reset();
	}

	@Override
	public boolean isLogical() {
		return true;
	}
}

class TopExpression extends BasicExpression {

	public static BasicExpression scan(final ExpressionText t)
	throws ConstructionException {
		return OrExpression.scan(t);
	}
}

public class Expression {

	String S;
	BasicExpression E;
	boolean Valid;
	String ErrorText;
	DepList DL;
	double Value;
	boolean HasDoubleValue = false;
	boolean ForcePlus = false;

	/**
	 * Create a new expression
	 *
	 * @param s
	 *            expression string
	 * @param c
	 *            construction to search for objects
	 * @param o
	 *            "this" object
	 * @param var
	 *            variable names used in the referring object
	 * @param nocircles
	 *            prevent circular exressions
	 */
	public Expression(String s, final Construction c,
			final ConstructionObject o, final String var[],
			final boolean nocircles) {
		if (s.startsWith("+")) {
			ForcePlus = true;
			s = s.substring(1);
		}
		S = s;
		DL = new DepList();
		try {
			final ExpressionText t = new ExpressionText(s, c, o, DL, nocircles);
			t.setVar(var);
			E = TopExpression.scan(t);
			if (t.next() != 0) {
				throw new ConstructionException(Global.name("exception.superfluous"));
			}
			Valid = true;
		} catch (final Exception e) {
			Valid = false;
			ErrorText = (e.toString());
			// e.printStackTrace();
		}
	}

	public Expression(final String s, final Construction c,
			final ConstructionObject o, final String var[]) {
		this(s, c, o, var, false);
	}

	public Expression(final String s, final Construction c,
			final ConstructionObject o) {
		this(s, c, o, null, false);
	}

	public boolean isValid() {
		return Valid;
	}

	public String getErrorText() {
		return ErrorText;
	}

	public double getValue() throws ConstructionException {
		if (HasDoubleValue) {
			return Value;
		}
		if (!Valid) {
			throw new ConstructionException(Global.name("exception.expression"));
		}
		final double x = E.getValue();
		if (Double.isNaN(x) || Double.isInfinite(x)) {
			throw new ConstructionException(Global.name("exception.value"));
		}
		return x;
	}

	public void setValue(final double x) {
		HasDoubleValue = true;
		ForcePlus = false;
		DL = new DepList();
		Value = x;
	}

	@Override
	public String toString() {
		if (HasDoubleValue) {
			return "" + Value;
		}
		if (Valid) {
			return E.toString();
		} else {
			return S;
		}
	}

	public DepList getDepList() {
		return DL;
	}

	public void translate() {
		E.translate();
		DL.translate();
	}

	public boolean isNumber() {
		return HasDoubleValue || E.isNumber();
	}

	public void reset() {
		if (E != null && Valid) {
			E.reset();
		}
	}

	public boolean isForcePlus() {
		return ForcePlus;
	}

	public void setForcePlus(final boolean flag) {
		ForcePlus = flag;
	}

	public ConstructionObject getObject() {
		if (E instanceof ObjectExpression) {
			return ((ObjectExpression) E).getObject();
		} else {
			return null;
		}
	}

	/**
	 * Add the objects this expression depends on to the static dependency list
	 * of the current object.
	 */
	public void addDep(final ConstructionObject o) {
		final Enumeration e = getDepList().elements();
		while (e.hasMoreElements()) {
			ConstructionObject.DL.add((ConstructionObject) e.nextElement());
		}
	}
}
