package NGP.Graphics;

/**
 * A graphical text object.  See the NGP tutorial for more on how to use
 * Fonts with this class.
 *
 * @author Matt Chotin (<a href="mailto:mhc@cs.brown.edu">mhc</a>)
 */

public class Text implements NGP.Graphic, NGP.Locatable, NGP.Colorable {

  /** Our location */
  private java.awt.Point _point;
  /** Our DrawingPanel */
  private NGP.Containers.DrawingPanel _dpanel;
  /** Our rotation */
  private double _rotationAngle;
  /** Our color */
  private java.awt.Color _color;
  /** The bounding rectangle */
  private java.awt.Rectangle _bounds;
  /** Indicate whether we should wrap or not */
  private boolean _wrapped = false;
  /** The String that the text should display */
  private String _string;
  /** The font to use */
  private java.awt.Font _font;

  /**
   * Create a Text object with the passed in DrawingPanel and use the passed
   * in String as its text.
   *
   * @param dp the DrawingPanel for this Text
   * @param string the String to use
   */
  public Text(NGP.Containers.DrawingPanel dp, String string) {
    _dpanel = dp;
    _string = string;
    _color = DEFAULT_GRAY;
    if (_dpanel.getGraphics() != null)
      _font = _dpanel.getGraphics().getFont();
    else _font = new java.awt.Font("SansSerif",
                                    java.awt.Font.PLAIN, 12);
    _point = new java.awt.Point(0, 0);
    if (_dpanel.getGraphics() != null) this.createBounds();
    this.show();
  }

  /**
   * Set the String that this Text should use
   *
   * @param string the new String for this Text
   */
  public void setString(String string) {
    _string = string;
    if (_dpanel.getGraphics() != null) {
      this.createBounds();
      _dpanel.repaint(this.getBounds());
    }
    else _dpanel.repaint();
  }

  /**
   * Get the String that this Text is using
   *
   * @return the String that this text is using
   */
  public String getString() {
    return _string;
  }

  /**
   * Hide the Text so it won't be drawn anymore (NGP will lose it's reference
   * to it).
   */
  public void hide() {
    if (_dpanel != null) {
      _dpanel.removeGraphic(this);
      _dpanel.repaint();
    }
  }

  /**
   * Show the Text so it will be drawn (NGP now has a reference to it).
   */
  public void show() {
    _dpanel.addGraphic(this);
    _dpanel.repaint();
  }

  /**
   * Normal users need not use this!
   *<p>
   * Set the color of the text, then draw it.
   *
   * @param g the Graphics2D passed from the DrawingPanel
   */
  public void paint(java.awt.Graphics2D g) {
    if (_bounds == null) this.createBounds();
    g.setPaint(_color);
    g.setFont(_font);
    g.setRenderingHint(java.awt.RenderingHints.KEY_TEXT_ANTIALIASING,
           java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    if (_rotationAngle != 0) {
      double centerX = _bounds.getCenterX();
      double centerY = _bounds.getCenterY();
      g.rotate(_rotationAngle, centerX, centerY);
      g.drawString(_string, _point.x, _point.y);
      g.rotate(_rotationAngle*-1, centerX, centerY);
    }
    else g.drawString(_string, _point.x, _point.y);
  }

  /** Set this Text so that it will always display in its DrawingPanel */
  public void wrap() {
    _wrapped = true;
  }

  /**
   * Set this Text so that it will does not necessarily display in its
   * DrawingPanel
   */
  public void unwrap() {
    _wrapped = false;
  }

  /**
   * Set the font of this Text object.  See the java.awt.Font documentation
   * for more information, and check out the NGP tutorial for even more.
   *
   * @param font the Font that this Text should use
   */
  public void setFont(java.awt.Font font) {
    _font = font;
    if (_bounds != null) {
      this.createBounds();
      //_dpanel.repaint(this.getBounds());

      java.awt.Graphics g = _dpanel.getGraphics();
      g.setFont(_font);
      java.awt.FontMetrics fm = g.getFontMetrics();
      int descent = fm.getMaxDescent();
      java.awt.Rectangle r = new java.awt.Rectangle(this.getBounds());
      r.grow(5,descent);
      
      _dpanel.repaint(r);


    }
    else _dpanel.repaint();
  }

  /**
   * Get the font that this Text is using.
   *
   * @return the Font that this text is using
   */
  public java.awt.Font getFont() {
    return _font;
  }

  /**
   * Set the color of this Text object.
   *
   * @param color the new Color for this Text
   */
  public void setColor(java.awt.Color color) {
    _color = color;
    if (_bounds != null) {
      java.awt.Graphics g = _dpanel.getGraphics();
      g.setFont(_font);
      java.awt.FontMetrics fm = g.getFontMetrics();
      int descent = fm.getMaxDescent();
      java.awt.Rectangle r = new java.awt.Rectangle(this.getBounds());
      r.grow(5,descent);
      
      _dpanel.repaint(r);
    }
    else _dpanel.repaint();
  }

  /**
   * Get the color of this Text object
   *
   * @return the Color of this Text
   */
  public java.awt.Color getColor() {
    return _color;
  }

  /**
   * Set the location for this Text object
   *
   * @param p the new Point for this Text
   */
  public void setLocation(java.awt.Point p) {
    _point = p;
    if (_wrapped) {
          java.awt.Dimension panelSize = _dpanel.getDimension();
          int x = _point.x%panelSize.width;
          int y = _point.y%panelSize.height;
          if (x < 0) x += panelSize.width;
          if (y < 0) y += panelSize.height;
          _point.x = x;
          _point.y = y;
    }
    if (_bounds != null) {
      java.awt.Rectangle oldBounds = this.getBounds();
      java.awt.Rectangle oldR = new java.awt.Rectangle(oldBounds);
      
      _bounds.setLocation(new java.awt.Point((int)_point.getX(),
					     (int)(_point.getY() - 
						   oldBounds.getHeight())));
      //_bounds.setLocation(_point);

      java.awt.Graphics g = _dpanel.getGraphics();
      g.setFont(_font);
      java.awt.FontMetrics fm = g.getFontMetrics();
      int descent = fm.getMaxDescent();

      java.awt.Rectangle r = new java.awt.Rectangle(this.getBounds());
      r.grow(5,descent);

      oldR.grow(5,descent);
      
      _dpanel.repaint(r.union(oldR));
      //_dpanel.repaint(this.getBounds().union(oldBounds));
    }
    else {
      _dpanel.repaint();
    }
  }

  /**
   * Get the current location of this Text
   *
   * @return the Point representing the current location
   */
  public java.awt.Point getLocation() {
    return _point;
  }

  /**
   * Set the rotation for this Text
   *
   * @param degrees the degrees (clockwise) that this should rotate
   */
  public void setRotation(int degrees) {
    if (_bounds != null) {
      java.awt.Rectangle oldBounds = this.getBounds();
      java.awt.Rectangle oldR = new java.awt.Rectangle(oldBounds);

      _rotationAngle = degrees * (Math.PI/180.0);

      java.awt.Graphics g = _dpanel.getGraphics();
      g.setFont(_font);
      java.awt.FontMetrics fm = g.getFontMetrics();
      int descent = fm.getMaxDescent();

      java.awt.Rectangle r = new java.awt.Rectangle(this.getBounds());
      r.grow(5,descent);

      oldR.grow(5,descent);
      
      _dpanel.repaint(r.union(oldR));
      //_dpanel.repaint(this.getBounds().union(oldBounds));
    }
    else {
      _rotationAngle = degrees * (Math.PI/180.0);

      _dpanel.repaint();
    }
  }

  /**
   * Get the rotation for this Text
   *
   * @return the degrees (clockwise) that this should rotate
   */
  public int getRotation() {
    return (int)(_rotationAngle * (180/Math.PI));
  }

  /**
   * Return the rectangle that represents the bounds of this Text
   *
   * @return the Rectangle that is the bounds of this Text
   */
  public java.awt.Rectangle getBounds() {
    if (_bounds == null)
      return new java.awt.Rectangle(new java.awt.Point(0,0),
                                    new java.awt.Dimension(_dpanel.getDimension()));
    //else
    java.awt.Rectangle r = _bounds;
    if (0 != _rotationAngle) {
      double x = r.getCenterX();
      double y = r.getCenterY();
      java.awt.geom.AffineTransform trans =
        java.awt.geom.AffineTransform.getRotateInstance(_rotationAngle, x, y);
      java.awt.Shape s = trans.createTransformedShape(_bounds);
      r = s.getBounds();
    }
    return r;
  }

  /**
   * Return whether this Text contains the Point passed in
   *
   * @param p the Point to check for containment
   * @return <code>true</code> if it is contained, <code>false</code> otherwise
   */
  public boolean contains(java.awt.Point p) {
    return this.getBounds().contains(p);
  }

  /**
   * Return whether this Text intersects with the passed in Graphic
   *
   * @param g the Graphic we should test for intersection
   * @return <code>true</code> if they do intersect, <code>false</code>
   * otherwise
   */
  public boolean intersects(NGP.Graphic g) {
    return this.getBounds().intersects(g.getBounds());
  }

  /**
   * Set the DrawingPanel of this Text so that it appears somewhere else.
   *
   * @param dp the new DrawingPanel for the Text
   */
  public void setDrawingPanel(NGP.Containers.DrawingPanel dp) {
    this.hide();
    _dpanel = dp;
    this.show();
  }

  /**
   * Return the DrawingPanel for this Shape
   *
   * @return the DrawingPanel for this Shape
   */
  public NGP.Containers.DrawingPanel getDrawingPanel() {
    return _dpanel;
  }

  /**
   * Create the bounding rectangle for this text object.  This method should
   * be called whenever the location or the font characteristics of this Text
   * have changed.
   */
  protected void createBounds() {
    java.awt.Graphics g = _dpanel.getGraphics();
    g.setFont(_font);
    java.awt.FontMetrics fm = g.getFontMetrics();
    int stringwidth = fm.stringWidth(_string);
    int stringheight = (int)(fm.getStringBounds(_string, g).getHeight());
    java.awt.Point p = new java.awt.Point(_point.x, _point.y - stringheight);
    _bounds = new java.awt.Rectangle(p,
             new java.awt.Dimension(stringwidth,
                  stringheight));
  }

  /** Override to do something useful. */
  public void react() { }

  /**
   * Called when the Panel detects that the mouse was clicked.
   * If the click is within this Text, {@link #react() react}
   * will be called.
   */
  public void mouseClicked(java.awt.event.MouseEvent e) {
    if (this.contains(e.getPoint())) this.react();
  }

  /** Called when the Panel detects that the mouse entered.  Does nothing. */
  public void mouseEntered(java.awt.event.MouseEvent e) { }
  /** Called when the Panel detects that the mouse exited.  Does nothing. */
  public void mouseExited(java.awt.event.MouseEvent e) { }
  /** Called when the Panel detects that the mouse was pressed. Does nothing.*/
  public void mousePressed(java.awt.event.MouseEvent e) { }
  /**Called when the Panel detects that the mouse was released. Does nothing.*/
  public void mouseReleased(java.awt.event.MouseEvent e) { }

}
