/*
 * PictureButton.java
 *
 * Copyright (c) 1999 by Rüdiger Appel, All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies. 
 *
 * See also: http://www.3quarks.com
 *
 */


import java.applet.*;
import java.awt.*;
import java.net.*;
import java.util.*;


public class PictureButton extends Applet implements Runnable
  {
  private Thread    loader          = null;
  private Thread    timer           = null;
  private boolean   allLoaded       = false;
  
  private String    linkUrl         = null;
  private String    linkTarget      = null;
  private String    linkApplet      = null;
  private String    linkMessage     = null;
  private Polygon   linkArea        = null;
  private int       linkDelay       = 0;
  
  private Point     imagePosition   = null;
  private Image     normalImage     = null;
  private Image     mouseOverImage  = null;
  private Image     mouseDownImage  = null;
  private Image     currentImage    = null;
  private Image     buffer          = null;
  private Image     backgroundImage = null;
  
  private AudioClip mouseDownSound  = null;

  private boolean   isMouseOver     = false;
  private boolean   isMouseDown     = false;
  private boolean   isMouseCapture  = false;
  private boolean   isStatusMessage = false;

  
  public PictureButton ()
    {
    // write applet info
    System.out.println (getAppletInfo ());
    }
  
  
  public String getAppletInfo ()
    {
    // return my copyright notice
    return "PictureButton, Version 1.0"
         + System.getProperty ("line.separator")
         + "Copyright (c) 1999 by Rüdiger Appel, All Rights Reserved" 
         + System.getProperty ("line.separator")
         + "See also: http://www.3quarks.com";
    }

  
  public void init ()
    {
    // get link parameters
    linkUrl     = getParameter ("LinkUrl");
    linkTarget  = getParameter ("LinkTarget");
    linkApplet  = getParameter ("LinkApplet");
    linkMessage = getParameter ("LinkMessage");
    linkDelay   = getIntegerParameter ("LinkDelay", 100);
    linkArea    = getPolygonParameter ("LinkArea");

    // get image position
    imagePosition = getPointParameter ("ImagePosition");
    
    // get and set background parameters
    Color backgroundColor = getColorParameter ("BackgroundColor");
    if (backgroundColor != null)
      setBackground (backgroundColor);
    }


	private int getIntegerParameter (String name, int defaultValue)
		{
    // return an integer parameter 
    try 
      {
      return Integer.parseInt (getParameter (name).trim ());
      }
    catch (Exception exception) 
      {
      return defaultValue;
      }
    }
  

  private Polygon getPolygonParameter (String name)
    {
    // return a polygon parameter 
    try 
      {
      StringTokenizer tokenizer = new StringTokenizer (getParameter (name), ",");
      Polygon         polygon   = new Polygon ();

      while (true)
        if (tokenizer.hasMoreTokens ())
          polygon.addPoint (Integer.parseInt (tokenizer.nextToken ().trim ()),
                            Integer.parseInt (tokenizer.nextToken ().trim ()));
        else
          return polygon;
      }
    catch (Exception exception) {}

    return null;
    }
  
    
  private Point getPointParameter (String name)
    {
    // return a point parameter 
    try 
      {
      StringTokenizer tokenizer = new StringTokenizer (getParameter (name), ",");

      if (tokenizer.countTokens () == 2)
        return new Point (Integer.parseInt (tokenizer.nextToken ().trim ()),
                          Integer.parseInt (tokenizer.nextToken ().trim ()));
      }
    catch (Exception exception) {}

    return null;
    }
  

	private Color getColorParameter (String name)
		{
    // return a color parameter 
    try 
      {
      StringTokenizer tokenizer = new StringTokenizer (getParameter (name), ",");

      switch (tokenizer.countTokens ())
        {
        case 1:
          {
          String value = tokenizer.nextToken ().trim ();
          if ((value.length () == 7) && (value.charAt (0) == '#'))
            return new Color (Integer.parseInt (value.substring (1, 3), 16),
                              Integer.parseInt (value.substring (3, 5), 16),
                              Integer.parseInt (value.substring (5, 7), 16));
          }
        case 3:
          return new Color (Integer.parseInt (tokenizer.nextToken ().trim ()),
                            Integer.parseInt (tokenizer.nextToken ().trim ()), 
                            Integer.parseInt (tokenizer.nextToken ().trim ()));
        }
      }
    catch (Exception exception) {}
		  
    return null;
    }
  

  public void update (Graphics graphics)
    {
    paint (graphics);
    }


  public void paint (Graphics graphics)
    {
    // get applet size
    int width  = size ().width;
    int height = size ().height;

    // create buffer if not exist
    if (buffer == null)
      buffer = createImage (width, height);

    // get buffer graphics
    Graphics bufferGraphics = buffer.getGraphics ();
    bufferGraphics.clipRect (0, 0, width, height);

    // fill background
    bufferGraphics.setColor (getBackground ());
    bufferGraphics.fillRect (0, 0, width, height);

    // draw background
    if (backgroundImage != null)
      {
      int backgroundWidth  = backgroundImage.getWidth  (this);
      int backgroundHeight = backgroundImage.getHeight (this);
      for (int x = 0; x < width; x += backgroundWidth)
        for (int y = 0; y < height; y += backgroundHeight)
          bufferGraphics.drawImage (backgroundImage, x, y, this);
      }
    
    // paint button image
    currentImage = getButtonImage ();
    if (currentImage != null)
      if (imagePosition != null)
        bufferGraphics.drawImage (currentImage, 
                                  imagePosition.x, 
                                  imagePosition.y, 
                                  this);
      else                                  
        bufferGraphics.drawImage (currentImage, 
                                  (width  - currentImage.getWidth  (this)) / 2, 
                                  (height - currentImage.getHeight (this)) / 2, 
                                  this);

    // paint buffer
    graphics.drawImage (buffer, 0, 0, this);
    }


  private Image getButtonImage ()
    {
    Image image = null;

    // select an image
    if (isMouseOver)
      if (isMouseDown)
        image = mouseDownImage;
      else
        image = mouseOverImage;
    else
      image = normalImage;

    // check selected image
    if (image != null)
      return image;

    // select mouse over image if mouse over button
    if (isMouseOver && (mouseOverImage != null))
      return mouseOverImage;

    // select normal image
    return normalImage;
    }


  private void updateButton (boolean over, boolean down, boolean capture)
    {
    // play sound if button goes down
    if (down && !isMouseDown && (mouseDownSound != null))
      mouseDownSound.play ();
      
    // save mouse flags
    isMouseOver    = over;
    isMouseDown    = down;
    isMouseCapture = capture;
    
    // update button image
    if (currentImage != getButtonImage ())
      repaint ();
   
    // update browser status line
    if (isStatusMessage != (isMouseOver || isMouseDown))
      {
      isStatusMessage = isMouseOver || isMouseDown;
      
      if (isStatusMessage)
        {
        if (linkMessage != null) 
          showStatus (linkMessage);
        else
          if (linkUrl != null) 
            showStatus (linkUrl);
        }
      else
        showStatus ("Done");
      }
    }

  
  private boolean mouseHit (int x, int y)
    {
    // return true if mouse over button
    if ((x >= 0) && (x < size ().width) &&
        (y >= 0) && (y < size ().height))
      {
      if (linkArea != null)
        return linkArea.inside (x, y);
      else
        return true;
      }

    return false;
    }


  public boolean mouseEnter (Event event, int x, int y)
    {
    // update button
    updateButton (mouseHit (x, y), false, false);
    
    return true;
    }

  
  public boolean mouseExit (Event event, int x, int y)
    {
    // update button
    updateButton (false, false, false);
    
    return true;
    }


  public boolean mouseMove (Event event, int x, int y)
    {
    // update button
    updateButton (mouseHit (x, y), false, isMouseCapture);

    return true;
    }

  
  public boolean mouseDrag (Event event, int x, int y)
    {
    // update button if mouse captured
    if (isMouseCapture)
      updateButton (mouseHit (x, y), mouseHit (x, y), true);

    return true;
    }

   
  public boolean mouseDown (Event event, int x, int y)
    {
    // update button if mouse hit
    if (mouseHit (x, y))
      updateButton (true, true, true);

    return true;
    }


  public boolean mouseUp (Event event, int x, int y)
    {
    // set action flag
    boolean action = isMouseCapture && mouseHit (x, y);
    
    // update button
    updateButton (mouseHit (x, y), false, false);
    
    // start timer for activation
    if (action)
      if (timer == null)
        {
        timer = new Thread (this);
        timer.start ();
        }
    
    return true;
    }


  public void start ()
    {
    // start loader
    if (!allLoaded)
      {
      loader = new Thread (this);
      loader.start ();
      }
    }


  public void stop ()
    {
    // stop loader
    if (loader != null)
      {
      loader.stop ();
      loader = null;
      }

    // stop timer
    if (timer != null)
      {
      timer.stop ();
      timer = null;
      }
    }


  public void run ()
    {
    if (Thread.currentThread () == loader)
      {
      // load all images
      MediaTracker tracker = new MediaTracker (this);

      backgroundImage = loadImage (tracker, getParameter ("BackgroundImage"), 1);
      if (backgroundImage != null)
        repaint ();

      normalImage = loadImage (tracker, getParameter ("NormalImage"), 2);
      if (normalImage != null)
        repaint ();

      mouseOverImage = loadImage (tracker, getParameter ("MouseOverImage"), 3);
      if ((normalImage == null) && (mouseOverImage != null))
        repaint ();

      mouseDownImage = loadImage (tracker, getParameter ("MouseDownImage"), 4);

      // load audio clip
      try
        {
        StringTokenizer tokenizer = new StringTokenizer (getParameter ("MouseDownSound"), ",");
        
        // get the audio clip
        mouseDownSound = getAudioClip (getCodeBase (), tokenizer.nextToken ().trim ());
        
        // check for the windows bug
        if (tokenizer.nextToken ().trim ().equalsIgnoreCase ("safety") &&
            (System.getProperty ("os.name").equalsIgnoreCase ("Windows 95") || 
             System.getProperty ("os.name").equalsIgnoreCase ("Windows 98")))
          mouseDownSound = null;
        }
      catch (Exception exception) {}
      
      allLoaded = true;
      }

    if (Thread.currentThread () == timer)
      {
      // wait a moment
      try 
        {
        timer.sleep (linkDelay);
        }
      catch (InterruptedException exception) {}

      // activate the link
      activateLink ();
      }
    }


  private Image loadImage (MediaTracker tracker, String name, int identifier)
    {
    // load an image
    if (name != null)
      {
      Image image = getImage (getCodeBase (), name);
      tracker.addImage (image, identifier);
      try 
        {
        tracker.waitForID (identifier);
        if (!tracker.isErrorID (identifier))
          return image;
        }
      catch (InterruptedException exception) {}
      }

    return null;
    }


  private void activateLink ()
    {
    // clear timer
    timer = null;

    // handle linked applet
    if (linkApplet != null)
      {
      Applet target = getAppletContext ().getApplet (linkApplet);
      if (target != null)
        target.handleEvent (new Event (this, Event.ACTION_EVENT, getParameter ("Name")));
      }

    // show linked document
    if (linkUrl != null)
      {
      try 
        {
        URL url = new URL (getCodeBase (), linkUrl);
        if (linkTarget == null)
          getAppletContext ().showDocument (url);
        else
          getAppletContext ().showDocument (url, linkTarget);
        }
        catch (MalformedURLException exception) {}
      }
    }
    
  
  }