package com.eactive.eai.transformer.engine;

import java.io.Serializable;

import org.nfunk.jep.JEP;
import org.nfunk.jep.function.PostfixMathCommand;

import com.eactive.eai.transformer.function.FunctionDefinition;
import com.eactive.eai.transformer.function.FunctionManager;
import com.eactive.eai.transformer.function.crypto.Decrypt;
import com.eactive.eai.transformer.function.crypto.Des3Decrypt;
import com.eactive.eai.transformer.function.crypto.Des3Encrypt;
import com.eactive.eai.transformer.function.crypto.DesDecrypt;
import com.eactive.eai.transformer.function.crypto.DesEncrypt;
import com.eactive.eai.transformer.function.crypto.Encrypt;
import com.eactive.eai.transformer.function.crypto.Seed128Decrypt;
import com.eactive.eai.transformer.function.crypto.Seed128Encrypt;
import com.eactive.eai.transformer.function.crypto.SeedDecrypt;
import com.eactive.eai.transformer.function.crypto.SeedEncrypt;
import com.eactive.eai.transformer.function.generic.ToBytes;
import com.eactive.eai.transformer.util.Logger;

public class Parser implements Serializable {

	private static final long serialVersionUID = -3657522834962737552L;

	private static Logger logger = Logger.getLogger("transformer");

	private transient JEP jep = new JEP(); // Create a new parser

	public Parser() {
		getJep().addStandardFunctions();
		getJep().addStandardConstants();

		getJep().setImplicitMul(true);
		getJep().setAllowAssignment(true);
		getJep().setAllowUndeclared(true);

		try {
			addGenericFunctions();
		} catch (Exception e) {
			logger.error("Parser | error when add generic function", e);
		}
	}

	public void parse(String expr) throws Exception {
		getJep().parseExpression(expr);

		if (getJep().hasError()) {
			throw new Exception("[Parser] Error when parsing expr : " + getJep().getErrorInfo());
		}
	}

	public void addVariable(String name, double value) throws Exception {
		getJep().addVariable(name, value);

		if (getJep().hasError()) {
			throw new Exception("[Parser] Error when parsing double : " + getJep().getErrorInfo());
		}
	}

	public void addVariable(String name, Object value) throws Exception {
		getJep().addVariable(name, value);

		if (getJep().hasError()) {
			throw new Exception("[Parser] Error when parsing Object : " + getJep().getErrorInfo());
		}
	}

	public double getValue() throws Exception {
		double value = getJep().getValue();

		if (getJep().hasError()) {
			throw new Exception("[Parser] Error during evaluation :  " + getJep().getErrorInfo());
		}

		return value;
	}

	public Object getValueAsObject() throws Exception {
		Object value = getJep().getValueAsObject();

		if (getJep().hasError()) {
			throw new Exception("[Parser] Error during evaluation - error info :  [" + getJep().getErrorInfo() + "]");
		}

		return value;
	}

	public void initSymbolTable() {
		getJep().initSymTab();
	}

	@SuppressWarnings("unchecked")
	public String[] getSymbols() {
		String[] symbols = new String[getJep().getSymbolTable().size()];
		symbols = (String[])getJep().getSymbolTable().keySet().toArray(new String[symbols.length]);
		return symbols;
	}

	private void addGenericFunctions() throws Exception {
        FunctionManager manager = FunctionManager.getInstance();
        
        String [] funcName = manager.getAllFunctionNames();
        int size = funcName.length;
        FunctionDefinition funcDef = null;
        PostfixMathCommand command = null;
        for( int i = 0; i < size; i++) {
        	funcDef = manager.getFunctionDefinition(funcName[i]);
        	if (funcDef == null) continue;
        	String functionClass = funcDef.getFunctionClass();
        	if (functionClass == null) continue;
        	if(!(functionClass.startsWith("com.eactive.eai.transformer.function")
        			|| functionClass.startsWith("com.eactive.eai.custom.transformer.function"))) {
        		continue;
        	}
        	
            // 등록된 함수의 실제 Class가 없는 경우 Skip 한다.
        	try {
        		command = (PostfixMathCommand) Class.forName(functionClass).newInstance();
        	} catch (Exception e ) {
        		continue;  
        	}
            try{     
                addFunction(funcName[i], command);
            }catch (Exception e ) {
                 throw new Exception("[Parser] Error during addGenericfunction :  " + getJep().getErrorInfo());  
            }
        }          

		addFunction("tobytes", new ToBytes());
		addFunction("encrypt", new Encrypt());
		addFunction("decrypt", new Decrypt());
		addFunction("des_encrypt", new DesEncrypt());
		addFunction("des_decrypt", new DesDecrypt());
		addFunction("des3_encrypt", new Des3Encrypt());
		addFunction("des3_decrypt", new Des3Decrypt());
		addFunction("seed_encrypt", new SeedEncrypt());
		addFunction("seed_decrypt", new SeedDecrypt());
		addFunction("seed128_encrypt", new Seed128Encrypt());
		addFunction("seed128_decrypt", new Seed128Decrypt());
	}
	
	private void addFunction(String name, PostfixMathCommand command) {
		if(name == null || command == null) {
			logger.warn("addFunction ("+name+","+command+") skip null");
			return; 
		}
		getJep().addFunction(name.toUpperCase(), command);
		getJep().addFunction(name.toLowerCase(), command);
	}

	public JEP getJep() {
		return jep;
	}

	public void setJep(JEP jep) {
		this.jep = jep;
	}
}
