package metalexer.jflex;

import java.io.Reader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import test.jflex.natlab.CommentBuffer;
import beaver.Scanner;

public class ReflectionHelper {
    @SuppressWarnings("unchecked")
    public static Scanner instantiateScanner(String className, Reader reader) {
        try {
            Class<Scanner> scannerClass = (Class<Scanner>) Class.forName(className);
            Constructor<Scanner> scannerConstructor = scannerClass.getConstructor(Reader.class);
            return scannerConstructor.newInstance(reader);
        } catch(Exception e) {
            throw new RuntimeException("Failed to instantiate scanner " + className, e);
        }
    }

    @SuppressWarnings("unchecked")
    public static Scanner instantiateScanner(String className, Reader reader, CommentBuffer commentBuffer) {
        try {
            Class<Scanner> scannerClass = (Class<Scanner>) Class.forName(className);
            Constructor<Scanner> scannerConstructor = scannerClass.getConstructor(Reader.class);
            Method commentBufferMethod = scannerClass.getMethod("setCommentBuffer", CommentBuffer.class);
            Scanner scannerInstance = scannerConstructor.newInstance(reader);
            commentBufferMethod.invoke(scannerInstance, commentBuffer);
            return scannerInstance;
        } catch(Exception e) {
            throw new RuntimeException("Failed to instantiate scanner " + className, e);
        }
    }

    public static class MetaLexerWrapper {
        private final Object mlInstance;
        private final Method processMethod;
        
        private final Class<?> mlTransitionClass;
        private final Method embeddingNumMethod;

        public MetaLexerWrapper(String className) {
            try {
                Class<?> mlClass = Class.forName(className);
                mlInstance = mlClass.newInstance();
                processMethod = mlClass.getMethod("processSymbol", int.class);
                
                mlTransitionClass = Class.forName(className + "$Transition");
                embeddingNumMethod = mlTransitionClass.getMethod("getEmbeddingNum");
            } catch(Exception e) {
                throw new RuntimeException("Failed to instantiate meta-lexer " + className, e);
            }
        }

        public Integer processSymbol(int sym) {
            try {
                Object transition = processMethod.invoke(mlInstance, sym);
                return (transition == null) ? null :  (Integer) embeddingNumMethod.invoke(transition);
            } catch (Exception e) {
                throw new RuntimeException("Failed to invoke processSymbol()", e);
            }
        }
    }
}
