package metalexer.jflex;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Properties;

public class JFlexHelper {

    private static final String PROP_FILE = "common.properties";
    private static final String LIB_DIR_PROP_NAME = "lib.dir";
    private static final String JAR_PROP_NAME = "jflex.jar.path.prop";

    private final Class<?> silentExitClass;
    private final Method genMethod;
    private final boolean jflexAvailable;

    public JFlexHelper() {
        Class<?> silentExitClass = null;
        Method genMethod = null;
        boolean jflexAvailable = false;
        try {
            Properties properties = new Properties();
            try {
                properties.load(new FileInputStream(PROP_FILE));
            } catch (IOException e) {
                throw new RuntimeException("Failed to load properties file " + PROP_FILE, e);
            }
            String libDir = properties.getProperty(LIB_DIR_PROP_NAME);
            if(libDir == null) {
                throw new RuntimeException("Failed to load property " + LIB_DIR_PROP_NAME);
            }
            String jarFile = properties.getProperty(JAR_PROP_NAME);
            if(jarFile == null) {
                throw new RuntimeException("Failed to load property " + JAR_PROP_NAME);
            }
            jarFile = libDir + "/" + jarFile;

            URL jarURL = null;
            try {
                jarURL = new URL("file:" + jarFile);
                ClassLoader loader = new URLClassLoader(new URL[] { jarURL });
                silentExitClass = Class.forName("JFlex.SilentExit", true, loader);
                Class<?> mainClass = Class.forName("JFlex.Main", true, loader);
                genMethod = mainClass.getMethod("generate", String[].class);
                jflexAvailable = true;
            } catch (MalformedURLException e) {
                //TODO: could be a real error - the property might be incorrect
                throw new RuntimeException("Jar path is incorrect: " + jarURL, e);
            } catch (Exception e) {
                //suppress error - just let jflexAvailable get set to false
            }
        } finally {
            this.silentExitClass = silentExitClass;
            this.genMethod = genMethod;
            this.jflexAvailable = jflexAvailable;
        }
    }

    public boolean isJFlexAvailable() {
        return jflexAvailable;
    }

    public void generate(String file, String destDir) {
        if(!jflexAvailable) {
            throw new IllegalStateException("Can't generate when jflex is not available");
        }
        try {
            genMethod.invoke(null, (Object) new String[] {"-d", destDir, "--nobak", "-v", file});
        } catch(InvocationTargetException e) {
            Throwable targetException = e.getTargetException();
            if(silentExitClass.isInstance(targetException)) {
                //suppress this error - it indicates normal termination
            } else {
                //TODO: could be a real error
                throw new RuntimeException(targetException);
            }
        } catch (IllegalArgumentException e) {
            //this should never happen - the arguments are correct
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            //this should never happen - the method is public and static
            throw new RuntimeException(e);
        }
    }
}
