import java.awt.*;
import java.util.Vector;
import java.applet.*;
import java.net.URL;

import WordBubble;

/**
 * Display an image with word bubbles that change
 * periodically and/or randomly.  This applet uses the helper class
 * WordBubble to draw the bubbles.  This applet accepts a large number
 * of HTML parameter arguments, to help make it more
 * flexible.  Note that this applet is deliberately
 * coded with very few defensive measures against
 * user error, in an effort to keep it small!
 *
 * Here are the parameters:
 *
 *      imageURL	absolute URL for image to display (no scaling!)
 *
 *	cycles		how many messages to show, 0 for
 *			running forever.
 *
 *	runmessage	message to showStatus during running
 *
 *	cycleTimeMin	minimum show time in seconds or millis
 *	
 *	cycleTimeMax	maximum show time in seconds or millis
 *
 *	spaceTimeMin	min time between shows in seconds or millis
 *			
 *	spaceTimeMax	max time between shows in seconds or millis
 *	
 *	fontName	name of font to use for messages,
 *			default is "Helvetica"
 *
 *	defFontSize	default size of font to use,
 *			default is 10.
 *
 *	randomize	whether to randomize selection 
 *			of point pairs and messages, or
 *			go in order.
 *	
 *	wordbackground	hex specification for background color
 *			(default is white)
 *
 *	background	hex specification for background of 
 *			whole applet. (default is 0xC0C0C0)
 *			
 *
 *	wordforeground	hex specification for line/text color
 *			(default is black)
 *
 *	endpointN	end point no. N
 *			(a point in the coordinate space of the
 *			 image where the end of a word bubble
 *			 should go.)
 *
 *	centerpointN	center point no. N
 *			(a point in the coordinate space of the
 *			 image where the center of a word bubble
 *			 should go.)
 *
 *	messageN	message text no. N
 *	
 *	sizeN		font size for message text no. N
 *			(if not given, then use default font)
 *
 *
 *  In this version, the image is always positioned to the south
 *  center of the available applet extent.
 */

public class TalkImageApplet extends Applet implements Runnable
{
  String imageURLString;
  URL imageURL;
  Image image;
  int cycles;
  int cycleMinMillis;
  int cycleMaxMillis;
  int spaceMinMillis;
  int spaceMaxMillis;
  String fontName;
  String runmessage;
  Font defaultFont;
  int defFontSize;
  boolean randomize;
  Color bubbleBackground;
  Color bubbleForeground;

  Vector endpoints;
  Vector centerpoints;
  Vector wordbubbles;
  Vector fontSizes;

  Rectangle imageRect;

  WordBubble currentbubble = null;
  int currentMessageIndex = 0;
  Thread animator = null;
  MediaTracker mt1;

  protected int randint(int lo, int hi) {
    if (lo == hi)
      return lo;
    else
      return lo + (int)(Math.floor(Math.random() * (double)(hi - lo)));
  }
  
  /**
   * initialize this darn applet, including calling readParams
   * to set up the parameters.  This does NOT start the animation
   * thread.
   */
  public void init() {
    /* set initial values */
    imageURLString = null;
    imageURL = null;
    image = null;
    cycles = 0;
    cycleMinMillis = 1500;
    cycleMaxMillis = 5000;
    spaceMinMillis = 250;
    spaceMaxMillis = 1500;

    fontName = "Helvetica";
    defFontSize = 10;
    defaultFont= null;
    randomize = true;
    
    bubbleBackground = Color.white;
    bubbleForeground = Color.black;

    endpoints = new Vector();
    centerpoints = new Vector();
    wordbubbles = new Vector();
    fontSizes = new Vector();

    currentbubble = null;
    currentMessageIndex = -1;
    animator = null;
    imageRect = null;
    
    /* read the parameters */
    readParams();

    /* load the image */
    try {
      imageURL = new URL(imageURLString);
      image = getImage(imageURL);
      if (image == null) { System.out.println("\nCouldn't load image!" + imageURL); }
      mt1 = new MediaTracker(this);
      mt1.addImage(image, 1);
      if (mt1.isErrorAny()) { System.out.println("Media error occurred 1."); image=null; }
      mt1.waitForAll();
      if (mt1.isErrorAny()) { System.out.println("Media error occurred 2."); image = null; }
    }
    catch (Exception e) { 
      mt1 = null; System.out.println("Could not do image!"); 
      image = null;
    }

    /* do the default font */
    defaultFont = new Font(fontName, Font.PLAIN, defFontSize);
    System.out.println("Font itself is " + defaultFont);

    /* show the applet */
    show();
  }

  /**
   * This waits for the image to load, then starts the
   * animator thread.
   */
  public void start() {
    System.out.println("In start...");

    /* configure all the WordBubble objects */
    int ix;
    for(ix = 0; ix < wordbubbles.size(); ix++) {
      WordBubble w1 = (WordBubble)(wordbubbles.elementAt(ix));
      w1.setParent(this);
      w1.setTextFont(defaultFont);
      if (fontSizes.elementAt(ix) != null) {
	int fs1 = Integer.parseInt((String)(fontSizes.elementAt(ix)));
	if (fs1 > 2 && fs1 < 40) {
	  Font f1 = new Font(fontName, Font.PLAIN, fs1);
	  w1.setTextFont(f1);
	  // System.out.println("Font for " + ix + " is " + f1);
	}
      }
      w1.background = bubbleBackground;
      w1.foreground = bubbleForeground;
    }


    if (image != null) {
      imageRect = new Rectangle(0,0, image.getWidth(this), image.getHeight(this));
      imageRect.translate((size().width - imageRect.width)/2,
			  (size().height - imageRect.height));
    }
    repaint();

    animator = new Thread(this);
    animator.start();
    showStatus(runmessage);
  }

  /**
   * stop the animator thread
   */
  public void stop() {
    // System.out.println("In stop.");
    if (animator != null && animator.isAlive()) {
      animator.stop();
    }
    animator = null;
    showStatus("Applet done.");
  }

  /**
   * return some info about this little applet 
   */
  public String getAppletInfo() {
    return "Talking Image Applet v1.0 (28 Dec 1997), by Neal Ziring";
  }


  /**
   * on destroy remember to stop the thread
   * if it still exists.
   */
  public void destroy() {
    if (animator != null) {
      animator.stop();
    }
  }

  /**
   * run the animation 
   */
  public void run() {
    Point p1, p2;
    Point endpnt, ctrpnt;
    int nextmsgindex;
    int nextptindex;

    // System.out.println("In run()");

    try {
      Thread.sleep(spaceMaxMillis);
    } catch (Exception e) { }

    Graphics g1 = getGraphics();

    if (image != null)
    do {
      if (randomize) {
	int tmpindex;
	nextptindex = randint(0, endpoints.size());
	do {
	  nextmsgindex = randint(0, wordbubbles.size());
	} while (currentMessageIndex == nextmsgindex); 	    
      }
      else {
	nextptindex = (currentMessageIndex + 1) % wordbubbles.size();
	nextmsgindex = (currentMessageIndex + 1) % wordbubbles.size();
      }

      p1 = (Point)(endpoints.elementAt(nextptindex % endpoints.size()));
      if (p1 == null) p1 = (Point)(endpoints.elementAt(0));
      endpnt = new Point(p1.x + imageRect.x, p1.y + imageRect.y);
      
      p2 = (Point)(centerpoints.elementAt(nextptindex % centerpoints.size()));
      if (p2 == null) p2 = (Point)(centerpoints.elementAt(0));
      ctrpnt = new Point(p2.x + imageRect.x, p2.y + imageRect.y);

      // System.out.println("In loop, p1="+endpnt+" and p2="+ctrpnt+" and nextmsgindex=" + nextmsgindex);

      currentbubble = (WordBubble)(wordbubbles.elementAt(nextmsgindex));
      currentMessageIndex = nextmsgindex;
      currentbubble.setEndpoint(endpnt);
      currentbubble.setCenterpoint(ctrpnt);
      currentbubble.draw(g1);
      try {
	Thread.sleep(randint(cycleMinMillis,cycleMaxMillis));
      } catch (Exception e) { }

      currentbubble.undraw(g1);
      currentbubble = null;
      try {
	Thread.sleep(randint(spaceMinMillis,spaceMaxMillis));
      } catch (Exception e) { }
      cycles -= 1;
    } while (cycles != 0);

    g1.dispose();
  }

  /**
   * no need for anything special, just paint
   */
  public void update(Graphics g) {
    paint(g);
  }

  /**
   * paint the image and current bubble if necessary
   */
  public void paint(Graphics g) {
    //    System.out.println("Hey, in paint, size = " + size() + " and color is " + getBackground());
    int wid = size().width;
    int hgt = size().height;
    g.setColor(getBackground());
    /* avoid image flash by only drawing around the sides */
    g.fillRect(0,0, wid, imageRect.y);
    g.fillRect(0,0, imageRect.x, hgt);
    if (image != null ) {
      g.fillRect(0,0, wid, imageRect.y);
      g.fillRect(0,0, imageRect.x, hgt);
      g.fillRect(imageRect.x + imageRect.width, 
		 imageRect.y, wid, hgt);
      g.drawImage(image, imageRect.x, imageRect.y, this);
      if (currentbubble != null) {
	currentbubble.draw(g);
      }
    }
    else { g.fillRect(0,0, wid, hgt); }
  }

  public String getparm(String parm, String def) {
    String ret = getParameter(parm);
    if (ret != null) 
      return ret;
    else
      return def;
  }

  
  /**
   * read our many parameters and create our wordbubble objects
   */
  void readParams() {
    // read unnumered params first
    imageURLString = getParameter("imageURL");
    cycles = Integer.parseInt(getparm("cycles","0"));
    runmessage = getparm("runmessage", getAppletInfo());
    cycleMinMillis = Integer.parseInt(getparm("cycleTimeMin","1500"));
    if (cycleMinMillis < 50) cycleMinMillis *= 1000;
    cycleMaxMillis = Integer.parseInt(getparm("cycleTimeMax","10000"));
    if (cycleMaxMillis < 250) cycleMaxMillis *= 1000;
    spaceMinMillis = Integer.parseInt(getparm("spaceTimeMin","250"));
    if (spaceMinMillis < 50) spaceMinMillis *= 1000;
    spaceMaxMillis = Integer.parseInt(getparm("spaceTimeMax","1500"));
    if (spaceMaxMillis < 150) spaceMaxMillis *= 1000;

    fontName = getparm("fontName", "Helvetica");
    randomize = Boolean.valueOf(getparm("randomize", "true")).booleanValue();
    defFontSize = Integer.parseInt(getparm("defFontSize", "11"));
    bubbleBackground = new Color(Integer.parseInt(getparm("wordbackground","ffffff"),16));
    bubbleForeground = new Color(Integer.parseInt(getparm("wordforeground","0"),16));
    setBackground(new Color(Integer.parseInt(getparm("background","C0C0C0"),16)));
    //    System.out.println("New bg color is " + getBackground());

    /* now handled the numbered ones */
    boolean gotone = true;
    String tmpval, tmpparm;
    int ix;
    for(ix = 0; gotone; ix++) {
      gotone = false;
      tmpparm = "message" + ix;
      tmpval = getParameter(tmpparm);
      if (tmpval != null) {
	wordbubbles.addElement(new WordBubble(tmpval,this));
	gotone = true;
	tmpparm = "size" + ix;
	tmpval = getParameter(tmpparm);
	if (tmpval != null)
	  fontSizes.addElement(tmpval);
	else 
	  fontSizes.addElement("0");
      }

      tmpparm = "endpoint" + ix;
      tmpval = getParameter(tmpparm);
      if (tmpval != null) {
	endpoints.addElement(pointFromString(tmpval));
	gotone = true;
      }
      tmpparm = "centerpoint" + ix;
      tmpval = getParameter(tmpparm);
      if (tmpval != null) {
	centerpoints.addElement(pointFromString(tmpval));
	gotone = true;
      }
    }

    return;
  }

  Point pointFromString(String str) {
    int xval = 0;
    int yval = 0;
    int cp = str.indexOf(',');
    if (cp > 0) {
      xval = Integer.parseInt(str.substring(0,cp).trim());
      yval = Integer.parseInt(str.substring(cp + 1).trim());
    }
    //    System.out.println("In pointFromString, str=" + str + " and xval=" + xval + " and yval=" + yval);
    return(new Point(xval, yval));
  }

  
}

