package xtc.lang.blink.agent;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.HashMap;

/**
 * A hard coded Java class generator. This tool takes Java class files, and
 * generates a C source file and optionally a C header file that define a
 * character arrays that contain the input class files.
 *
 * @author Byeongcheol Lee
 */
public class GenerateHardcodedJavaClass {

  /**
   * Print the reason why this program can not accept a user command line, and
   * show the command line usage.
   *
   * @param reason The reason.
   */
  public static void usage(String reason) {
    if (reason != null) {
      System.out.println(reason);
    }
    System.out.println(
        "usage: " +  GenerateHardcodedJavaClass.class.getName()
        + " [-o <output file>] [-h <output header file]"
        + "[<array name> <input file>] [<array name> <input file>] ...\n"
        + " "
    );
    System.exit(-1);
  }

  /**
   * Parse the user command line arguments and run the application.
   *
   * @param args The arguments.
   */
  public static void main(String[] args) {
    //parse arguments
    String outputSourceFileName = null;
    String outputHeaderFileName = null;
    HashMap<String,String> array2ClassFileMap = new HashMap<String,String>();
    for(int i = 0;i < args.length;i++) {
      if (args[i].equals("-o") ) {
        if ((i + 1) < args.length) {
          outputSourceFileName = args[++i];
        } else {
          usage("Please, specify output file after -o");
        }
      } else if (args[i].equals("-h")) {
        if ((i+1) < args.length) {
          outputHeaderFileName = args[++i];
        } else {
          usage("Please, specify output header file name after -h");
        }
      } else {
        if ((i + 1) < args.length) {
          String arrayName = args[i];
          String inputFileName = args[++i];
          File f = new File(inputFileName);
          if (f.isFile() && f.canRead()) {
            if (array2ClassFileMap.containsKey(arrayName)) {
              usage("Duplicated array name: " + arrayName);
            } else {
              array2ClassFileMap.put(arrayName, inputFileName);
            }
          } else {
            usage("You need a read acess permission: " + inputFileName);
          }
        } else {
          usage("Please, specify input file");
        }
      }
    }
    if (array2ClassFileMap.size() <= 0) {
      usage("Please, specify at least one <array name, input file> pair");
    }

    // run this tool and generates a header file and a source file.
    try {
      // generate header file.
      if ( outputHeaderFileName != null) {
        PrintWriter headerWriter =  new PrintWriter(new FileOutputStream(outputHeaderFileName));
        generateHeaderFile(outputHeaderFileName, array2ClassFileMap, headerWriter);
        headerWriter.flush();
        headerWriter.close();
      }

      //generate source file.
      PrintWriter os = new PrintWriter(
          (outputSourceFileName == null) ? System.out : new FileOutputStream(
              outputSourceFileName));
      generateSourceFile(array2ClassFileMap, os);
      os.flush();
      os.close();

    } catch (IOException e) {
      System.err.println("Can not recover from the input or output fault");
    }
  }

  /**
   * Generate header file declaring a set of character arrays for the Java class
   * files.
   *
   * @param headerFile The header file name.
   * @param map The map.
   * @param os The output stream.
   */
  private static void generateHeaderFile(String headerFile,
    HashMap<String,String> map, PrintWriter os) throws IOException {
    String symbolName = "_" + headerFile.toUpperCase().replace('.', '_') + "_";
    os.println("#ifndef " + symbolName);
    os.println("#define " + symbolName);
    for(final String arrayName : map.keySet()) {
      String inputFileName = map.get(arrayName);
      File inputFile = new File(inputFileName);
      long length = inputFile.length();
      os.printf("extern const signed char %s[%d];\n", arrayName, length);
    }
    os.println("#endif /* " + symbolName + " */");
  }

  /**
   * Read Java class files and generate C source lines.
   *
   * @param map The map.
   * @param os The output.
   */
  private static void generateSourceFile(HashMap<String,String> map,
    PrintWriter os) throws IOException {
    os.write("/* This file is generated by "
        + GenerateHardcodedJavaClass.class.getName() + ". */\n");
    for(final String arrayName : map.keySet()) {
      String inputFileName = map.get(arrayName);
      long length = new File(inputFileName).length();
      InputStream is = new FileInputStream(inputFileName);
      BufferedInputStream bis = new BufferedInputStream(is);
      os.write("/* This array is from " + inputFileName + ". */\n");
      os.printf("const char signed %s[%d] = {\n", arrayName, length);
      int c, i=1;
      while ((c = is.read()) >= 0) {
        os.printf("0x%02x, ", c);
        if (i % 8 == 0) {os.write("\n");}
        i++;
      }
      os.write("\n");
      os.write("};\n");
      bis.close();
    }
  }
}
