package edu.sc.seis.sod.subsetter;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import org.w3c.dom.Element;

import edu.sc.seis.sod.ConfigurationException;
import edu.sc.seis.sod.SodUtil;
import edu.sc.seis.sod.Start;
import edu.sc.seis.sod.status.Pass;
import edu.sc.seis.sod.status.StringTree;
import edu.sc.seis.sod.status.StringTreeLeaf;

public class AbstractScriptSubsetter implements Subsetter {

    public AbstractScriptSubsetter(Element config) throws ConfigurationException {
        this.config = config;
        scriptType = config.getAttribute("type");
        engine = factory.getEngineByName(scriptType);
        if (engine == null) {
        	throw new ConfigurationException("Unable to load script engine for "+scriptType);
        }
        script = cleanScript(SodUtil.getNestedText(config));
    }

    protected Object preeval() throws Exception {
        try {
            engine.put("result", new Pass(this));
            engine.put("util", new ScriptUtil(this));
            Object result = engine.eval(script);
            return result;// might be null
        } catch(ScriptException e) {
            logger.error("Problem in script: \n-------------|\n" + script + "|-------", e);
            Start.exit("Problem in script: \n-------------|\n" + script + "|-------\n\n" + e);
            // never get here...
            throw new RuntimeException();
        }
    }

    protected Object pullResult(Object result) throws Exception {
        if (result == null) {
            // try getting variable named result from engine
            result = engine.get("result");
        }
        if (result == null) {
            // assume all well and return a Pass
            return new Pass(this);
        }
        if (result instanceof StringTree) {
            return (StringTree)result;
        } else if (result instanceof Boolean) {
            return new StringTreeLeaf(this, ((Boolean)result).booleanValue());
        } else {
            throw new UnknownScriptResult("Script returns unknown results type, should be boolean or StringTree: " + result.toString());
        }
    }

    protected StringTree eval() throws Exception {
        return (StringTree)pullResult(preeval());
    }

    public static String cleanScript(String script) {
        if (script.indexOf("\n") == -1) {
            // one line
            return script.trim();
        }
        String trimBegEnd = script;
        // clean blank lines from beginning
        Matcher matcher = Pattern.compile("(?: *\\n)+(.*)", Pattern.DOTALL).matcher(trimBegEnd);
        if (matcher.matches()) {
            trimBegEnd = matcher.group(1);
        }
        // clean blank lines from end
        matcher = Pattern.compile("((?:(?: *\\n)*(?: *\\S[^\\n]*\\n)+)+)(?: *\\n?)*").matcher(trimBegEnd);
        if (matcher.matches()) {
            trimBegEnd = matcher.group(1);
        }
        String out = "";
        String[] lines = trimBegEnd.split("[\\r?\\n]");
        matcher = Pattern.compile("( *).*").matcher(lines[0]);
        if (matcher.matches()) {
            int numSpaces = matcher.group(1).length();
            Pattern spaceTrimmer = Pattern.compile(" {0," + numSpaces + "}(.*)");
            for (int i = 0; i < lines.length; i++) {
                matcher = spaceTrimmer.matcher(lines[i]);
                if (matcher.matches()) {
                    out += matcher.group(1) + "\n";
                } else {
                    throw new RuntimeException("How can this not match? pat=\"" + spaceTrimmer.pattern() + "\"  |"
                            + lines[i] + "|");
                }
            }
        } else {
            throw new RuntimeException("Didn't match:" + lines[0] + "|");
        }
        return out;
    }

    public static String formatForJythonSysPath(Class c, String jarDir) {
        String out = c.getClassLoader().getResource(jarDir).toString().substring("jar:file:".length()).replaceAll("\\!","");
        return out;
    }
    
    protected String script;

    protected Element config;

    protected String scriptType;;

    protected ScriptEngine engine;

    protected static ScriptEngineManager factory = new ScriptEngineManager();

    private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(AbstractScriptSubsetter.class);
}
