/* ---------------------------------------------------------------------
%   Copyright (C) 2007 Association for the COINS Compiler Infrastructure
%       (Read COPYING for detailed information.)
--------------------------------------------------------------------- */
package coins.drivergen;

import coins.drivergen.process.ProcessManager;

import java.io.File;
import java.io.FileWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Properties;
import java.util.List;
import java.text.ParseException;

/**
 * A driver generator for the COINS Compiler Driver API.
 * The Generator generate a new driver which is one of the the COINS Compiler.
 * the new driver class is created as the subclass of Driver class, and
 * the Generator generate its Compile method in order to the input file which
 * orders the compile processes.
 **/
public class Generator {
  /**
   * File name of new driver
   */
  protected static final String defaultDriverName = "MyDriver.java";
  /**
   * FileWriter for new driver
   */
  protected static FileWriter out;

  /**
   * The header part of the new driver.
   */
  protected static final String[] header = {
    "package coins.mydriver;",
    " ",
    "import coins.IoRoot;",
    "import coins.SymRoot;",
    "import coins.HirRoot;",
    "import coins.FlowRoot;",
    "import coins.driver.Driver;",
    "import coins.driver.Suffix;",
    "import coins.driver.CoinsOptions;",
    "import coins.driver.CommandLine;",
    "import coins.driver.CompileSpecification;",
    "import coins.driver.PassStopException;",
    "import coins.driver.StreamCopier;",
    "import coins.driver.Trace;",
    "import coins.FatalError;",
    "import coins.PassException;",
    " ",
    "import java.io.File;",
    "import java.io.InputStream;",
    "import java.io.InputStreamReader;",
    "import java.io.IOException;",
    "import java.io.ObjectInputStream;",
    "import java.io.ObjectOutputStream;",
    "import java.io.OutputStream;",
    "import java.io.OutputStreamWriter;",
    "import java.io.PrintStream;",
    "import java.io.PrintWriter;",
    "import java.util.ArrayList;",
    "import java.util.Iterator;",
    "import java.util.Properties;",
    "import java.util.List;",
    " ",
    "import coins.drivergen.Conditions;",
    "import coins.drivergen.InputIr;",
    "import coins.drivergen.Options;",
    "import coins.drivergen.process.ProcessManager;",
    "import coins.drivergen.ProcessException;",
    " ",
    "/**",
    " * A driver implementation using the COINS Compiler Driver API,",
    " * generated by the Generator with the Process Order File.",
    " **/",
    };

  /**
   * class declaration line
   */
  protected static final String[] classDecl = {
    "public class ",
    //"MyDriver",
    " extends Driver {"
  };

  /**
   * compile method header part
   */
  protected static final String[] compileHeader = {
    "/**",
    " * Compiler",
    " **/",
    "public void compile(File sourceFile,",
    "                  Suffix suffix,",
    "                  InputStream in,",
    "                  OutputStream out,",
    "                  IoRoot io)",
    "    throws IOException, PassException {",
    "",
    "    InputIr inputIr = new InputIr(io);",
    "    Options options = new Options(sourceFile, suffix, in, out, io);",
    "    int     condition = Conditions.SOURCE;",
    "    ProcessManager manager = new ProcessManager();",
    "",
    "    manager.init(inputIr, options);",
    "",
    "    try {",
    "      manager.go(new String[] {"
  };

  /**
   * compile method footer part
   */
  protected static final String[] compileFooter = {
    "      });",
    "    } catch (PassException e) {",
    "      System.err.println(\"PassException :\");",
    "      System.err.println(e);",
    "    } catch (Exception e) {",
    "      System.err.println(e);",
    "    }",
    "  }"
  };
    
  /**
   * The footer part of the new driver (1/3).
   */
  protected static final String[] footer1 = {
    "  /**",
    "   * A main function to invoke a driver instance.",
    "   *",
    "   * @param args a command line.",
    "   **/",
    "  public static void main(String[] args) {"
  };
  /**
   * The footer part of the new driver (2/3).
   */
  protected static final String[] footer2 = {
    "    new ",
    "().go(args);"
  };
  /**
   * The footer part of the new driver (3/3).
   */
  protected static final String[] footer3 = {
    "  }",
    "}"
  };

  /**
   *
   * A name in trace messages.  Derived classes can override it.
   **/
  protected String myName = "Generator";

  /**
   *
   * Output the contents of compile methos part of the mydriver.java
   * @param orderFile input file name
   *
   */
  protected void generateCompile(File orderFile) throws FileNotFoundException,IOException {

    /* write the header part of method:compile() */
    for (int i=0; i < compileHeader.length; ++i) {
      out.write(compileHeader[i]+"\n");
    }

    /*
      -> create InputIR
      -> create Options
      -> create Condition (what is the initial condition??)
    */
    /* read the order file, and output the process name */
    BufferedReader br = new BufferedReader(new FileReader(orderFile));

    ProcessManager manager = new ProcessManager();

    /* Loop ( with orderFile) */
    String line0;
    String line;
    String indent1 = new String("      ");
    String indent2 = new String("        ");
    int counter = 0;

    while ((line0 = br.readLine()) != null) {
      line = line0.trim();
      if (!(line.startsWith("//") || line.length()==0)) {
        if (counter != 0) {
          out.write(",\n");
        }
        if (manager.getProcess(line) == null) {
          System.err.println("\nProcess "+line+" is not found");
          System.err.println("    Faild to make MyDriver.java");
          System.exit(1);
        }
        /* write the process */
        out.write(indent2+"\""+line+"\"");
        ++counter;
      }
    }
    out.write("\n");

    br.close();

    /* write the footer part of method:compile() */
    for (int i=0; i < compileFooter.length; ++i) {
      out.write(compileFooter[i]+"\n");
    }

  }

  /**
   *
   * Output the contents of mydriver.java
   * @param args input file name
   *
   */
  protected void go(String[] args) throws FileNotFoundException,IOException {
    String driverName;
    File output;

    if (args.length > 1) {
      output = new File(args[1]);
    } else {
      output = new File(defaultDriverName);
    }
    if (output == null) {
      System.err.println("Failed to open "+args[1]);
      System.exit(1);
    }

    String tmp = output.getName();
    driverName = tmp.substring(0,tmp.lastIndexOf(".java"));

    File orderFile = new File(args[0]);
    if (orderFile.canRead()) {
      String line;

      out = new FileWriter(output);

      for (int i=0; i < header.length; ++i) {
        out.write(header[i]+"\n");
      }
      out.write(classDecl[0] + driverName + classDecl[1] + "\n");

      out.write("/*** Process order file [ "+args[0]+" ] ***/\n");
      generateCompile(orderFile);

      for (int i=0; i < footer1.length; ++i) {
        out.write(footer1[i]+"\n");
      }
      out.write(footer2[0] + driverName + footer2[1] + "\n");
      for (int i=0; i < footer3.length; ++i) {
        out.write(footer3[i]+"\n");
      }

      out.flush();
      out.close();

    } else {
      System.err.println("Process order file : "+args[0]+" is not readable");
      System.exit(1);
    }
  }

  /**
   * A main function to generate a driver program file.
   *
   * @param args a order file which order the processes of the driver.
   **/
  public static void main(String[] args) {
    if (args.length < 1) {
      System.err.println("Usage: processes order filename");
      System.exit(1);
    } else {
      try {
        new Generator().go(args);
      } catch (Exception e) {
        System.err.println(e.getMessage());
        System.exit(1);
      }
      System.exit(0);
    }
  }
}
