//Mike Maxim
//Main compiler driver class

package edu.cmu.cs.l3.main;

import edu.cmu.cs.l3.general.*;
import java.util.*;
import java.io.*;
import edu.cmu.cs.l3.parse.*;
import edu.cmu.cs.l3.translate.*;
import edu.cmu.cs.l3.codegen.*;
import edu.cmu.cs.l3.regalloc.*;

public class CompilerDriver {

    public CompilerDriver() {
        m_regoptions = new RegisterAllocatorOptions();
    }

    private static final int ABSYN=1, IR=2, ASSEM = 3, COMPILE=4;

    private static String getRootName(String filename) {
        int dotIndex = filename.lastIndexOf('.');
        return filename.substring(0,dotIndex);
    }

    private static boolean checkFileExistence(String filename) {
        File file = new File(filename);
        return file.exists();
    }

    private static void usage(GetOpt opts) {
        System.out.println("Usage: compile [OPTION...] <SOURCEFILE>");
        System.out.println("Where OPTION is:");
        System.out.print(opts.getUsageHelp());
        Globals.shutdown(-1);
    }

    private String preprocess(String file) {

        String cfile=null,ppline;

        //Check to make sure the file is for real
        if (!checkFileExistence(file)) {
            System.out.println("MAIN: Unable to find file: " + file);
            Globals.shutdown(-1);
        }

        //Run the preprocessor
        Runtime rt = Runtime.getRuntime();
        try {
            cfile = File.createTempFile("__l3_tmp", null).getAbsolutePath();
        } catch (Exception e) {
            System.out.println("MAIN: Unable to create a temporary file, failure");
            Globals.shutdown(-1);
        }

        ppline = "cpp -E -C -o " + cfile + " " + file;
        try {
            rt.exec(ppline).waitFor();
        } catch (Exception e) {
            System.out.println("MAIN: Unable to run pre-processor, check installation");
            System.out.println("MAIN: Shutting Down...");
            Globals.shutdown(-1);
        }

        return cfile;
    }

    private void processCmdLine(String[] args) {

        GetOpt opts = new GetOpt();
        String mode;
        int mopt,vopt, outopt,tyopt,caopt,cygopt;
        int iropt,optopt, spillopt;

        mopt = opts.regOption("mode", "m", "mode (interp,absyn,ir,assem,compile(default))", true, false);
        vopt = opts.regOption("verbose", "v", "verbose mode", false, false);
        outopt = opts.regOption("output", "o", "assembler output file", true, false);
        tyopt = opts.regOption("types", "t", "display typing info in ABSYN mode", false, false);
        caopt = opts.regOption("nocanon", "NC", "don't canonicalize IR printing", false, false);
        cygopt = opts.regOption("windows", "win", "linking with Cygwin/MinGW", false, false);
        iropt = opts.regOption("runtimeptr", "rp", "runtime lib pointer support", false, false);
        optopt = opts.regOption("optlevel", "O", "optimization level", true, false);
        spillopt = opts.regOption("dumbcg", "dcg", "generate spill instructions in code generator", false, false);

        try {
            opts.procCommandLine(args, 1);
        } catch (Exception e) {
            usage(opts);
        }

        m_regoptions.m_genspills = !opts.getOption(spillopt);

        m_inputfile = opts.getNonOptionArguments();

        if (opts.getOption(outopt)) {
            m_outputfile = opts.getOptionData(outopt);
        }
        else {
            m_outputfile = getRootName(m_inputfile)+".s";
        }

        if (opts.getOption(vopt)) {
            Globals.verboseCompilation = true;
        }

        if (opts.getOption(mopt)) {
            mode = opts.getOptionData(mopt);
            if (mode.equals("absyn"))
                m_mode = ABSYN;
            else if (mode.equals("ir"))
                m_mode = IR;
            else if (mode.equals("compile"))
                m_mode = COMPILE;
            else if (mode.equals("assem"))
                m_mode = ASSEM;
            else {
                System.out.println("MAIN: Illegal Mode, see usage help.");
                usage(opts);
            }
        }
        else {
            m_mode = COMPILE;
        }

        m_ty = opts.getOption(tyopt);
        m_canon = !opts.getOption(caopt);

        Globals.underCygwin = opts.getOption(cygopt);

        if (opts.getOption(iropt))
            Translate.m_memTranMode = Translate.RTMEM;
        else
            Translate.m_memTranMode = Translate.IRMEM;

        if (opts.getOption(optopt)) {
            try {
                m_opt = Integer.parseInt(opts.getOptionData(optopt));
            } catch (Exception e) { System.out.println("Invalid optmization level"); usage(opts); }
        }
        else
            m_opt = 1;
    }

    private void go(String[] args) throws Exception {

        String ifile;

        processCmdLine(args);
        ifile = preprocess(m_inputfile);

        switch (m_mode) {
            case ABSYN:
                ParseMain.go(ifile, m_inputfile, m_ty);
                break;
            case IR:
                TranslateMain.go(ifile, m_canon);
                break;
            case ASSEM:
                CodegenMain.go(ifile, m_regoptions);
                break;
            case COMPILE:
                switch (m_opt) {
                    case 0:
                        m_regoptions.m_coalesce = false;
                        Compiler.go(ifile, m_outputfile, false, m_regoptions);
                        break;
                    case 1:
                        m_regoptions.m_coalesce = false;
                        Compiler.go(ifile, m_outputfile, true, m_regoptions);
                        break;
                    case 2:
                        m_regoptions.m_coalesce = true;
                        Compiler.go(ifile, m_outputfile, true, m_regoptions);
                        break;
                }
                break;
        }
    }

    public static void main(String[] args) throws Exception {
        (new CompilerDriver()).go(args);
    }

    private String m_inputfile, m_outputfile;
    private RegisterAllocatorOptions m_regoptions;
    private int m_mode, m_opt;
    private boolean m_ty, m_canon = true;
}