package factory;

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

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

  /** 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 clipHash = new Hashtable();
  private static final boolean SHOULD_HASH = true;

  /** Makes and returns a clip 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 AudioClip createAudioClip(String fileName, Component comp)
  {
    return createAudioClip(fileName, null, comp);
  }

  /** Makes and returns a clip 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 clip, such as:
   *  <p><code><pre>
   *  jar:file:/h:/java/myapplet/myapplet.jar!/images/someclip.wav
   *
   *  or
   *
   *  jar:http://www.mysite.com/myapplet/myapplet.jar!/images/someclip.wav
   *  </pre></code>
   *
   *  @param fileName the fully-qualified filename, or relatively qualified URL,
   *  of the clip resource to load.
   *  @param comp the graphical component or applet responsible for tracking
   *  the loading of this clip.
   *  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 clip object.
   */
  public static AudioClip createAudioClip(String fileName, String jarFileName, Component comp)
  {
    AudioClip clip = null;

    // look up previously loaded clip
    if (SHOULD_HASH  &&  clipHash.containsKey(fileName))
      clip = (AudioClip)clipHash.get(fileName);

    // check legality of loading clip 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 clip 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);
          clip = applet.getAudioClip(fileURL);
        }
        catch (MalformedURLException mfurle)
        {
          throw new IllegalArgumentException("Bad URL loading applet audio clip from "
              + fileName + " : " + mfurle);
        }
      }

      // if not an applet, load clip from a file
      else
      {
        if (jarFileName != null)
        {
          // load from JAR as URL
          URL fileURL = getJarFileURL(System.getProperty("user.dir")
                                      + "/" + jarFileName, fileName);
          clip = Applet.newAudioClip(fileURL);
        }
        else
        {
          try
          {
            // load as plain file
            URL fileURL = new URL("file:" + System.getProperty("user.dir") + "/" + fileName);
            clip = Applet.newAudioClip(fileURL);
          }
          catch (MalformedURLException mfurle)
          {
            throw new IllegalArgumentException("Bad URL loading file audio clip from "
                + fileName + " : " + mfurle);
          }
        }
      }

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

      if (SHOULD_HASH)
        clipHash.put(fileName, clip);
    }

    return clip;
  }

  // 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());
    }
  }
}
