package org.nfunk.jep;

import java.io.StringReader;
import java.util.Vector;
import org.nfunk.jep.function.Abs;
import org.nfunk.jep.function.ArcCosine;
import org.nfunk.jep.function.ArcCosineH;
import org.nfunk.jep.function.ArcSine;
import org.nfunk.jep.function.ArcSineH;
import org.nfunk.jep.function.ArcTanH;
import org.nfunk.jep.function.ArcTangent;
import org.nfunk.jep.function.ArcTangent2;
import org.nfunk.jep.function.Arg;
import org.nfunk.jep.function.ComplexPFMC;
import org.nfunk.jep.function.Cosine;
import org.nfunk.jep.function.CosineH;
import org.nfunk.jep.function.Exp;
import org.nfunk.jep.function.If;
import org.nfunk.jep.function.Imaginary;
import org.nfunk.jep.function.Logarithm;
import org.nfunk.jep.function.Modulus;
import org.nfunk.jep.function.NaturalLogarithm;
import org.nfunk.jep.function.Polar;
import org.nfunk.jep.function.PostfixMathCommandI;
import org.nfunk.jep.function.Random;
import org.nfunk.jep.function.Real;
import org.nfunk.jep.function.Sine;
import org.nfunk.jep.function.SineH;
import org.nfunk.jep.function.SquareRoot;
import org.nfunk.jep.function.Str;
import org.nfunk.jep.function.Sum;
import org.nfunk.jep.function.TanH;
import org.nfunk.jep.function.Tangent;
import org.nfunk.jep.type.Complex;
import org.nfunk.jep.type.DoubleNumberFactory;
import org.nfunk.jep.type.NumberFactory;

public class JEP {
  private static final boolean debug = false;
  
  private boolean traverse = false;
  
  protected boolean allowUndeclared = false;
  
  protected boolean allowAssignment;
  
  protected boolean implicitMul;
  
  protected SymbolTable symTab;
  
  protected FunctionTable funTab;
  
  protected Vector errorList;
  
  protected Parser parser;
  
  private Node topNode = null;
  
  protected EvaluatorVisitor ev;
  
  protected NumberFactory numberFactory;
  
  protected OperatorSet opSet;
  
  public JEP() {
    this.allowAssignment = false;
    this.implicitMul = false;
    this.numberFactory = (NumberFactory)new DoubleNumberFactory();
    this.opSet = new OperatorSet();
    initSymTab();
    initFunTab();
    this.errorList = new Vector();
    this.ev = new EvaluatorVisitor();
    this.parser = new Parser(new StringReader(""));
  }
  
  public JEP(boolean paramBoolean1, boolean paramBoolean2, boolean paramBoolean3, NumberFactory paramNumberFactory) {
    this.implicitMul = paramBoolean3;
    if (paramNumberFactory == null) {
      this.numberFactory = (NumberFactory)new DoubleNumberFactory();
    } else {
      this.numberFactory = paramNumberFactory;
    } 
    initSymTab();
    initFunTab();
    this.errorList = new Vector();
    this.ev = new EvaluatorVisitor();
    this.parser = new Parser(new StringReader(""));
    parseExpression("");
  }
  
  protected JEP(JEP paramJEP) {
    this.allowAssignment = paramJEP.allowAssignment;
    this.implicitMul = paramJEP.implicitMul;
    this.ev = paramJEP.ev;
    this.funTab = paramJEP.funTab;
    this.numberFactory = paramJEP.numberFactory;
    this.parser = paramJEP.parser;
    this.symTab = paramJEP.symTab;
    this.errorList = paramJEP.errorList;
  }
  
  public void initSymTab() {
    this.symTab = new SymbolTable(new VariableFactory());
  }
  
  public void initFunTab() {
    this.funTab = new FunctionTable();
  }
  
  public void addStandardFunctions() {
    this.funTab.put("sin", (PostfixMathCommandI)new Sine());
    this.funTab.put("cos", (PostfixMathCommandI)new Cosine());
    this.funTab.put("tan", (PostfixMathCommandI)new Tangent());
    this.funTab.put("asin", (PostfixMathCommandI)new ArcSine());
    this.funTab.put("acos", (PostfixMathCommandI)new ArcCosine());
    this.funTab.put("atan", (PostfixMathCommandI)new ArcTangent());
    this.funTab.put("atan2", (PostfixMathCommandI)new ArcTangent2());
    this.funTab.put("sinh", (PostfixMathCommandI)new SineH());
    this.funTab.put("cosh", (PostfixMathCommandI)new CosineH());
    this.funTab.put("tanh", (PostfixMathCommandI)new TanH());
    this.funTab.put("asinh", (PostfixMathCommandI)new ArcSineH());
    this.funTab.put("acosh", (PostfixMathCommandI)new ArcCosineH());
    this.funTab.put("atanh", (PostfixMathCommandI)new ArcTanH());
    this.funTab.put("log", (PostfixMathCommandI)new Logarithm());
    this.funTab.put("ln", (PostfixMathCommandI)new NaturalLogarithm());
    this.funTab.put("exp", (PostfixMathCommandI)new Exp());
    this.funTab.put("sqrt", (PostfixMathCommandI)new SquareRoot());
    this.funTab.put("abs", (PostfixMathCommandI)new Abs());
    this.funTab.put("mod", (PostfixMathCommandI)new Modulus());
    this.funTab.put("sum", (PostfixMathCommandI)new Sum());
    this.funTab.put("rand", (PostfixMathCommandI)new Random());
    this.funTab.put("if", (PostfixMathCommandI)new If());
    this.funTab.put("str", (PostfixMathCommandI)new Str());
  }
  
  public void addStandardConstants() {
    this.symTab.addConstant("pi", new Double(Math.PI));
    this.symTab.addConstant("e", new Double(Math.E));
  }
  
  public void addComplex() {
    this.symTab.addConstant("i", new Complex(0.0D, 1.0D));
    this.funTab.put("re", (PostfixMathCommandI)new Real());
    this.funTab.put("im", (PostfixMathCommandI)new Imaginary());
    this.funTab.put("arg", (PostfixMathCommandI)new Arg());
    this.funTab.put("cmod", (PostfixMathCommandI)new Abs());
    this.funTab.put("complex", (PostfixMathCommandI)new ComplexPFMC());
    this.funTab.put("polar", (PostfixMathCommandI)new Polar());
  }
  
  public void addFunction(String paramString, PostfixMathCommandI paramPostfixMathCommandI) {
    this.funTab.put(paramString, paramPostfixMathCommandI);
  }
  
  public Double addVariable(String paramString, double paramDouble) {
    Double double_ = new Double(paramDouble);
    this.symTab.makeVarIfNeeded(paramString, double_);
    return double_;
  }
  
  public void addConstant(String paramString, Object paramObject) {
    this.symTab.addConstant(paramString, paramObject);
  }
  
  public Complex addVariable(String paramString, double paramDouble1, double paramDouble2) {
    Complex complex = new Complex(paramDouble1, paramDouble2);
    this.symTab.makeVarIfNeeded(paramString, complex);
    return complex;
  }
  
  public void addVariable(String paramString, Object paramObject) {
    this.symTab.makeVarIfNeeded(paramString, paramObject);
  }
  
  public Object removeVariable(String paramString) {
    return this.symTab.remove(paramString);
  }
  
  public Object getVarValue(String paramString) {
    return this.symTab.getVar(paramString).getValue();
  }
  
  public boolean setVarValue(String paramString, Object paramObject) {
    return this.symTab.setVarValue(paramString, paramObject);
  }
  
  public Variable getVar(String paramString) {
    return this.symTab.getVar(paramString);
  }
  
  public Object removeFunction(String paramString) {
    return this.funTab.remove(paramString);
  }
  
  public void setTraverse(boolean paramBoolean) {
    this.traverse = paramBoolean;
  }
  
  public boolean getTraverse() {
    return this.traverse;
  }
  
  public void setImplicitMul(boolean paramBoolean) {
    this.implicitMul = paramBoolean;
  }
  
  public boolean getImplicitMul() {
    return this.implicitMul;
  }
  
  public void setAllowUndeclared(boolean paramBoolean) {
    this.allowUndeclared = paramBoolean;
  }
  
  public boolean getAllowUndeclared() {
    return this.allowUndeclared;
  }
  
  public void setAllowAssignment(boolean paramBoolean) {
    this.allowAssignment = paramBoolean;
  }
  
  public boolean getAllowAssignment() {
    return this.allowAssignment;
  }
  
  public void parseExpression(String paramString) {
    StringReader stringReader = new StringReader(paramString);
    try {
      this.errorList.removeAllElements();
      this.topNode = this.parser.parseStream(stringReader, this);
    } catch (Throwable throwable) {
      this.topNode = null;
      if (throwable instanceof ParseException) {
        this.errorList.addElement(((ParseException)throwable).getMessage());
      } else {
        this.errorList.addElement("Syntax error");
      } 
    } 
    if (this.traverse && !hasError()) {
      ParserDumpVisitor parserDumpVisitor = new ParserDumpVisitor();
      try {
        this.topNode.jjtAccept(parserDumpVisitor, null);
      } catch (ParseException parseException) {
        this.errorList.addElement(parseException.getMessage());
      } 
    } 
  }
  
  public Node parse(String paramString) throws ParseException {
    StringReader stringReader = new StringReader(paramString);
    return this.parser.parseStream(stringReader, this);
  }
  
  public Object evaluate(Node paramNode) throws Exception {
    return this.ev.getValue(paramNode, new Vector(), this.symTab);
  }
  
  public double getValue() {
    Object object = getValueAsObject();
    if (object == null)
      return Double.NaN; 
    if (object instanceof Complex) {
      Complex complex = (Complex)object;
      return (complex.im() != 0.0D) ? Double.NaN : complex.re();
    } 
    return (object != null && object instanceof Number) ? ((Number)object).doubleValue() : Double.NaN;
  }
  
  public Complex getComplexValue() {
    Object object = getValueAsObject();
    return (object == null) ? null : ((object instanceof Complex) ? (Complex)object : ((object instanceof Number) ? new Complex(((Number)object).doubleValue(), 0.0D) : null));
  }
  
  public Object getValueAsObject() {
    if (this.topNode != null && !hasError()) {
      Object object;
      try {
        object = this.ev.getValue(this.topNode, this.errorList, this.symTab);
      } catch (Exception exception) {
        this.errorList.addElement("Error during evaluation");
        return null;
      } 
      return object;
    } 
    return null;
  }
  
  public boolean hasError() {
    return !this.errorList.isEmpty();
  }
  
  public String getErrorInfo() {
    if (hasError()) {
      String str = "";
      for (byte b = 0; b < this.errorList.size(); b++)
        str = str + this.errorList.elementAt(b) + "\n"; 
      return str;
    } 
    return null;
  }
  
  public Node getTopNode() {
    return this.topNode;
  }
  
  public SymbolTable getSymbolTable() {
    return this.symTab;
  }
  
  public FunctionTable getFunctionTable() {
    return this.funTab;
  }
  
  public NumberFactory getNumberFactory() {
    return this.numberFactory;
  }
  
  public OperatorSet getOperatorSet() {
    return this.opSet;
  }
  
  public Parser getParser() {
    return this.parser;
  }
}
