/**
 *
 */
package ca.uwaterloo.joos;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import ca.uwaterloo.joos.ast.AST;
import ca.uwaterloo.joos.ast.visitor.ToStringVisitor;
import ca.uwaterloo.joos.checker.HierarchyBuilder;
import ca.uwaterloo.joos.checker.HierarchyChecker;
import ca.uwaterloo.joos.parser.LR1;
import ca.uwaterloo.joos.parser.LR1Parser;
import ca.uwaterloo.joos.parser.ParseTree;
import ca.uwaterloo.joos.scanner.DFA;
import ca.uwaterloo.joos.scanner.Scanner;
import ca.uwaterloo.joos.scanner.Token;
import ca.uwaterloo.joos.symboltable.NameLinker;
import ca.uwaterloo.joos.symboltable.SymbolTable;
import ca.uwaterloo.joos.symboltable.TypeLinker;
import ca.uwaterloo.joos.weeder.Weeder;

/**
 * @author Greg Wang
 *
 */
public class Main {
	private static final Logger logger = Main.getLogger(Main.class);

	public static Logger getLogger(Class<?> cls) {
		return Logger.getLogger(cls.getSimpleName());
	}

	private final Scanner scanner;
	private final LR1Parser parser;
	private final Preprocessor preprocessor;
	private final Weeder weeder;
	
	public Main() {
		// Construct Preprocessor
		this.preprocessor = new Preprocessor();

		// Read a DFA from file
		DFA dfa = null;
		try {
			dfa = new DFA(new File("resources/joos.dfa"));
		} catch (Exception e) {
			System.err.println("ERROR: Invalid DFA File format: " + e.getLocalizedMessage() + " " + e.getClass().getName());
			System.exit(-11);
		}
		// Construct Scanner
		this.scanner = new Scanner(dfa);

		// Read a LR1 from file
		LR1 lr1 = null;
		try {
			lr1 = new LR1(new File("resources/joos.lr1"));
		} catch (Exception e) {
			System.err.println("ERROR: Invalid LR1 File format: " + e.getLocalizedMessage() + " " + e.getClass().getName());
			e.printStackTrace();
			System.exit(-12);
		}
		// Construct Parser
		this.parser = new LR1Parser(lr1);
		
		this.weeder = new Weeder();
	}

	public AST constructAst(File source) throws Exception {
		logger.info("Processing: " + source.getName());

		/* Scanning */
		// Construct a Scanner which use the DFA
		List<Token> tokens = null;

		// Scan the source codes into tokens
		tokens = this.scanner.fileToTokens(source);

		// Preprocess the tokens
		tokens = preprocessor.processTokens(tokens);

		/* Parsing */
		ParseTree parseTree = null;
		parseTree = this.parser.parseTokens(tokens);
		
//		if(!source.getPath().contains("stdlib")) {
//		System.out.println(parseTree);
//		}
		
		/* AST Constructing */
		AST ast = new AST(parseTree, source.getName());
		
//		if(!source.getPath().contains("stdlib")) {
//		ToStringVisitor visitor = new ToStringVisitor();
//		ast.getRoot().accept(visitor);
//		System.out.println(visitor.getString());
//		}
		
		/* AST Weeding */
		this.weeder.weedAst(ast);
		return ast;
	}
	
	public SymbolTable typeLinking(List<AST> asts) throws Exception {
		SymbolTable table = new SymbolTable();
		
		table.build(asts);
//		table.listScopes();
		
		HierarchyBuilder hierarchyBuilder = new HierarchyBuilder();
		
		for(AST ast: asts) {
			TypeLinker linker = new TypeLinker(table);
			ast.getRoot().accept(linker);
			ast.getRoot().accept(hierarchyBuilder);
		}
		
//		table.listScopes();
		
		HierarchyChecker hierarchyChecker = new HierarchyChecker(hierarchyBuilder);
		hierarchyChecker.CheckHierarchy();
		
		return table;
	}
	
	public void nameLinking(List<AST> asts, SymbolTable table) throws Exception {
		NameLinker linker = new NameLinker(table);
		
		for(AST ast: asts) {
			ast.getRoot().accept(linker);
		}
		
//		for(Class<?> c : linker.nameUsageParentClasses) {
//			System.out.println(c.getName());
//		}
	}
	
	public void execute(String[] args) throws Exception {
		List<AST> asts = new ArrayList<AST>();
		for(String arg: args) {
			asts.add(this.constructAst(new File(arg)));
		}
		
		SymbolTable table = typeLinking(asts);
		nameLinking(asts, table);
	}

	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) {
		if(args.length < 1) {
			System.exit(-1);
		}
		
		Main instance = new Main();

		try {
			instance.execute(args);
		} catch (Exception e) {
			System.err.println("ERROR: " + e.getLocalizedMessage() + " " + e.getClass().getName());
			e.printStackTrace();
			System.exit(42);
		}
	}
}

@@ -296,21 +296,21 @@
 ierarchy
-Build
+Check
 er;%0Aimpo
@@ -2983,32 +2983,34 @@
 tScopes();%0A%09%09%0A%09%09
+//
 HierarchyBuilder
@@ -3002,21 +3002,21 @@
 ierarchy
-Build
+Check
 er hiera
@@ -3046,21 +3046,21 @@
 rchy
-Build
+Check
 er();%0A%09%09
 %0A%09%09f
@@ -3051,24 +3051,45 @@
 hecker();%0A%09%09
+//table.listScopes();
 %0A%09%09for(AST a
@@ -3100,16 +3100,84 @@
 asts) %7B%0A
+%09%09%09HierarchyChecker hierarchyChecker = new HierarchyChecker(table);%0A
 %09%09%09TypeL
@@ -3276,29 +3276,29 @@
 pt(hierarchy
-Build
+Check
 er);%0A%09%09%7D%0A%09%09%0A
@@ -3301,113 +3301,21 @@
 %0A%09%09%0A
-//%09%09table.listScopes();%0A%09%09%0A%09%09HierarchyChecker hierarchyChecker = new HierarchyChecker(hierarchyBuilder);%0A
+%09%09%0A//%09%09%0A%09%09%0A//
 %09%09hi

add excute for hierachychecker()