package factory;

import java.applet.*;
import java.awt.*;
import java.net.*;
import java.util.*;


/** A factory for loading images from the hard disk or web sites for use in
 *  an applet or application.  Uses flyweighting to cache identical images.
 *
 *  @author Martin Stepp (stepp)
 */
public class ImageFactory
{
  // private constructor to prohibit constructing factory
  private ImageFactory() {}

  /** The current running version of Java; used to discover capabilities. */
  public static final double JAVA_VERSION = new Double(System.getProperty("java.version").substring(0, 3)).doubleValue();

  private static final Hashtable imageHash = new Hashtable();
  private static final boolean SHOULD_HASH = true;

  /** Makes and returns an image by loading it from the given file name.
   *  In application context, this loads from the local disk.
   *  In applet context, this treats fileName as a relative URL from the
   *  applet's current code base URL directory.
   *
   *  @param fileName the fully-qualified filename, or relatively qualified URL,
   *  of the image resource to load.
   *  @param comp the graphical component or applet responsible for tracking
   *  the loading of this image.
   *
   *  @return the successfully loaded Image object.
   */
  public static Image createImage(String fileName, Component comp)
  {
    return createImage(fileName, null, comp);
  }

  /** Makes and returns an image by loading it from the given file name
   *  within the given JAR archive.
   *  In application context, this loads from the local disk.
   *  In applet context, this treats fileName as a relative URL from the
   *  applet's current code base URL directory.
   *
   *  This method and its jarFileName option exists to permit
   *  loading images from within a JAR; the method will construct a String
   *  that represents the JAR path to the image, such as:
   *  <p><code><pre>
   *  jar:file:/h:/java/myapplet/myapplet.jar!/images/someimage.gif
   *
   *  or
   *
   *  jar:http://www.mysite.com/myapplet/myapplet.jar!/images/someimage.gif
   *  </pre></code>
   *
   *  @param fileName the fully-qualified filename, or relatively qualified URL,
   *  of the image resource to load.
   *  @param comp the graphical component or applet responsible for tracking
   *  the loading of this image.
   *  If in applet context, the applet should be passed for this parameter.
   *  @param jarFileName loads as though the fileName is in the given jar file.
   *                     Ignored if null is passed.
   *
   *  @return the successfully loaded Image object.
   */
  public static Image createImage(String fileName, String jarFileName, Component comp)
  {
    Image img = null;

    // look up previously loaded image
    if (SHOULD_HASH  &&  imageHash.containsKey(fileName))
      img = (Image)imageHash.get(fileName);

    // check legality of loading image from a JAR
    else if (JAVA_VERSION < 1.2  &&  jarFileName != null)
      throw new IllegalArgumentException("Can't load from a JAR in pre-v1.2 Java! "
                                         + "Current version is: " + JAVA_VERSION);

    // if an applet, load image from the web
    else
    {
      if (comp instanceof Applet)
      {
        Applet applet = (Applet) comp;
        URL codeBase = applet.getCodeBase();
        if (codeBase == null)
          throw new IllegalArgumentException("Applet has null code base! \n\n"
              + "Don't load images in constructors; do all image-handling no \n"
              + "earlier than in the applet's init() method.");

        try
        {
          URL fileURL = (jarFileName != null)
                      ? getJarFileURL(codeBase, jarFileName, fileName)
                      : new URL(codeBase, fileName);
          img = applet.getImage(fileURL);
        }
        catch (MalformedURLException mfurle)
        {
          throw new IllegalArgumentException("Bad URL loading applet image from "
              + fileName + " : " + mfurle);
        }
      }

      // if not an applet, load image from a file
      else
      {
        Toolkit tk = Toolkit.getDefaultToolkit();
        if (jarFileName != null)
        {
          // load from JAR as URL
          URL fileURL = getJarFileURL(System.getProperty("user.dir")
                                      + "/" + jarFileName, fileName);
          img = tk.getImage(fileURL);
        }
        else
        {
          // load as plain file
          img = tk.getImage(fileName);
        }
      }

      if (img == null)
        throw new IllegalArgumentException("Image was null; something must have gone wrong!");

      // wait for image to load using MediaTracker
      MediaTracker mt = new MediaTracker(comp);
      mt.addImage(img, 0);
      try { mt.waitForID(0); } catch (InterruptedException ie) {}

      // check to see if image loaded properly
      if (img.getWidth(comp) == -1 || img.getHeight(comp) == -1)
        throw new IllegalArgumentException("No image was found in " + fileName);

      if (SHOULD_HASH)
        imageHash.put(fileName, img);
    }

    return img;
  }
  
  // returns URL of given file inside given JAR file, using given code base
  private static URL getJarFileURL(URL codeBase, String jarFileName, String fileName)
  {
    try 
    {
      URL u = new URL("jar:" + codeBase + jarFileName + "!/" + fileName);
      return u;
    }
    catch (MalformedURLException mfurle)
    {
      throw new IllegalArgumentException(mfurle.toString());
    }
  }

  // returns URL of given file inside given JAR file, making no assumptions
  // about code base (can pass a full path JAR file name)
  private static URL getJarFileURL(String jarFileName, String fileName)
  {
    try 
    {
      URL u = new URL("jar:file:" + jarFileName + "!/" + fileName);
      return u;
    }
    catch (MalformedURLException mfurle)
    {
      throw new IllegalArgumentException(mfurle.toString());
    }
  }
}
