package leigh.conflux.nodegraph;

import leigh.mecha.log.MechaLogger;
import leigh.mecha.log.MechaLoggerFactory;
import leigh.mecha.nodegraph.Node;
import leigh.mecha.nodegraph.ProcessorState;
import leigh.mecha.nodegraph.UselessAttribute;
import leigh.mecha.util.Snavig;
import org.apache.commons.lang.math.NumberUtils;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.io.Serializable;

/**
 * Process a Graal expression.
 *
 * @author Alex Leigh
 */
public class ExpressionNode extends Node {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(ExpressionNode.class);
    public static final String NODE_NAME = "leigh.conflux.v1.Expression";
    public static final String KEY_IN_EXPR = "expr";
    public static final String KEY_IN_A = "a";
    public static final String KEY_IN_B = "b";
    public static final String KEY_IN_C = "c";
    public static final String KEY_IN_D = "d";
    public static final String KEY_VAR_A = "a";
    public static final String KEY_VAR_B = "b";
    public static final String KEY_VAR_C = "c";
    public static final String KEY_VAR_D = "d";

    public static final String KEY_OUT_VALUE = "value";
    private final ScriptEngine scriptEngine;

    public ExpressionNode(String id) {
        super(id);
        ScriptEngineManager mgr = new ScriptEngineManager();
        scriptEngine = mgr.getEngineByName("graal.js");

        assert (scriptEngine != null);

        addOutput(KEY_OUT_VALUE, new UselessAttribute() {
            public Serializable getSingleValue(ProcessorState state) throws Exception {
                Serializable aVal = getValue(KEY_IN_A, state);
                Serializable bVal = getValue(KEY_IN_B, state);
                Serializable cVal = getValue(KEY_IN_C, state);
                Serializable dVal = getValue(KEY_IN_D, state);

                logger.debug("Read values. [a: {}] [b: {}] [c: {}] [d: {}]", aVal, bVal, cVal, dVal);

                scriptEngine.put(KEY_VAR_A, aVal);
                scriptEngine.put(KEY_VAR_B, bVal);
                scriptEngine.put(KEY_VAR_C, cVal);
                scriptEngine.put(KEY_VAR_D, dVal);

                Object o = scriptEngine.eval(String.valueOf(getInputSingle(KEY_IN_EXPR, state)));
                String v = String.valueOf(o);
                if (o != null && v.equals("null")) return null;
                return v;
            }
        });
    }

    /**
     * Return a String value given different sorts of inputs that this node might experience.
     */
    private Serializable getValue(String key, ProcessorState state) throws Exception {
        Serializable x = getInputSingle(key, state);
       /*
        if (x == null) {
            EO obj = getInputObject(key, state);
            logger.debug("Object input case. [obj: {}]", obj);
            if(obj!=null) {
                x = obj.getValue(key);
            }
        }
        */

        if (x == null) return null;

        String s = Snavig.convertToString(x);

        if (NumberUtils.isNumber(s)) {
            return NumberUtils.createNumber(s);
        }

        // FIXME: This will lose precision for integers
        try {
            return Double.parseDouble(s);
        } catch (NumberFormatException e) {
            // Intentionally Blank
        }

        return x;
    }
}
