package mjc;

import java.io.PushbackReader;
import java.io.FileReader;
import java.util.Comparator;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;

import mjc.lexer.Lexer;
import mjc.parser.Parser;
import mjc.symbol.SymbolTable;
import mjc.symbol.SymbolTableBuilder;
import mjc.node.Start;
import mjc.analysis.ASTGraphPrinter;
import mjc.analysis.ASTPrinter;
import mjc.analysis.TypeChecker;
import mjc.error.MiniJavaError;

public class ARMMain {
    private final ASTPrinter astPrinter = new ASTPrinter();
    private final ASTGraphPrinter graphPrinter = new ASTGraphPrinter();
    private final CommandLineParser commandLineParser = new GnuParser();
    private final HelpFormatter helpFormatter = new HelpFormatter();
    private final Options options = new Options();

    private final static int EXIT_SUCCESS = 0;
    private final static int EXIT_FAILURE = 1;

    public ARMMain() {
        helpFormatter.setOptionComparator(new OptionComarator<Option>());
    }

    public static void main(String[] args) {
        ARMMain program = new ARMMain();
        try {
            System.exit(program.run(args));
        } catch (Exception e) {
            System.err.println(e.getMessage());
            System.exit(EXIT_FAILURE);
        }
    }

    private int run(String[] args) throws Exception {
        options.addOption("S", false, "output assembler code");
        options.addOption("o", true, "output file");
        options.addOption("p", false, "print abstract syntax tree");
        options.addOption("g", false, "print abstract syntax tree in GraphViz format");
        options.addOption("s", false, "print symbol table");
        options.addOption("h", false, "show help message");

        final CommandLine commandLine = commandLineParser.parse(options, args);

        if (commandLine.hasOption("h")) {
            helpFormatter.printHelp("mjc <infile> [options]", options);
            System.exit(EXIT_SUCCESS);
        }

        if (commandLine.getArgs().length != 1) {
            helpFormatter.printHelp("mjc <infile> [options]", options);
            System.exit(EXIT_FAILURE);
        }

        /******************************
         * Stage 1: Lexing + Parsing. *
         *****************************/

        final String fileName = commandLine.getArgs()[0];
        final PushbackReader reader = new PushbackReader(new FileReader(fileName));
        final Parser parser = new Parser(new Lexer(reader));
        final Start tree = parser.parse();

        if (commandLine.hasOption("p"))
            astPrinter.print(tree);

        if (commandLine.hasOption("g"))
            graphPrinter.print(tree);

        /*******************************
         * Stage 2: Semantic Analysis. *
         *******************************/

        // Build symbol table.
        final SymbolTableBuilder builder = new SymbolTableBuilder();
        final SymbolTable symbolTable = builder.build(tree);
        if (builder.hasErrors()) {
            for (MiniJavaError error : builder.getErrors()) {
                System.err.println(error);
            }
        }

        if (commandLine.hasOption("s"))
            System.out.println(symbolTable);

        // Run type-check.
        final TypeChecker typeChecker = new TypeChecker();
        if (!typeChecker.check(tree, symbolTable)) {
            for (MiniJavaError error : typeChecker.getErrors()) {
                System.err.println(error);
            }
        }


        if (builder.hasErrors() || typeChecker.hasErrors()) {
            // Errors in symbol table building or type checking, abort.
            return EXIT_FAILURE;
        }

        return EXIT_SUCCESS;
    }

    // Comparator for Options, to get them in the order we want in help output.
    class OptionComarator<T extends Option> implements Comparator<T> {
        private static final String ORDER = "Sopgsh";

        @Override
        public int compare(T option1, T option2) {
            return ORDER.indexOf(option1.getOpt()) - ORDER.indexOf(option2.getOpt());
        }
    }
}

@@ -7,16 +7,44 @@
 e mjc;%0A%0A
+import java.io.IOException;%0A
 import j
@@ -382,57 +382,171 @@
 ns;%0A
-%0Aimport mjc.lexer.Lexer;%0Aimport mjc.parser.Parser
+import org.apache.commons.cli.ParseException;%0A%0Aimport mjc.lexer.Lexer;%0Aimport mjc.lexer.LexerException;%0Aimport mjc.parser.Parser;%0Aimport mjc.parser.ParserException
 ;%0Aim
@@ -1233,24 +1233,420 @@
 ARMMain() %7B%0A
+        options.addOption(%22S%22, false, %22output assembler code%22);%0A        options.addOption(%22o%22, true, %22output file%22);%0A        options.addOption(%22p%22, false, %22print abstract syntax tree%22);%0A        options.addOption(%22g%22, false, %22print abstract syntax tree in GraphViz format%22);%0A        options.addOption(%22s%22, false, %22print symbol table%22);%0A        options.addOption(%22h%22, false, %22show help message%22);%0A%0A
         help
@@ -1684,24 +1684,25 @@
 ew OptionCom
+p
 arator%3COptio
@@ -2003,452 +2003,554 @@
     
-private int run(String%5B%5D args) throws Exception %7B%0A        options.addOption(%22S%22, false, %22output assembler code%22);
+/**%0A     * Run compiler with the given command line arguments.%0A     *%0A     * @param args Command line arguments.%0A     * @return EXIT_SUCCESS if compilation succeeded, otherwise EXIT_FAILURE.
 %0A     
+*%0A
    
-options.addOption(%22o%22, true, %22output
+  * @throws ParseException if parsing of command line arguments
  f
+a
 ile
-%22);
+d.
 %0A     
-   options.addOption(%22p%22, false, %22print abstract syntax tree%22);%0A        options.addOption(%22g%22, false, %22print abstract syntax tree in GraphViz format%22);%0A        options.addOption(%22s%22, false, %22print symbol table%22);%0A        options.addOption(%22h%22, false, %22show help message%22);
+* @throws IOException if an I/O error occurred.%0A     * @throws LexerException if lexical analysis failed.%0A     * @throws ParserException if parsing failed.%0A     */%0A    private int run(String%5B%5D args) throws ParseException, ParserException, LexerException, IOException %7B
 %0A%0A  
@@ -4611,16 +4611,17 @@
 ptionCom
+p
 arator%3CT

Tidy up a little.