package com.fp.common.formula;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;
import org.nfunk.jep.JEP;
import org.nfunk.jep.ParseException;
import org.nfunk.jep.function.PostfixMathCommand;

public class FormulaEvaluator {
    private static final Logger LOG = Logger.getLogger(FormulaEvaluator.class);

    private static final ThreadLocal<FormulaEvaluator> running = new ThreadLocal<FormulaEvaluator>();

    private JEP parser;

    private String expression;

    private Map<String, Object> constants = new HashMap<String, Object>();

    public FormulaEvaluator() {
        this(null);
    }

    public static FormulaEvaluator getRunning() {
        return FormulaEvaluator.running.get();
    }

    public FormulaEvaluator(String pExpression) {
        this.parser = new JEP();
        this.parser.addStandardConstants();
        this.parser.addStandardFunctions();
        this.parser.setAllowUndeclared(false);
        this.parser.setImplicitMul(false);
        this.expression = pExpression;

    }

    public BigDecimal evalNumeric() throws Exception {
        this.parse();
        FormulaEvaluator.running.set(this);
        Double val = this.parser.getValue();
        if (val.isNaN()) {
            throw new Exception(this.parser.getErrorInfo());
        }
        FormulaEvaluator.LOG.debug(val);
        return BigDecimal.valueOf(val);
    }

    /**
     * Entrega el Valor de expression
     * 
     * @return Valor de expression
     */
    public String getExpression() {
        return this.expression;
    }

    public void parse() throws ParseException {
        this.parser.parseExpression(this.expression);
        if (this.parser.hasError()) {
            throw new ParseException(this.parser.getErrorInfo());
        }
    }

    public void setConstant(String pName, Object pVal) {
        this.constants.put(pName, pVal);
        this.parser.addConstant(pName, pVal);
    }

    public Object getConstant(String pName) {
        return this.constants.get(pName);
    }

    /**
     * Fija el valor de la propiedad expression
     * 
     * @param expression El valor para expression
     */
    public void setExpression(String expression) {
        this.expression = expression;
    }

    public void setFunction(String pName, PostfixMathCommand pCommand) {
        this.parser.addFunction(pName, pCommand);
    }
}