package net.ranides.assira.grammar;


import net.ranides.assira.grammar.impl.ParserFactory;
import net.ranides.assira.io.ResourceUtils;
import net.ranides.assira.reflection.IClass;
import net.ranides.assira.text.Charsets;
import net.ranides.assira.text.IOStrings;
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.Parser;

import java.io.File;
import java.io.IOException;

public interface G4Parser {

    G4Node parse(String rule, String text);

    boolean validateRuleName(String ruleName);

    boolean validateTokenName(String ruleName);

    String ruleName(int ruleIndex);

    String ruleName(Class<?> ruleType);

    String tokenName(int tokenIndex);

    G4ParserCoverage rules();

    static G4ParserBuilder builder() {
        return new G4ParserBuilder();
    }

    class G4ParserBuilder {

        private String source;

        private String imports;

        private IClass<? extends Lexer> lexer;

        private IClass<? extends Parser> parser;

        private G4ErrorHandler handler = (m,e) -> {
            throw new G4Exception(m,e);
        };

        public G4ParserBuilder resource(String source) {
            try {
                return source(ResourceUtils.text(source));
            } catch (IOException e) {
                throw new G4Exception(e);
            }
        }

        public G4ParserBuilder source(String source) {
            this.source = source;
            return this;
        }

        public G4ParserBuilder sourceFile(String source) {
            try {
                return source(IOStrings.read(new File(source), Charsets.UTF8));
            } catch (IOException e) {
                throw new G4Exception(e);
            }
        }

        public G4ParserBuilder imports(String imports) {
            this.imports = imports;
            return this;
        }

        public G4ParserBuilder lexer(Class<? extends Lexer> lexer) {
            return lexer(IClass.typeinfo(lexer));
        }

        public G4ParserBuilder lexer(IClass<? extends Lexer> lexer) {
            this.lexer = lexer;
            return this;
        }

        public G4ParserBuilder parser(Class<? extends Parser> parser) {
            return parser(IClass.typeinfo(parser));
        }

        public G4ParserBuilder parser(IClass<? extends Parser> parser) {
            this.parser = parser;
            return this;
        }

        public G4ParserBuilder handler(G4ErrorHandler handler) {
            this.handler = handler;
            return this;
        }

        public G4Parser build() {
            if((parser == null) != (lexer == null)) {
                throw new G4Exception("You need to define both generated lexer and parser");
            }
            if((parser==null) == (source==null) ) {
                throw new G4Exception("You need to define only generated or interpreted parser at once");
            }
            if(parser!=null) {
                if(imports!=null) {
                    throw new G4Exception("Generated parsers do not need imports directory");
                }
                return ParserFactory.newInstance(lexer, parser, handler);
            } else {
                return ParserFactory.newInstance(source, imports, handler);
            }
        }
    }

}
