// ============================================================================
// File:               $File$
//
// Project:            
//
// Purpose:            
//
// Author:             Rammi
//
// Copyright Notice:   (c) 2008  Rammi (rammi@caff.de)
//                     This code is in the public domain.
//                     Use at own risk.
//                     No guarantees given.
//
// Latest change:      $Date$
//
// History:	       $Log$
//=============================================================================
package de.caff.asteroid.analysis;

import de.caff.i18n.I18n;
import de.caff.i18n.swing.RJButton;
import de.caff.util.Utility;
import de.caff.util.debug.Debug;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.MalformedURLException;
import java.net.URL;

/**
 *  Applet to display dump files.
 */
public class AnalysisApplet
        extends JApplet
{
  static {
    I18n.addAppResourceBase("de.caff.asteroid.analysis.resources.AnalysisApplet");
  }
  public static final String VERSION = "0.78";

  public static final String PARAM_FRAME                = "framed";
  public static final String PARAM_FRAME_WIDTH          = "framewidth";
  public static final String PARAM_FRAME_HEIGHT         = "framewidth";
  public static final String PARAM_FRAME_BUTTON_TEXT    = "frameButtonText";
  public static final String PARAM_FRAME_BUTTON_ICON    = "frameButtonIcon";
  public static final String PARAM_FRAME_BUTTON_TOOLTIP = "frameButtonTooltip";
  public static final String PARAM_FRAME_BUTTON_BLACK   = "frameButtonBlack";
  public static final String PARAM_FILE                 = "file";
  public static final String PARAM_AUTO_START           = "autoStart";

  /** Displayed in frame mode? */
  private boolean framed;
  /** Value for auto start of animation. */
  private boolean autoStart;

  /** The applet component where everything is displayed. */
  private AnalysisAppletDisplay appletDisplay;

  /**
   * Constructor.
   */
  public AnalysisApplet()
  {
    getContentPane().setLayout(new BorderLayout());
  }

  /**
   * Called by the browser or applet viewer to inform
   * this applet that it has been loaded into the system. It is always
   * called before the first time that the <code>start</code> method is
   * called.
   * <p/>
   * A subclass of <code>Applet</code> should override this method if
   * it has initialization to perform. For example, an applet with
   * threads would use the <code>init</code> method to create the
   * threads and the <code>destroy</code> method to kill them.
   * <p/>
   * The implementation of this method provided by the
   * <code>Applet</code> class does nothing.
   *
   * @see java.applet.Applet#destroy()
   * @see java.applet.Applet#start()
   * @see java.applet.Applet#stop()
   */
  @Override
  public void init()
  {
    Utility.setApplet(this);
    Debug.initFromProperties(Debug.DEBUG_ALL_MASK & ~Debug.TRACE_FLAG);
    Debug.message("init()\nappletDisplay=%1\nthis=%2\nthread=%3",
                  appletDisplay, Integer.toHexString(hashCode()),
                  Thread.currentThread());
    if (appletDisplay == null) {
      autoStart = Utility.getBooleanParameter(PARAM_AUTO_START, false);
      if (Utility.getBooleanParameter(PARAM_FRAME, false)) {
        framed = true;
        final AnalysisAppletFrame appletFrame = new AnalysisAppletFrame(this);
        Box outer = Box.createHorizontalBox();
        Box inner = Box.createVerticalBox();
        String buttonText = getParameter(PARAM_FRAME_BUTTON_TEXT);
        String buttonIconURL = getParameter(PARAM_FRAME_BUTTON_ICON);
        JButton button = null;
        if (buttonText != null  ||  buttonIconURL != null) {
          Icon icon = null;
          if (buttonIconURL != null) {
            try {
              icon = new ImageIcon(getAppletContext().getImage(new URL(getDocumentBase(), buttonIconURL)));
            } catch (MalformedURLException e) {
              e.printStackTrace();
            }
          }
          button = new JButton(buttonText, icon);
          String buttonTooltip = getParameter(PARAM_FRAME_BUTTON_TOOLTIP);
          if (buttonTooltip != null) {
            button.setToolTipText(buttonTooltip);
          }
        }
        if (button == null) {
          // fallback
          button = new RJButton("btnShowFrame");
        }
        outer.add(Box.createHorizontalGlue());
        outer.add(inner);
        outer.add(Box.createHorizontalGlue());
        inner.add(Box.createVerticalGlue());
        inner.add(button);
        inner.add(Box.createVerticalGlue());
        button.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent e)
          {
            appletFrame.setVisible(true);
            appletFrame.toFront();
          }
        });
        appletFrame.setSize(Utility.getIntParameter(PARAM_FRAME_WIDTH, 640),
                            Utility.getIntParameter(PARAM_FRAME_HEIGHT, 520));
        getContentPane().add(outer, BorderLayout.CENTER);

        appletDisplay = appletFrame;
        if (Utility.getBooleanParameter(PARAM_FRAME_BUTTON_BLACK, false)) {
          getContentPane().setBackground(Color.black);
          button.setBackground(Color.black);
        }
      }
      else {
        AnalysisAppletComponent appletComp = new AnalysisAppletComponent(this);
        getContentPane().add(appletComp, BorderLayout.CENTER);

        appletDisplay = appletComp;
      }

      String file = getParameter(PARAM_FILE);
      if (file != null) {
        showDumpFile(file);
      }
    }
  }

  /**
   * Called by the browser or applet viewer to inform
   * this applet that it is being reclaimed and that it should destroy
   * any resources that it has allocated. The <code>stop</code> method
   * will always be called before <code>destroy</code>.
   * <p/>
   * A subclass of <code>Applet</code> should override this method if
   * it has any operation that it wants to perform before it is
   * destroyed. For example, an applet with threads would use the
   * <code>init</code> method to create the threads and the
   * <code>destroy</code> method to kill them.
   * <p/>
   * The implementation of this method provided by the
   * <code>Applet</code> class does nothing.
   *
   * @see java.applet.Applet#init()
   * @see java.applet.Applet#start()
   * @see java.applet.Applet#stop()
   */
  @Override
  public void destroy()
  {
    Debug.message("destroy()\nthis=%1", Integer.toHexString(hashCode()));
  }

  /**
   * Called by the browser or applet viewer to inform
   * this applet that it should start its execution. It is called after
   * the <code>init</code> method and each time the applet is revisited
   * in a Web page.
   * <p/>
   * A subclass of <code>Applet</code> should override this method if
   * it has any operation that it wants to perform each time the Web
   * page containing it is visited. For example, an applet with
   * animation might want to use the <code>start</code> method to
   * resume animation, and the <code>stop</code> method to suspend the
   * animation.
   * <p/>
   * Note: some methods, such as <code>getLocationOnScreen</code>, can only
   * provide meaningful results if the applet is showing.  Because
   * <code>isShowing</code> returns <code>false</code> when the applet's
   * <code>start</code> is first called, methods requiring
   * <code>isShowing</code> to return <code>true</code> should be called from
   * a <code>ComponentListener</code>.
   * <p/>
   * The implementation of this method provided by the
   * <code>Applet</code> class does nothing.
   *
   * @see java.applet.Applet#destroy()
   * @see java.applet.Applet#init()
   * @see java.applet.Applet#stop()
   * @see java.awt.Component#isShowing()
   * @see java.awt.event.ComponentListener#componentShown(java.awt.event.ComponentEvent)
   */
  @Override
  public void start()
  {
    Debug.message("start()");
  }

  /**
   * Called by the browser or applet viewer to inform
   * this applet that it should stop its execution. It is called when
   * the Web page that contains this applet has been replaced by
   * another page, and also just before the applet is to be destroyed.
   * <p/>
   * A subclass of <code>Applet</code> should override this method if
   * it has any operation that it wants to perform each time the Web
   * page containing it is no longer visible. For example, an applet
   * with animation might want to use the <code>start</code> method to
   * resume animation, and the <code>stop</code> method to suspend the
   * animation.
   * <p/>
   * The implementation of this method provided by the
   * <code>Applet</code> class does nothing.
   *
   * @see java.applet.Applet#destroy()
   * @see java.applet.Applet#init()
   */
  @Override
  public void stop()
  {
    Debug.message("stop()");
    if (!framed) {
      // no need to use performance if not visible
      appletDisplay.stopAnimation();
    }
  }

  /**
   *  Load a new dump file.
   *  Animation starts automatically if <tt>autoStart</tt> applet parameter is <code>true</code>.
   *  Invoke this method from JavaScript as a workaround to bug #6669818.
   *  @param dumpFile dump file
   *  @see #runDumpFile(String)
   *  @see #loadDumpFile(String)
   */
  public void showDumpFile(String dumpFile)
  {
    Debug.message("showDumpFile(%1)", dumpFile);
    appletDisplay.showDumpFile(dumpFile, autoStart);
  }

  /**
   *  Load a new dump file and start the animation.
   *  Invoke this method from JavaScript as a workaround to bug #6669818.
   *  @param dumpFile dump file
   *  @see #showDumpFile(String)
   *  @see #loadDumpFile(String)
   */
  public void runDumpFile(String dumpFile)
  {
    appletDisplay.showDumpFile(dumpFile, true);
  }

  /**
   *  Load a new dump file, but don't start the animation.
   *  Invoke this method from JavaScript as a workaround to bug #6669818.
   *  @param dumpFile dump file
   *  @see #showDumpFile(String)
   *  @see #runDumpFile(String) 
   */
  public void loadDumpFile(String dumpFile)
  {
    appletDisplay.showDumpFile(dumpFile, false);
  }

  /**
   * Returns information about this applet. An applet should override
   * this method to return a <code>String</code> containing information
   * about the author, version, and copyright of the applet.
   * <p/>
   * The implementation of this method provided by the
   * <code>Applet</code> class returns <code>null</code>.
   *
   * @return a string containing information about the author, version, and
   *         copyright of the applet.
   */
  @Override
  public String getAppletInfo()
  {
    return String.format("Asteroids Dumpfile Analyser V. %s \n" +
                         "Written by Rammi (rammi@caff.de) in 2008 \n" +
                         "Use freely without restrictions, but at your own risk. \n" +
                         "See http://caff.de/asteroid/ for more info and sources.", VERSION);
  }

  /**
   * Returns information about the parameters that are understood by
   * this applet. An applet should override this method to return an
   * array of <code>Strings</code> describing these parameters.
   * <p/>
   * Each element of the array should be a set of three
   * <code>Strings</code> containing the name, the type, and a
   * description. For example:
   * <p><blockquote><pre>
   * String pinfo[][] = {
   * 	 {"fps",    "1-10",    "frames per second"},
   * 	 {"repeat", "boolean", "repeat image loop"},
   * 	 {"imgs",   "url",     "images directory"}
   * };
   * </pre></blockquote>
   * <p/>
   * The implementation of this method provided by the
   * <code>Applet</code> class returns <code>null</code>.
   *
   * @return an array describing the parameters this applet looks for.
   */
  @Override
  public String[][] getParameterInfo()
  {
    return new String[][] {
            { PARAM_FILE,                      "url",     "location of dump file to display" },
            { PARAM_AUTO_START,                "boolean", "start running after file is loaded" },
            { PARAM_FRAME,                     "boolean", "true: run in frame; false: run in page" },
            { PARAM_FRAME_WIDTH,               "int > 0", "start width of frame in frame mode" },
            { PARAM_FRAME_HEIGHT,              "int > 0", "start height of frame in frame mode" },
            { PARAM_FRAME_BUTTON_TEXT,         "string",  "inpage button text in frame mode" },
            { PARAM_FRAME_BUTTON_ICON,         "url",     "inpage button icon in frame mode" },
            { PARAM_FRAME_BUTTON_TOOLTIP,      "string",  "inpage button tooltip text in frame mode" },
            { PARAM_FRAME_BUTTON_BLACK,        "boolean", "only for frame mode: paint the button black?" },
            { Debug.PARAM_DEBUG_SHOW_WINDOW,   "boolean", "show debug window?" },
            { Debug.PARAM_DEBUG_PRINT_CONSOLE, "boolean", "print debug messages to console?" },
            { Debug.PARAM_DEBUG_MASK,          "TSWEFLA", "T:Trace, S:Standard, W:Warning, E:Error, F:Fatal, L:Log, A:Assertions" }
    };
  }
}
