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 MissingJFlex {

    private static final String PROP_FILE = "common.properties";
    private static final String PROP_NAME = "jflex.jar.path.prop";

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

    public MissingJFlex() {
        boolean jflexAvail = 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 jarFile = properties.getProperty(PROP_NAME);
            if(jarFile == null) {
                throw new RuntimeException("Failed to load property " + PROP_NAME);
            }

            URL jarURL = null;
            try {
                jarURL = new URL("file:lib/jflex-1.4.1/lib/JFlex.jar");
                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);
                jflexAvail = 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 (ClassNotFoundException e) {
                //TODO: could be a real error - the jar might be incorrect
                throw new RuntimeException(e);
            } catch (NoSuchMethodException e) {
                //this should never happen - the method exists
                throw new RuntimeException(e);
            } catch (SecurityException e) {
                //this should never happen - the method is public
                throw new RuntimeException(e);
            }
        } finally {
            jflexAvailable = jflexAvail;
        }
    }

    public boolean isJFlexAvailable() {
        return jflexAvailable;
    }

    public void generate(String file, String destDir) {
        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);
        }
    }

    public static void main(String[] args) {
        MissingJFlex jflex = new MissingJFlex();
        jflex.generate("scratch/pipetest.flex", "scratch/missingjflex");
    }
}
