package pp.s1184725.boppi;

import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeListener;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.apache.commons.lang3.tuple.Pair;

public class BasicParserHelper {
	public static Pair<BasicParser,List<LogRecord>> getParserWithLog(Path file, Logger logger) throws IOException {
		return Pair.of(getParser(CharStreams.fromFileName(file.toString()), logger), makeListLog(logger));
	}

	public static Pair<BasicParser,List<LogRecord>> getParserWithLog(String code, Logger logger) {
		return Pair.of(getParser(CharStreams.fromString(code), logger), makeListLog(logger));
	}

	public static BasicParser getParser(CharStream stream, Logger logger) {
		BasicLexer lexer = new BasicLexer(stream);
		lexer.removeErrorListeners();
		lexer.addErrorListener(new BaseErrorListener() {
			@Override
			public void syntaxError(Recognizer<?, ?> r, Object oSym, int l, int c, String msg, RecognitionException e) {
				logger.severe(""+l+":"+c+" "+msg);
			}
		});

		BasicParser parser = new BasicParser(new CommonTokenStream(lexer));
		parser.removeErrorListeners();
		parser.addErrorListener(new BaseErrorListener() {
			@Override
			public void syntaxError(Recognizer<?, ?> r, Object oSym, int l, int c, String msg, RecognitionException e) {
				logger.severe(""+l+":"+c+" "+msg);
			}
		});

		return parser;
	}

	public static List<LogRecord> makeListLog(Logger logger) {
		List<LogRecord> log = new ArrayList<LogRecord>();
		Handler listLogger = new Handler() {
		    @Override
		    public void close() throws SecurityException {}
		    
		    @Override
		    public void flush() {}
		    
		    @Override
		    public void publish(LogRecord record) {
		    	log.add(record);
		    }
		};

		for (Handler handler : logger.getHandlers())
        	logger.removeHandler(handler);

        logger.setUseParentHandlers(false);
        logger.addHandler(listLogger);
        
        return log;
	}
	
	public static String getAnnotatedDOT(ParseTree tree, Annotations annotater) {
		StringBuilder sb = new StringBuilder();
		sb.append("digraph {\n");
		
		new ParseTreeWalker().walk(new ParseTreeListener() {
			private String escape(String str) {
				return str.replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
			}
			
			@Override
			public void visitTerminal(TerminalNode node) {
				sb.append("\tn"+node.hashCode()+"[label=<"+escape(node.getText())+">;shape=rect]\n");
			}
			
			@Override
			public void visitErrorNode(ErrorNode node) {
				sb.append("\tn"+node.hashCode()+"[label=<"+escape(node.getText())+">;style=filled;fillcolor=red]\n");
			}
			
			@Override
			public void exitEveryRule(ParserRuleContext ctx) {
				float hue = (ctx.getClass().hashCode() % 65521)/65521.0f;

				sb.append("\tn"+ctx.hashCode()+"[label=<");
				sb.append("rule: "+escape(ctx.getClass().getSimpleName())+"<br/>");
				if (annotater.registers.get(ctx) != null)
					sb.append("reg: <b>"+escape(annotater.registers.get(ctx).getName())+"</b><br/>");
				if (annotater.variables.get(ctx) != null)
					sb.append("var: "+escape(annotater.variables.get(ctx).toString())+"<br/>");
				if (annotater.types.get(ctx) != null)
					sb.append("type: "+escape(annotater.types.get(ctx).name())+"<br/>");
				sb.append(">;style=filled;fillcolor=\""+hue+"+0.1+1\"]\n");

				if (ctx.children != null)
					for (ParseTree child : ctx.children)
						sb.append("\tn"+ctx.hashCode()+" -> n"+child.hashCode()+"\n");
			}
			
			@Override
			public void enterEveryRule(ParserRuleContext ctx) {}
		}, tree);
		sb.append("}\n");
		
		return sb.toString();
	}

}
