import java.awt.*;
import java.awt.event.*;
import java.applet.*;

/**
 * <p>Mini launcher app to run applets built with Processing in standalone/fullscreen mode.
 * Fullscreen support requires Java 1.4 or later, resolution will be chosen automatically (based on applet dimensions)
 * or can be set via commandline parameters.</p>
 * 
 * @version 0.1 (18/05/04) initial release
 * @author info@toxi.co.uk
 *
 * parts of the code originally by:
 *    Ben Fry (http://acg.media.mit.edu/people/fry/)
 *    Micheal Zoellner (http://www.m05.de)
 * 
 * <p><b>example usages</b> (best put in a shell script or batch file ;)
 * <pre>java -cp applaunch.jar;myDemoApplet.jar BAppLauncher -applet myDemoApplet -sw 640 -sh 480 -bg ffffff</pre>
 * Display "myDemoApplet" fullscreen @ 640x480 pixels. If the applet's dimension is smaller than the screen size
 * the applet will be centered and the area around it filled in white (ffffff in hex).
 * 
 * <pre>java -cp applaunch.jar;myDemoApplet.jar BAppLauncher -applet myDemoApplet -title hello world</pre>
 * Diplay "myDemoApplet" in a standard window, centered on screen (no res. switching). The window title will be set to "hello world"</p>
 * 
 */

public class BAppLauncher extends Applet {

  public BAppLauncher() {}

  public static void main(String[] args) {
    if (args.length < 2 || getValueForKey(args, "-h", false, false) != "") {
      showUsage();
    }
    else {

      String appName = getValueForKey(args, "-applet", true, false);
      if (appName != "") {
        System.out.println("launching " + appName + "...");
        try {
          // get default screendevice and configuration
          GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
          GraphicsConfiguration gc = gd.getDefaultConfiguration();

          // get current screen dimensions
          DisplayMode mode = gd.getDisplayMode();
          int currW = mode.getWidth();
          int currH = mode.getHeight();

          // get background colour param
          String val = getValueForKey(args, "-bg", true, false);
          Color bgcol = new Color(val.equals("") ? 0 : Integer.parseInt(val, 16));

          // check for new screen size
          val = getValueForKey(args, "-sw", true, false);
          int userW = val.equals("") ? 0 : Integer.parseInt(val);
          val = getValueForKey(args, "-sh", true, false);
          int userH = val.equals("") ? 0 : Integer.parseInt(val);
          // check if fullscreen mode has been requested
          boolean isFullScreen = (!getValueForKey(args, "-f", false, false).equals("")) || (userW>0 && userH>0);

          // get window title or default to applet name
          val = getValueForKey(args, "-title", true, true);
          String title = val.equals("") ? appName : val;

          // try to instanciate applet
          Frame frame = new Frame(title, gc);
          Class c = Class.forName(appName);
          BApplet applet = (BApplet)c.newInstance();
          applet.init();
          applet.start();

          // prepare frame with user settings
          frame.setUndecorated(isFullScreen);
          frame.setBackground(bgcol);
          frame.setLayout(null);

          if (gd.isFullScreenSupported() && isFullScreen) {
            gd.setFullScreenWindow(frame);
            // temporarily hide the frame (gets enabled by setFullScreenWindow() )
            frame.hide();
            if (gd.isDisplayChangeSupported()) {
              // if no screen res has been set explicitly, try to find the most suitable
              if (userW == 0 || userH == 0) {
                System.out.println("checking display modes...");
                DisplayMode[] avail = gd.getDisplayModes();
                int best = avail.length-1;
                int minDiff = 1000000;
                // check if applet is landscape or portrait format
                if (applet.width > applet.height) {
                  for (int i = 0; i < avail.length; i++) {
                    int currx = avail[i].getWidth() - applet.width;
                    if (currx >= 0 && currx <= minDiff) {
                      best = i;
                      minDiff = currx;
                    }
                  }
                }
                else {
                  // vertical checking
                  for (int i = 0; i < avail.length; i++) {
                    int curry = avail[i].getHeight() - applet.height;
                    if (curry >= 0 && curry <= minDiff) {
                      best = i;
                      minDiff = curry;
                    }
                  }
                }
                System.out.println("best match screen res: " + avail[best].getWidth() + "x" + avail[best].getHeight());
                userW = avail[best].getWidth();
                userH = avail[best].getHeight();
              }
              // switch resolution & center applet
              gd.setDisplayMode(new DisplayMode(userW, userH, 32, DisplayMode.REFRESH_RATE_UNKNOWN));
              frame.add(applet);
              applet.setBounds((userW - applet.width) / 2, (userH - applet.height) / 2, applet.width, applet.height);
              frame.setLocation(0, 0);
            }
          }
          else {
            // shrink window to applet size
            // make use of BorderLayout to have frame.pack() behave properly
            frame.setLayout(new BorderLayout());
            frame.setResizable(false);
            frame.add(applet);
            frame.pack();
            // center on screen
            frame.setLocation((currW - frame.getWidth()) / 2, (currH - frame.getHeight()) / 2);
          }

          frame.show();
          applet.requestFocus();

          frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
              System.exit(0);
            }
          });

          applet.addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
              // quit app on ESC
              if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                System.exit(0);
              }
            }
          });

        }
        catch (Exception e) {
          e.printStackTrace();
          System.exit(1);
        }
      }
    }
  }

  /**
   * primitive command line arguments parser
   * 
   * @param args String array of key/value items
   * @param key Name of the key to check for
   * @param valueInNextArg Flag to indicate if key has a value (true) or if we only check for presence of the key
   * @param multPossible Flag indicating if a key value can consist of several successive items in the array
   * @return Key's value (or itself) or an empty string if key (or its value) hasn't been found.
   */
  private static String getValueForKey(String[] args, String key, boolean valueInNextArg, boolean multPossible) {
    if (valueInNextArg) {
      for (int i = 0; i < args.length - 1; i++) {
        if (args[i].equals(key)) {
          String val = args[i + 1];
          if (multPossible) {
            for (int j = i + 2; j < args.length; j++) {
              if (!args[j].startsWith("-"))
                val += " " + args[j];
            }
          }
          return val;
        }
      }
    }
    else {
      for (int i = 0; i < args.length; i++) {
        if (args[i].equals(key))
          return args[i];
      }
    }
    return "";
  }

  /**
   * print help text for newbie users
   */
  private static void showUsage() {
    System.out.println("usage: java -cp applaunch.jar;<user.jar> BAppLauncher [options] -applet <UserClassName>");
    System.out.println("    -h          show this help message");
    System.out.println("    -title      set window title (omitted in fullscreen mode)");
    System.out.println("    -f          fullscreen mode");
    System.out.println("\nthe following options are only valid if fullscreen mode is active:");
    System.out.println("    -sw         set desired screen width in pixels for fullscreen");
    System.out.println("                (automatically sets -f option)");
    System.out.println("    -sh         desired screen height in pixels...");
    System.out.println("                *if fullscreen mode is enabled, but no screen size is set explicitly,");
    System.out.println("                the app will attempt a best match");
    System.out.println("    -bg 123456  background/border colour in 6-digit hex format (eg. ff0000 for bright red)");
    System.out.println("\npress ESC or ALT+F4 (win) or COMMAND+Q (OSX) to quit application.");
    System.exit(1);
  }
}
