import java.awt.*;
import java.util.Vector;

/**
 * A specialized graphical object class to be used for
 * drawing "word bubbles" over images.  The word bubble
 * object has the following state data:
 * <ul>
 * <li>a text string (fixed, break lines with |)
 * <li>a drawing font (usually set dynamically)
 * <li>a spike start point (usually set dynamically)
 * <li>a bubble center point (usually set dynamically)
 * <li>a foreground color
 * <li>a background color
 * </ul>
 *
 * The point of this class is to be able to draw a word
 * bubble over an image fairly simply.
 */

public class WordBubble {
  /**
   * character used to split lines
   */
  public static final char SPLIT_CHAR = '|';

  /**
   * arc width of our rounded corners
   */
  public static final int HORIZ_ARC_EXTENT = 12;

  /**
   * arc height of our rounded corners
   */
  public static final int VERT_ARC_EXTENT = 10;

  private Component parent;
  private String [] lines;
  Color foreground;
  Color background;
  private Font textfont;
  private Point endpoint;
  private Point centerpoint;
  private Rectangle bubblerect;
  private FontMetrics textfontmetrics;

  /**
   * create a WordBubble with particular text
   *
   * @param txt string to show in the bubble
   */
  public WordBubble(String txt, Component par) {
    Vector v1 = new Vector();
    int ix, s1, s2;

    /* cache parent for later */
    parent = par;
    /* take care of lines member data first */
    for(s2 = 0, s1 = txt.indexOf(SPLIT_CHAR, s2);
	s1 >= 0;
	s2 = s1 + 1, s1 = txt.indexOf(SPLIT_CHAR, s2))
      {
	v1.addElement((txt.substring(s2,s1)).trim());
      }
    v1.addElement((txt.substring(s2)).trim());
    lines = new String[v1.size()];
    for(ix = 0; ix < lines.length; ix++) { lines[ix] = (String)(v1.elementAt(ix));}

    /* now set others to default values */
    background = Color.white;
    foreground = Color.black;

    textfont = null;
    endpoint = null;
    centerpoint = null;
    bubblerect = null;
    textfontmetrics = null;
  }

  /**
   * set parent component
   */
  public void setParent(Component par)
  {
    parent = par;
  }

  /**
   * set text font
   *
   * @param tf new text font
   */
  public void setTextFont(Font tf) 
  { 
    textfont = tf;
    bubblerect = null;
    textfontmetrics = null;
    return;
  }

  
  /**
   * set spike endpoint 
   */
  public void setEndpoint(Point pt)
  {
    endpoint = pt;
    return;
  }

  /**
   * set rectangle center point
   */
  public void setCenterpoint(Point pt)
  {
    centerpoint = pt;
    bubblerect = null;
    return;
  }


  private void ensureFontMetrics(Graphics g) {
    if (textfontmetrics == null) {
      textfontmetrics = g.getFontMetrics(textfont);
    }
  }

  /**
   * recompute the bubble rect based on the center point, text,
   * text font, boundaries of the parent component, etc.
   */
  public void recomputeRect(Graphics g) 
  {
    Dimension bsiz = new Dimension();
    int ix, tmpw;

    ensureFontMetrics(g);
    bsiz.height = lines.length * textfontmetrics.getHeight();
    bsiz.width = 0;
    for(ix = 0; ix < lines.length; ix++) {
      tmpw = textfontmetrics.stringWidth(lines[ix]);
      if (tmpw > bsiz.width) { bsiz.width = tmpw; }
    }
    bsiz.width += (3 * HORIZ_ARC_EXTENT) / 2;
    bsiz.height += (3 * VERT_ARC_EXTENT) / 2;
    bubblerect = new Rectangle(centerpoint.x - bsiz.width/2,
			       centerpoint.y - bsiz.height/2,
			       bsiz.width, bsiz.height);
    
    int xcorr = 0;
    int ycorr = 0;
    Dimension tmpsiz = parent.size();

    if (bubblerect.x < 0) { 
      xcorr = -(bubblerect.x); 
    }
    else if (bubblerect.x + bsiz.width > (tmpsiz.width - 1)) {
      xcorr = (tmpsiz.width - 1) - (bubblerect.x + bsiz.width);
    }
    if (bubblerect.y < 0) { 
      ycorr = -(bubblerect.y); 
    }
    else if (bubblerect.y + bsiz.height > (tmpsiz.height - 1)) {
      ycorr = (tmpsiz.height - 1) - (bubblerect.y + bsiz.height);
    }
    bubblerect.translate(xcorr,ycorr);

    centerpoint.x = (bubblerect.x + bubblerect.width / 2);
    centerpoint.y = (bubblerect.y + bubblerect.height / 2);
  }

  /**
   * Draw the bubble at its various point
   */
  public synchronized void draw(Graphics g) {
    if (bubblerect == null) recomputeRect(g);

    // step 1, draw the bubble
    //    System.out.println("Drawing '" + lines[0] + "' at " + bubblerect);
    g.setColor(background);
    g.fillRoundRect(bubblerect.x, bubblerect.y,
		    bubblerect.width, bubblerect.height,
		    HORIZ_ARC_EXTENT, VERT_ARC_EXTENT);
    g.setColor(foreground);
    g.drawRoundRect(bubblerect.x, bubblerect.y,
		    bubblerect.width, bubblerect.height,
		    HORIZ_ARC_EXTENT, VERT_ARC_EXTENT);
    g.setColor(background);
    Polygon p1 = new Polygon();
    int y1 = ((endpoint.y > centerpoint.y)?
	      ((centerpoint.y + bubblerect.height / 2) - 1):
	      ((centerpoint.y - bubblerect.height / 2) + 1));
    p1.addPoint(centerpoint.x - HORIZ_ARC_EXTENT, y1);
    p1.addPoint(endpoint.x, endpoint.y);
    p1.addPoint(centerpoint.x + HORIZ_ARC_EXTENT, y1);
    g.fillPolygon(p1);
    g.setColor(foreground);
    g.drawLine(centerpoint.x - HORIZ_ARC_EXTENT, y1, endpoint.x, endpoint.y);
    g.drawLine(centerpoint.x + HORIZ_ARC_EXTENT, y1, endpoint.x, endpoint.y);

    int ix;
    int tmpwid;
    int yval;
    int xval;

    yval = (centerpoint.y - (bubblerect.height / 2)) + ((3 * VERT_ARC_EXTENT) / 4);
    yval += textfontmetrics.getMaxAscent();

    //    System.out.println("About to draw, bubblerect=" + bubblerect + " and yval=" + yval);
    for(ix = 0; ix < lines.length; ix++) {
      tmpwid = textfontmetrics.stringWidth(lines[ix]);
      xval = bubblerect.x + ((bubblerect.width - tmpwid) / 2);
      //      System.out.println("       xval for line " + ix + " = " + xval);
      g.setFont(textfont);
      g.drawString(lines[ix], xval, yval);
      yval += textfontmetrics.getHeight();
    }

    return;
  }

  /**
   * Undraw the bubble by clearing the parent in the
   * appropriate area and asking for a redraw of the
   * necessary area.
   */
  public void undraw(Graphics g) {
    Rectangle clearrect;
    clearrect = new Rectangle(bubblerect.x, bubblerect.y, 
			      bubblerect.width, bubblerect.height);
    clearrect.add(endpoint);
    clearrect.grow(1,1);
    parent.repaint(clearrect.x, clearrect.y, clearrect.width, clearrect.height);
  }

}

    

    
