package dspl;
import dspl.backend.AssemblyfileGenerator;
import dspl.backend.InstructionProcessor;
import dspl.frontend.*;
import dspl.intermediate.instructions.*;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;


import antlr.RecognitionException;
import antlr.TokenStreamException;
import antlr.collections.AST;
import antlr.debug.misc.ASTFrame;


/**
 * @author dlariviere, vkm
 *
 */
public class dsplc {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Options compilerOptions = new Options(args); 
		
		String inputFile = compilerOptions.inputFile;
		String outputASMFile;
		
		int status = 0;
		
		//if we're going to compile the asm file, then the specified outputfile is the name of the executable, therefore use the inputfile.s as the name 
		if (compilerOptions.compileAsm) {
			//outputFile by default has same name as inputfile with ".s" instead of ".dspl" extension
			outputASMFile = compilerOptions.outputFile.substring(0, compilerOptions.outputFile.lastIndexOf('.')) + ".s"; 
		}
		else {
			outputASMFile = compilerOptions.outputFile; 
		}
		
		System.out.println(inputFile + " / " + outputASMFile); 
		 
		//compile the source program
		compileSourceFile(inputFile, outputASMFile, compilerOptions);
	
		//if actually generating an executable (rather than just outputting assembly) compile the intermediate assembly code into executable
		if (compilerOptions.compileAsm) { 
			compileAsmFile(outputASMFile, compilerOptions.outputFile, compilerOptions); 
		}
	}
	
	
	/**
	 * Responsible for printing the usage parameters of dsplc
	 * to the console. 
	 */
	private static void printUsage() { 
		System.out.println("Usage: dsplc <inputfile>");
		System.out.println("\tExample: dsplc HelloWorld.dspl"); 
	}
	
	/**
	 * 
	 * @param inputFile: name of dspl source file to compile 
	 * @param outputFile: name of output assembly file to generate
	 * @return 0 if sucessfull, -1 if error occured
	 */
	private static int compileSourceFile(String inputFile, String outputFile, Options compileOptions) { 
		System.out.println("Compiling " + inputFile); 
		
		BufferedReader testFile; //.dspl input file
		AssemblyfileGenerator asmGen; //.s assembly file generator
		
		
		//Open input source DSPL file
		try {
			testFile = new BufferedReader(new FileReader(inputFile));
		} catch (FileNotFoundException e1) {
			System.out.println("input source file \"" + inputFile + "\" not found"); 
			return -1; 
		}
		
		//Open output destination ASM file
		try {
			asmGen = new AssemblyfileGenerator(outputFile, true);
		} catch (FileNotFoundException e1) {
			System.out.println("Error opening destination output file \"" + outputFile + "\""); 
			return -1; 
		} 
	    
		//Create Scanner/Parser and parse the input source file
		DSPLLexer myLexer = new DSPLLexer(testFile);
		DSPLParser myParser = new DSPLParser(myLexer);
		try
		{
		    myParser.setASTNodeClass("dspl.frontend.DSPLAST");
		    myParser.program();
		    AST tree = myParser.getAST();

		    SymbolTable sourceSymbolTable; // = new SymbolTable();
		    //sourceSymbolTable.constructTable(tree);
		    
		    //System.out.println(tree.toStringTree());
		    if (compileOptions.showAST) { 
		    	ASTFrame frame = new ASTFrame("AST from " + inputFile, tree);
		    	frame.setVisible(true);
		    }
		    
		    DSPLAstToIntermediateWalker walker = new DSPLAstToIntermediateWalker();
		    walker.setASTNodeClass("dspl.frontend.DSPLAST");
		    InstructionSet programSet = walker.program(tree);
		    Instruction firstInstruction = programSet.first();
		    sourceSymbolTable = walker.getSymbolTable();
		    
		    asmGen.writeSkeletonASM(sourceSymbolTable.getAllVariables());
		    
		    InstructionProcessor ip = new InstructionProcessor(sourceSymbolTable, asmGen);
			ip.processInstruction(firstInstruction);
			
			asmGen.writeAsmInstr("call", "_exit");
			asmGen.closeFile();
			
		    /*
		    TestTreeWalker.parseTree((CommonAST)tree); 
		    System.out.println("Instruction set generated.");
		    */
		}
		catch (RecognitionException e)
		{
		    System.err.println(e.toString());
		    System.exit(2); 
		}
		catch (TokenStreamException e)
		{
		    System.err.println(e.toString());
		    System.exit(2); 
		}
		catch (SemanticAnalysisException se)
		{
			System.err.println(se.toString());
			System.exit(2);
		} 
		catch (SymbolicException e) 
		{
			System.err.println(e.toString());
			System.exit(2);
		}

		//catch (Error e)
		//{
			//System.err.println(e.toString());
			//System.exit(2);
		//}
		
		System.out.println(inputFile + " sucessfully compiled!"); 
		return 0; 
	}
	
	//Command-line invoking code based on http://www.javalobby.org/java/forums/t53333.html
	private static int compileAsmFile(String asmFile, String outputExecutableFile, Options cOps) { 
		try {
			String ls_str;
			System.out.println("Compiling " + asmFile); 
			Process ls_proc = Runtime.getRuntime().exec("gcc -o	" + outputExecutableFile + " " + asmFile + " -lc");
			
			DataInputStream ls_in = new DataInputStream(ls_proc.getInputStream()); 
			
			try {
				while ((ls_str = ls_in.readLine()) != null) {
					System.out.println(ls_str);
				}
			} catch (IOException e) {
				e.printStackTrace(); 
				System.exit(0);
			}
		} catch (IOException e1) {
			System.err.println(e1);
			System.exit(1);
		} 
		
		return 0; 
	}
}

/**
 * Helper class for processing all of the arguements passed to dsplc
 * on the command-line, properly evaluating them, and storing any
 * flags set which effect compilation. 
 * @author David
 *
 */
class Options { 
	public boolean compileAsm = false; //invoked by -c
	public boolean showAST = false; 	//invoked by -t
	public String inputFile = null;
	public String outputFile = null; 
	
	public Options(String args[]) { 
		//must contain at least two strings, the source dspl file, and the output file (either .s or perhaps a binary)
		if (args.length<2) { 
			printUsage(); 
			throw new Error("Error: invalid # of parameters"); 
		}

		int i;
		for (i=0; i<args.length-2; i++) {
			if (args[i].compareTo("-c")==0) { 
				compileAsm = true; 
			}
			else if (args[i].compareTo("-t")==0) {
				showAST = true; 
			}
			else if (args[i].compareTo("-o2e")==0) {
				System.setOut(System.err); 
			}
			else { 
				printUsage(); 
				throw new Error("invalid args");
			}
		}
		
		//last two arguments are the input and output files
		inputFile = args[i];
		outputFile = args[++i]; 
	}
	
	/**
	 * Print the usage of dsplc
	 *
	 */
	public void printUsage() { 
		System.out.println("Usage: java dspl.dsplc [-c] [-t] inputFile outputFile");
		System.out.println("\t-c: Invoke gcc and generate an executable program (named outputFile)");
		System.out.println("\t-t: Display the parsed AST frame"); 
		System.out.println("\t-o2e: Redirect all standard output to standard err"); 
		System.out.println("inputFile: the source (.dspl) file to be compiled"); 
		System.out.println("outputFile: compiled output file, type depends on other flags set, by default will be assembly output"); 
	}
}
