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 PipeWrapper {
        private final Object pipeInstance;
        private final Method writeMethod;
        private final Method writeFinalMethod;

        public PipeWrapper(String className) {
            try {
                Class<?> pipeClass = Class.forName(className);
                pipeInstance = pipeClass.newInstance();
                writeMethod = pipeClass.getMethod("write", char.class);
                writeFinalMethod = pipeClass.getMethod("writeFinal", char.class);
            } catch(Exception e) {
                throw new RuntimeException("Failed to instantiate pipe " + className, e);
            }
        }

        public void write(char ch) {
            try {
                writeMethod.invoke(pipeInstance, ch);
            } catch (Exception e) {
                throw new RuntimeException("Failed to invoke write()", e);
            }
        }

        public void writeFinal(char ch) {
            try {
                writeFinalMethod.invoke(pipeInstance, ch);
            } catch (Exception e) {
                throw new RuntimeException("Failed to invoke writeFinal()", e);
            }

        }
    }

    public static class MetaLexerWrapper {
        private final Object mlInstance;
        private final Method startMethod;
        private final Method checkMethod;

        public MetaLexerWrapper(String className, PipeWrapper pipeWrapper) {
            try {
                Class<?> mlClass = Class.forName(className);
                Constructor<?> mlConstructor = mlClass.getConstructor(Reader.class);
                mlInstance = mlConstructor.newInstance(pipeWrapper.pipeInstance);
                startMethod = mlClass.getMethod("start");
                checkMethod = mlClass.getMethod("checkForTransition");
            } catch(Exception e) {
                throw new RuntimeException("Failed to instantiate meta-lexer " + className, e);
            }
        }

        public void start() {
            try {
                startMethod.invoke(mlInstance);
            } catch (Exception e) {
                throw new RuntimeException("Failed to invoke start()", e);
            }
        }

        public Integer checkForTransition() {
            try {
                return (Integer) checkMethod.invoke(mlInstance);
            } catch (Exception e) {
                throw new RuntimeException("Failed to invoke checkForTransition()", e);
            }
        }
    }
}
