/** (C) 1999 World Xiangqi League, Confidential, All Rights Reserved */

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

class ImageButton extends Canvas implements Runnable {
  private static Club club;
  // ToolTips
  private static Thread thread = null;
  private static Vector buttons = new Vector(20,20);
  
  private String name;
  private String tip;
  private Image image;		// button's own image else imageStrip
  private int index;		// index into imageStrip
  private boolean pushed=false;
  private Color outEdge, topLeft, botRight;

  private static Image imageStrip;
  private static final String[] STRIP_ELEMENTS = {
    "first", "prev", "next", "last", "shout",
    "home", "who", "new", "goto", "save", "web"
  };
  private static final int STRIP_W = 32; // width of each button
  //private static final int STRIP_H = 16;
  
  public String toString () { // for debugging
    return "<"+name+".gif "+
      ((image==null) ? "null" : 
       image.getWidth(null)+"x"+image.getHeight(null)) +">";
  }
  public Dimension preferredSize() {
    // BUG: don't allocate new Dimension each time
    return new Dimension(32,28);
  }
  public Dimension minimumSize() {
    return preferredSize();
  }

  // -----------------------------------------------------------------
  /** Called by Club.buildGUI() to load the button strip. */
  public static void loadToolbar(Club c) {
    club = c;
    imageStrip = load("toolbar.gif",club,true);
  }

  /** When someone creates a new ImageButton we'll first
   *  look up its name to see if it is in imageStrip.
   */
  private int stripIndex(String name) {
    for (int i = 0; i<STRIP_ELEMENTS.length; i++)
      if (STRIP_ELEMENTS[i].equals(name))
	return i;
    return -1;
  }
  
  /** Constructor for an ImageButton */
  ImageButton(String name, String tip) {
    this.name = name;
    this.tip = tip;

    if (thread==null) {
      thread = new Thread(club.threadGroup, this, "ImageButton ToolTips");
      //thread.setDaemon(true);
      thread.start();
    }
    buttons.addElement(this);
    
    outEdge = topLeft = botRight = getBackground();
    index = stripIndex(name);
    if (index>=0) {
      // Don't load it; we'll pull from strip at paint time
      //Club.debug("ImageButton load from strip: "+name+", at "+index);
      image = imageStrip;
    } else {
      String path="buttons/"+name+".gif";
      this.image = load(path,this,true);
      //Club.debugImage("ImageButton "+path,image);
    }
    // Size this canvas:
    resize(32,28); //hack
    //resize(max(32,image.getWidth(null)),
    //max(24,image.getHeight(null)));
  }

  // -----------------------------------------------------------------

  /** Load image from PATH, observed by component OBSERVER.
   * If WAIT is true, block until load is complete.
   * PATH is relative from images/ directory on server.
   * @return null on failure.
   */
  public static Image load(String path, Component observer, boolean wait) {
    /// Should load all piece/button/etc images from one file!
    if (!path.startsWith("http:"))
      path=Login.serverLocal +"/images/" +path;
    //Club.debug("ImageButton:load "+path);
    URL url = null;
    try { url=new URL(path); }
    catch (MalformedURLException e) { Club.warning(e,null); return null; }
    //Club.debug("ImageButton:load "+url);
    /*
    Toolkit tk = Toolkit.getDefaultToolkit();    
    Image image = tk.getImage(url);
    */
    Image image = observer.getToolkit().getImage(url);
    if (image==null) {
      Club.debug("ImageButton:load() error for "+url);
      return null;
    }
    if (wait)
      waitFor(image,observer);
    // Else each g.drawImage() will return false and more update()
    //   calls will be generated to paint while image comes in.
    // E.g., wait for buttons but not chess pieces.
    return image;
  }

  /** Create a MediaTracker for IMAGE on OBSERVER,
   * load the image, and wait until load is complete.
   */
  public static boolean waitFor(Image image,Component observer) {
    if (observer==null)
      return true;
    MediaTracker tracker = new MediaTracker(observer);
    tracker.addImage(image,0);
    try { tracker.waitForID(0); }
    catch (InterruptedException e) {
      Club.warning(e,"waitFor() error."); 
      return false;
    }
    return true;
  }

  // -----------------------------------------------------------------
  // BUTTON PAINTING / HIGHLIGHTING
  //
  // Netscape 4 toolbar button highlighting model:
  // - mouse out:     no edge; color image
  // - mouse inside:  white / gray plus outside black; highlight image
  // - mouse pressed: gray / white plus outside black;
  // Microsoft Internet Explorer 3 toolbar button highlighting model:
  // - mouse out:     no edge; b&w image 
  // - mouse in:      white / gray (one pixel); color image
  // - mouse pressed: gray / white (one pixel);
  // Since we don't yet have different images for each button,
  //  we'll go with the stronger Netscape style highlighting.
  public boolean handleEvent(Event event) {
    //Club.debugEvent("ImageButton",event);
    ImageButton.clearEdge(this);
    switch (event.id) {
      case Event.MOUSE_ENTER:
	club.cursor("ImageButton "+name,Frame.DEFAULT_CURSOR);
        pushed = false;
        outEdge  = Color.black;
        topLeft  = Color.white;
        botRight = Color.gray;  
        repaint();
	arm(event);
        break;
      case Event.MOUSE_EXIT:
      case Event.LOST_FOCUS:
	//club.cursorReset("ImageButton "+name);
        pushed = false;
        outEdge = topLeft = botRight = getBackground();
        repaint();
	disarm(event);
        break;
      case Event.MOUSE_DOWN:
	club.state(null);
        pushed = true;
        outEdge  = Color.black;
        topLeft  = Color.gray;
        botRight = Color.white;
        repaint();
	disarm(event);
        break;
      case Event.MOUSE_UP:
        if (pushed)
          deliverEvent(new Event(this, Event.ACTION_EVENT, name));
        pushed = false;
	if (inside(event.x,event.y)) {
	  outEdge  = Color.black;
	  topLeft  = Color.white;
	  botRight = Color.gray;
	} else {
	  outEdge = topLeft = botRight = getBackground();	  
	}
        repaint();
	disarm(event);
        break;
      default:
	//Club.debugEvent("ImageButton",event);
	if (!inside(event.x,event.y)) {
	  pushed = false;
	  outEdge = topLeft = botRight = getBackground();	  	  
	  repaint();
	  disarm(event);
	}
	break;
    }
    return super.handleEvent(event);
  }

  public void update(Graphics g) { paint(g); }

  public void paint(Graphics g) {
    int dw = size().width, dh = size().height;
    int w = image.getWidth(this), h = image.getHeight(this);
    int x = (dw - w) / 2;
    int y = (dh - h) / 2;
    g.setColor(getBackground());
    g.fillRect(x,y, w+2,h+2);
    g.setColor(outEdge);
    g.drawLine(0,0, dw-1,0);
    g.drawLine(0,0, 0,dh-1);
    g.drawLine(0,dh-1, dw-1,dh-1);
    g.drawLine(dw-1,dh-1, dw-1,0);
    g.setColor(topLeft);
    g.drawLine(1,1, dw-2,1);
    g.drawLine(1,1, 1,dh-2);
    g.setColor(botRight);
    g.drawLine(dw-2,dh-2, dw-2,1);
    g.drawLine(1,dh-2, dw-2,dh-2);

    // first prev next last shout home new goto save web
    // 0     1    2    3    4     5    6   7    8    9
    // <<|   <    >    |>>  !
    //*2     3    4         (index)
    // 1     2    3    4    (index-1)   
    if (index>=0) { // draw from imageStrip
      g.clipRect(2,2, dw-4,dh);
      int hoffset = x+(pushed?1:-1) - (index-5)*STRIP_W;
      //Club.debug(name+" @"+index+" "+hoffset);
      g.drawImage(image, hoffset, y+(pushed?1:0), this);
    } else {
      Club.debug("ImageButton no strip");
      g.clipRect(2,2, dw-4,dh-4);
      g.drawImage(image, x+(pushed?1:-1),y+(pushed?1:-1), this);
    }
  }

  private int max(int a, int b) { return ((a>b) ? a : b); }
  private int min(int a, int b) { return ((a<b) ? a : b); }
  private int abs(int value) { return (value<0) ? -value : value; }

  // -----------------------------------------------------------------
  // TOOLTIPS
  
  private long timestamp = 0;

  // Bogus hack since click buttons sometimes miss MOUSE_EXIT.
  public static void clearEdge(ImageButton current) {
    Enumeration e = buttons.elements();
    while (e.hasMoreElements()) {
      ImageButton button = (ImageButton)e.nextElement();
      if (button!=current && button.isShowing() &&
	  button.outEdge==Color.black) {
	button.pushed = false;
        button.outEdge  = button.getBackground();
        button.topLeft  = button.getBackground();
        button.botRight = button.getBackground();
        button.repaint();
	button.disarm(null);
      }
    }
  }
  
  private void arm(Event event) { // MOUSE_ENTER
    timestamp = System.currentTimeMillis();
  }
  private void disarm(Event event) { // MOUSE_DOWN,UP,EXIT
    timestamp = 0;
  }
  
  public void run() {
    while (thread!=null) {
      try { thread.sleep(250); }
      catch (InterruptedException e) {}
      long now = System.currentTimeMillis();
      long last = 0;
      Enumeration e = buttons.elements();
      while (e.hasMoreElements()) {
	ImageButton button = (ImageButton)e.nextElement();
	if (button.isShowing() && (button.timestamp>0) && // armed/entered
	    (((now - last) < 1000) || ((now - button.timestamp) >= 1000))) {
	  club.state(button.tip);
	  last = now;
	  button.timestamp = 0;
	}
      }
    }
  }
}
