
import java.io.*;
import java.util.zip.ZipFile;
import java.util.zip.ZipEntry;
import java.util.Enumeration;

/**
 * PluginCompiler - DESCRIPTION
 * <p/>
 * An extension to the Processing project - http://Proce55ing.net
 * Much code taken directly from Processing source by Ben Fry.
 * <p/>
 * Copyright (c) 2004 Simon Greenwold
 * Created: Apr 7, 2004
 * <p/>
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * <p/>
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * <p/>
 * You should have received a copy of the GNU Lesser General
 * Public License along with the Processing project; if not,
 * write to the Free Software Foundation, Inc., 59 Temple Place,
 * Suite 330, Boston, MA  02111-1307  USA
 */

public class PluginCompiler {

  PluginCompiler(String pluginName) {
    compile(pluginName);
    jar(pluginName);
  }


  class StreamGobbler extends Thread
  {
    InputStream is;
    String type;

    StreamGobbler(InputStream is, String type)
    {
      this.is = is;
      this.type = type;
    }

    public void run()
    {
      try
      {
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader br = new BufferedReader(isr);
        String line=null;
        while ( (line = br.readLine()) != null)
          System.out.println(type + ">" + line);
      } catch (IOException ioe)
      {
        ioe.printStackTrace();
      }
    }
  }

  public void run(String arg) {
    run(new String[] {arg});
  }

  public void run(String args[])
  {
    if (args.length < 1)
    {
      System.out.println("USAGE: java GoodWindowsExec <cmd>");
      System.exit(1);
    }

    try
    {
      String osName = System.getProperty("os.name" );
      String[] cmd = new String[3];

      if( osName.equals( "Windows NT" ) || osName.equals( "Windows XP" ) )
      {
        cmd[0] = "cmd.exe" ;
        cmd[1] = "/C" ;
        cmd[2] = args[0];
      }
      else if( osName.equals( "Windows 95" ) )
      {
        cmd[0] = "command.com" ;
        cmd[1] = "/C" ;
        cmd[2] = args[0];
      }

      Runtime rt = Runtime.getRuntime();
      System.out.println("Execing " + cmd[0] + " " + cmd[1]
          + " " + cmd[2]);
      Process proc = rt.exec(cmd);
      // any error message?
      StreamGobbler errorGobbler = new
          StreamGobbler(proc.getErrorStream(), "ERROR");

      // any output?
      StreamGobbler outputGobbler = new
          StreamGobbler(proc.getInputStream(), "OUTPUT");

      // kick them off
      errorGobbler.start();
      outputGobbler.start();

      // any error???
      int exitVal = proc.waitFor();
      System.out.println("ExitValue: " + exitVal);
    } catch (Throwable t)
    {
      t.printStackTrace();
    }
  }


  public String concat (String[] strArray) {
    StringBuffer buff = new StringBuffer(1000);

    for (int i = 0; i < strArray.length; i++) {
      buff.append(strArray[i] + " ");
    }

    return buff.toString();
  }


  private void compile(String pluginName) {
    String srcPath = pluginName + File.separator + "src";
    String destPath = pluginName + File.separator + "classes";

    File srcPathFile = new File(destPath);
    if (!srcPathFile.exists()) {
      System.err.println("Source directory \"" + srcPath + "\" does not exist. Exiting.");
      System.exit(1);
    }

    File destPathFile = new File(destPath);
    if (!destPathFile.exists()) {
      destPathFile.mkdirs();
    }

    File includeFolder = new File(pluginName + File.separator + "lib");
    if (!includeFolder.exists()) includeFolder = null;

    String classPath = calcClassPath(includeFolder);

    File processingLibFolder = new File(".." + File.separator + "lib");
    String processingLibPath = includeFolder(processingLibFolder);

    classPath += ";" + processingLibPath;

    String buildPath = includeSourceFolder(new File(srcPath));

    String command[] = new String[] {
//#ifdef MACOS
      // linux doesn't seem to like this
      // though windows probably doesn't care
//      userdir + "jikes",
//#else
      ".." + File.separator + "jikes",
//#endif

      // necessary to make output classes compatible with 1.1
      // i.e. so that exported applets can work with ms jvm on the web
      "-target",
      "1.1",

      // used when run without a vm ("expert" mode)
      "-bootclasspath",
      calcBootClassPath(),
      //System.getProperty("sun.boot.class.path") + additional,

      // needed for macosx so that the classpath is set properly
      // also for windows because qtjava will most likely be here
      // and for linux, it just doesn't hurt
      "-classpath",
      //System.getProperty("java.class.path"),
      //calcClassPath(includeFolder),
      classPath,

      "-nowarn", // we're not currently interested in warnings
      //"+E", // output errors in machine-parsable format
      "-d", destPath, // output the classes in the buildPath
      //"-sourcepath", srcPath,
      buildPath// + File.separator + className + ".java" // file to compile
    };

    //try {
      //PrintWriter pw = new PrintWriter(new FileWriter("compile.bat"));
      //for (int i = 0; i < command.length; i++) {
      //  pw.print(command[i] + " ");
      //}
      //pw.close();

      run(concat(command));

    //} catch (IOException e) {
    //  e.printStackTrace();
    //}
  }

  public void jar(String pluginName) {
    String destPath = pluginName + File.separator + "classes";
    String destJar = pluginName + File.separator + pluginName + ".jar";

    String jarString = "jar cf " + destJar + " -C " + destPath + File.separator + " .";

    /*try {
      PrintWriter pw = new PrintWriter(new FileWriter("jar.bat"));
      pw.print(jarString);
      pw.close();
    } catch (IOException e) {
      e.printStackTrace();
    }*/

    run(jarString);
  }

  static String additional;
  static public String calcBootClassPath() {
    if (additional == null) {
/*#ifdef MACOS
additional =
includeFolder(new File("/System/Library/Java/Extensions/"));
#else*/
      additional = "";
//#endif
    }
    return System.getProperty("sun.boot.class.path") + additional;
  }

  static public String calcClassPath(File include) {
    return System.getProperty("java.class.path") + includeFolder(include);
  }

  /**
   * Return the path for a folder, with appended paths to
   * any .jar or .zip files inside that folder.
   */
  static public String includeSourceFolder(File folder) {
    if (folder == null) return "";

    StringBuffer abuffer = new StringBuffer();
    String sep = " ";

    try {
      // add the folder itself in case any unzipped files
      String path = folder.getCanonicalPath();
      //abuffer.append(sep);
      //abuffer.append(path);

      if (!path.endsWith(File.separator)) {
        path += File.separator;
      }
      //System.out.println("path is " + path);

      String list[] = folder.list();
      if (list == null) return "";
      for (int i = 0; i < list.length; i++) {
        if (list[i].toLowerCase().endsWith(".java")) {
          abuffer.append(sep);
          abuffer.append(path);
          abuffer.append(list[i]);
        }
      }
    } catch (IOException e) {
      e.printStackTrace();  // this would be odd
    }
    //System.out.println("included path is " + abuffer.toString());
    return abuffer.toString();
  }


  /**
   * Return the path for a folder, with appended paths to
   * any .jar or .zip files inside that folder.
   */
  static public String includeFolder(File folder) {
    if (folder == null) return "";

    StringBuffer abuffer = new StringBuffer();
    String sep = System.getProperty("path.separator");

    try {
      // add the folder itself in case any unzipped files
      String path = folder.getCanonicalPath();
      abuffer.append(sep);
      abuffer.append(path);

      if (!path.endsWith(File.separator)) {
        path += File.separator;
      }
      //System.out.println("path is " + path);

      String list[] = folder.list();
      for (int i = 0; i < list.length; i++) {
        if (list[i].toLowerCase().endsWith(".jar") ||
            list[i].toLowerCase().endsWith(".zip")) {
          abuffer.append(sep);
          abuffer.append(path);
          abuffer.append(list[i]);
        }
      }
    } catch (IOException e) {
      e.printStackTrace();  // this would be odd
    }
    //System.out.println("included path is " + abuffer.toString());
    magicImports(abuffer.toString());
    return abuffer.toString();
  }

  static public String[] magicImports(String path) {
    String imports[] = new String[100];
    int importCount = 0;

    String pieces[] =
        BApplet.splitStrings(path, File.pathSeparatorChar);

    for (int i = 0; i < pieces.length; i++) {
      //System.out.println("checking piece " + pieces[i]);
      if (pieces[i].length() == 0) continue;

      if (pieces[i].toLowerCase().endsWith(".jar") ||
          pieces[i].toLowerCase().endsWith(".zip")) {
        try {
          ZipFile file = new ZipFile(pieces[i]);
          Enumeration entries = file.entries();
          while (entries.hasMoreElements()) {
            ZipEntry entry = (ZipEntry) entries.nextElement();
            if (entry.isDirectory()) {
              String name = entry.getName();
              if (name.equals("META-INF/")) continue;
              name = name.substring(0, name.length() - 1);
              name = name.replace('/', '.');

              if (importCount == imports.length) {
                String temp[] = new String[importCount << 1];
                System.arraycopy(imports, 0, temp, 0, importCount);
                imports = temp;
              }
              imports[importCount++] = name;
              //System.out.println("import " + name + ".*;");
            }
            //System.out.print(entry.isDirectory() ? "D " : "c ");
            //System.out.println(entry.getName());
          }
        } catch (IOException e) {
          System.err.println("Error in file " + pieces[i]);
          e.printStackTrace();
        }
      } else {
        File dir = new File(pieces[i]);
        if (dir.exists()) {
          importCount = magicImportsRecursive(dir, null,
              imports, importCount);
        }
      }
    }
    //return null;
    String output[] = new String[importCount];
    System.arraycopy(imports, 0, output, 0, importCount);
    return output;
  }


  static public int magicImportsRecursive(File dir, String sofar,
                                          String imports[],
                                          int importCount) {
    String files[] = dir.list();
    for (int i = 0; i < files.length; i++) {
      if (files[i].equals(".") || files[i].equals("..")) continue;

      File sub = new File(dir, files[i]);
      if (sub.isDirectory()) {
        String nowfar = (sofar == null) ?
            files[i] : (sofar + "." + files[i]);
        //System.out.println(nowfar);
        imports[importCount++] = nowfar;

        importCount = magicImportsRecursive(sub, nowfar,
            imports, importCount);
      }
    }
    return importCount;
  }

  public static void main(String[] args) {
    if (args.length < 1) {
      System.out.println("Usage: java PluginCompiler PluginName\n  Where PluginName is a "+
          "directory with a \"src\" directory inside it.");
      return;
    }
    PluginCompiler compiler = new PluginCompiler(args[0]);

  }

}
