import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.stringtemplate.*;
import org.antlr.runtime.Token.*;

import java.nio.charset.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.io.BufferedWriter;
import java.io.IOException;

import java.util.Queue;
import java.util.LinkedList;


public class Tiger {

    private static void bfs(CommonTree root){
        Queue q = new LinkedList();
        q.add(root);

        while(q.peek() != null){
            CommonTree node = (CommonTree)q.remove();
            System.out.println("Node: "+ node.getText());
            if (node.getChildren() != null) {
                for(Object t: node.getChildren()){
                    if(t != null)
                        q.add((CommonTree)t);
                } 
            }
        }
    }

    private static String readFile(String path, Charset encoding) throws Exception {
        byte[] encoded = Files.readAllBytes(Paths.get(path));
        return new String(encoded, encoding);
    }

    private static void writeDotFile(String fileName, String outputString, Charset encoding) throws Exception {
        Path file = Paths.get(fileName);

        try (BufferedWriter writer = Files.newBufferedWriter(file, encoding)) {
            writer.write(outputString, 0, outputString.length());
        } catch (IOException x) {
            System.err.format("IOException: %s%n", x);
        }
    }

    public static void main(String[] args) {
        try {
            String[] file_tokens = args[0].split("\\.(?=[^\\.]+$)");
            String src = readFile(args[0], StandardCharsets.UTF_8);
            TigerLexer lexer = new TigerLexer(new ANTLRStringStream(src));
            TigerParser parser = new TigerParser(new CommonTokenStream(lexer));
            
            /*To print lexer symbols of the program*/
            TigerLexer lexer1 = new TigerLexer(new ANTLRStringStream(src));
            CommonTokenStream cts = new CommonTokenStream(lexer1);
            
            int i = 1;
            Token to = cts.LT(i);
            while(to.getType() != -1){
                System.out.print(TigerParser.tokenNames[to.getType()] + " ");
                if(to.getType() == -1) break;
                to = cts.LT(++i);
            }
            
           System.out.println();

            CommonTree tree = (CommonTree)(parser.tiger_program().getTree());
            bfs(tree);
            
            DOTTreeGenerator gen = new DOTTreeGenerator();
            StringTemplate st = gen.toDOT(tree);
            
            writeDotFile(args[1], st.toString(), StandardCharsets.UTF_8);
    
            if(parser.getErrors().isEmpty() && lexer.getErrors().isEmpty()){
                System.out.println("***Successful Parse***");
            }
            else{
                if(!parser.getErrors().isEmpty()){
                    System.out.println("*** Parser Errors ***"); 
                    for(String s : parser.getErrors()) System.out.println(s);
                }

                if(!lexer.getErrors().isEmpty()){
                    System.out.println("*** Lexer Errors ***"); 
                    for(String s : lexer.getErrors()) System.out.println(s);
                }
            }
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

@@ -351,30 +351,1519 @@
 st;%0A
-%0A%0Apublic class Tiger %7B
+import java.util.Hashtable;%0Aimport java.util.Collections;%0Aimport java.util.List;%0A%0Apublic class Tiger %7B%0A%0A    private static class Args%7B%0A        String name;%0A        String type;%0A%0A        public Args(String name, String type)%7B%0A            this.name = name;%0A            this.type = type;%0A        %7D%0A    %7D%0A%0A    private static  class Symbol%7B%0A        String name;%0A        String type;%0A        String declaringProc;//none for global variables%0A        int lexicalLevel;%0A        int func_num_args;//only applicable for functions%0A        List%3CArgs%3E arg_list;//only applicable for functions%0A        %0A        Symbol next;%0A%0A        public Symbol(String name, String type, String declaringProc, %0A                      int lexicalLevel, int func_num_args, List%3CArgs%3E arg_list)%7B%0A            this.name = name;%0A            this.type = type;%0A            this.declaringProc = declaringProc;%0A            this.lexicalLevel = lexicalLevel;%0A            this.func_num_args = func_num_args;%0A            this.arg_list = arg_list;%0A%0A            //is null for the first identifier occurence%0A            this.next = null;%0A%0A        %7D %0A            %0A        public String toString()%7B%0A            return name + %22 %22 + type + %22 %22 + declaringProc + %22 %22 %0A                   + lexicalLevel + %22 %22 + func_num_args + %22 %22 + arg_list.size();%0A        %7D%0A    %7D%0A%0A    private static Hashtable%3CString, Symbol%3E ht = new Hashtable%3CString, Symbol%3E();%0A    private static void printSymbolTable(Hashtable%3CString, Symbol%3E ht)%7B%0A        System.out.println(ht); %0A    %7D%0A    
 %0A%0A  
@@ -3905,24 +3905,46 @@
 getTree());%0A
+            //run bfs%0A
             
@@ -3958,32 +3958,309 @@
 e);%0A            
+//print sample symbol table%0A            System.out.println(%22Sample Hash Table%22);%0A            ht.put(%22test%22, new Symbol(%22test_name%22, %22test_type%22, %22test_proc%22,%0A                                      0, 0, Collections.EMPTY_LIST)%0A            ); %0A            printSymbolTable(ht);%0A%0A
 %0A            DOT

add Symboltable ds