package com.fp.common.formula;

import java.math.BigDecimal;
import java.sql.Date;
import java.util.Stack;

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

import com.fp.common.helper.BeanManager;

public abstract class AbstractFunction extends PostfixMathCommand {
    private static final Logger LOG = Logger.getLogger(AbstractFunction.class);

    public abstract Object eval() throws Exception;

    protected Stack stack;

    @SuppressWarnings("unchecked")
    @Override
    public void run(Stack pStack) throws ParseException {
        this.stack = pStack;
        this.checkStack(pStack);
        try {
            pStack.push(this.eval());
        } catch (Exception e) {
            AbstractFunction.LOG.warn(e.getMessage(), e);
            throw new ParseException(e.getMessage());
        }
    }

    protected Object nextObjectParameter() {
        // return this.stack.remove(0);
        return this.stack.pop();
    }

    protected Object getConstant(String pName) {
        return FormulaEvaluator.getRunning().getConstant(pName);
    }

    protected String getStringConstant(String pName) throws Exception {
        String val = (String) FormulaEvaluator.getRunning().getConstant(pName);
        if (val == null) {
            throw new ParseException("Constante no especificada " + pName);
        }
        return val;
    }

    protected BigDecimal getBigDecimalConstant(String pName) throws Exception {
        Object val = FormulaEvaluator.getRunning().getConstant(pName);
        if (val == null) {
            throw new ParseException("Constante no especificada " + pName);
        }
        if (val instanceof Number) {
            return new BigDecimal(val.toString());
        }
        throw new ParseException("Valor no Valido");
    }

    protected BigDecimal nextBigDecimalParameter() throws Exception {
        Object param = this.nextObjectParameter();
        if (param instanceof Number) {
            return new BigDecimal(param.toString());
        }
        if (param instanceof String) {
            String constant = (String) param;
            Object val = this.getConstant(constant);
            if ((val != null) && (val instanceof Number)) {
                return new BigDecimal(val.toString());
            }
        }
        throw new ParseException("Tipo de Dato incorrecto");

    }

    protected Date nextDateParameter() throws Exception {
        Object param = this.nextObjectParameter();
        if (param instanceof Date) {
            return (Date) param;
        }
        return BeanManager.convertObject(param, Date.class);

    }

    protected String nextStringParameter() {
        Object param = this.nextObjectParameter();
        return param.toString();
    }
}
