//
// TextControl.java
//

/*
VisAD system for interactive analysis and visualization of numerical
data.  Copyright (C) 1996 - 2002 Bill Hibbard, Curtis Rueden, Tom
Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
Tommy Jasmin.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/

package visad;

import java.awt.Font;
import visad.util.HersheyFont;
import java.text.*;
import java.rmi.*;

import visad.util.Util;

/**
   TextControl is the VisAD class for controlling Text display scalars.<P>
*/
public class TextControl extends Control {

  private Object font = null;

  // abcd 5 February 2001
  //private boolean center = false;

  private double size = 1.0;

  // WLH 31 May 2000
  // draw on sphere surface
  private boolean sphere = false;

  private NumberFormat format = null;

  // abcd 1 February 2001
  // Rotation, in degrees, clockwise along positive x axis
  private double rotation = 0.0;
  // SL  15 March 2003
  private double characterRotation = 0.0;   // characterRotation
  private double scale = 1.0;  // Scaling factor
  private double[] offset = new double[]{0.0, 0.0, 0.0};

  // WLH 6 Aug 2001
  private boolean autoSize = false;
  private ProjectionControlListener pcl = null;

  /**
   * A class to represent the different types of justification.
   * Use a class so the user can't just pass in an arbitrary integer
   *
   * @author abcd 5 February 2001
   */
  public static class Justification {
    private String name;

    /** Predefined value for left justification */
    public static final Justification LEFT = new Justification("Left");

    /** Predefined value for center justification */
    public static final Justification CENTER = new Justification("Center");

    /** Predefined value for right justification */
    public static final Justification RIGHT = new Justification("Right");

    /** Predefined value for top justification */
    public static final Justification TOP = new Justification("Top");

    /** Predefined value for bottom justification */
    public static final Justification BOTTOM = new Justification("Bottom");

    /**
     * Constructor - simply store the name
     */
    public Justification(String newName)
    {
      name = newName;
    }
  }
  private Justification justification = Justification.LEFT;
  private Justification verticalJustification = Justification.BOTTOM;

  public TextControl(DisplayImpl d) {
    super(d);
  }

  public void setAutoSize(boolean auto)
         throws VisADException {
    if (auto == autoSize) return;
    DisplayImpl display = getDisplay();
    DisplayRenderer dr = display.getDisplayRenderer();
    MouseBehavior mouse = dr.getMouseBehavior();
    ProjectionControl pc = display.getProjectionControl();
    if (auto) {
      pcl = new ProjectionControlListener(mouse, this, pc);
      pc.addControlListener(pcl);
    }
    else {
      pc.removeControlListener(pcl);
    }
    autoSize = auto;
    try {
      changeControl(true);
    }
    catch (RemoteException e) {
    }
  }

  public boolean getAutoSize() {
    return autoSize;
  }

  public void nullControl() {
    try {
      setAutoSize(false);
    }
    catch (VisADException e) {
    }
    super.nullControl();
  }

  /** set the font; in the initial release this has no effect
  *
  * @param f is the java.awt.Font or the visad.util.HersheyFont
  */

  public void setFont(Object f)
         throws VisADException, RemoteException {
    if (f instanceof java.awt.Font ||
        f instanceof visad.util.HersheyFont ||
        f == null) {
      font = f;
      changeControl(true);
    } else {
      throw new VisADException("Font must be java.awt.Font or HersheyFont");
    }

  }

  /** return the java.awt.Font
  *
  * @return the java.awt.Font if the font is of that type; otherwise, null
  */
  public Font getFont() {
    if (font instanceof java.awt.Font) {
      return (Font) font;
    } else {
      return null;
    }
  }

  /** return the HersheyFont
  *
  * @return the visad.util.HersheyFont if the font is of
  * that type; otherwise, null
  */
  public HersheyFont getHersheyFont() {
    if (font instanceof visad.util.HersheyFont) {
      return (HersheyFont) font;
    } else {
      return null;
    }
  }

  /** set the centering flag; if true, text will be centered at
      mapped locations; if false, text will be to the right
      of mapped locations */
  public void setCenter(boolean c)
         throws VisADException, RemoteException {
    // abcd 5 February 2001
    justification = Justification.CENTER;
    //center = c;
    changeControl(true);
  }

// TODO: Deprecate this?
  /** return the centering flag */
  public boolean getCenter() {
    // abcd 5 February 2001
    //return center;
    if (justification == Justification.CENTER) {
      return true;
    } else {
      return false;
    }
  }

  /**
   * Set the justification flag
   *
   * Possible values are TextControl.Justification.LEFT,
   * TextControl.Justification.CENTER and TextControl.Justification.RIGHT
   */
  // abcd 5 February 2001
  public void setJustification(Justification newJustification)
         throws VisADException, RemoteException
  {
    // Store the new value
    justification = newJustification;

    // Tell the control it's changed
    changeControl(true);
  }

  /**
   * Return the justification value
   */
  // abcd 5 February 2001
  public Justification getJustification()
  {
    return justification;
  }

  /**
   * Set the vertical justification flag
   *
   * Possible values are TextControl.Justification.TOP,
   * TextControl.Justification.CENTER and TextControl.Justification.BOTTOM
   *
   * @author abcd 19 March 2003
   */
  public void setVerticalJustification(Justification newJustification)
         throws VisADException, RemoteException
  {
    // Store the new value
    verticalJustification = newJustification;

    // Tell the control it's changed
    changeControl(true);
  }

  /**
   * Return the vertical justification value
   *
   * @author abcd 19 March 2003
   */
  public Justification getVerticalJustification()
  {
    return verticalJustification;
  }

  /** set the size of characters; the default is 1.0 */
  public void setSize(double s)
         throws VisADException, RemoteException {
    size = s;
    changeControl(true);
  }

  /** return the size */
  public double getSize() {
    return size;
  }

  // WLH 31 May 2000
  public void setSphere(boolean s)
         throws VisADException, RemoteException {
    sphere = s;
    changeControl(true);
  }

  // WLH 31 May 2000
  public boolean getSphere() {
    return sphere;
  }

  // WLH 16 June 2000
  public void setNumberFormat(NumberFormat f)
         throws VisADException, RemoteException {
    format = f;
    changeControl(true);
  }

  // WLH 16 June 2000
  public NumberFormat getNumberFormat() {
    return format;
  }

  private boolean fontEquals(Object newFont)
  {
    if (font == null) {
      if (newFont != null) {
        return false;
      }
    } else if (newFont == null) {
      return false;
    } else if (!font.equals(newFont)) {
      return false;
    }

    return true;
  }

  // WLH 16 June 2000
  private boolean formatEquals(NumberFormat newFormat)
  {
    if (format == null) {
      if (newFormat != null) {
        return false;
      }
    } else if (newFormat == null) {
      return false;
    } else if (!format.equals(newFormat)) {
      return false;
    }

    return true;
  }

  /**
   * Set the rotation
   *
   * abcd 1 February 2001
   */
  public void setRotation(double newRotation)
         throws VisADException, RemoteException
  {
    // Store the new rotation
    rotation = newRotation;
    // Tell the control it's changed
    changeControl(true);
  }

  /**
   * Get the rotation
   *
   * abcd 1 February 2001
   */
  public double getRotation()
  {
    return rotation;
  }

  /** get a string that can be used to reconstruct this control later */
  public String getSaveString() {
    return null;
  }

  /** reconstruct this control using the specified save string */
  public void setSaveString(String save)
    throws VisADException, RemoteException
  {
    throw new UnimplementedException(
      "Cannot setSaveString on this type of control");
  }

  /** copy the state of a remote control to this control */
  public void syncControl(Control rmt)
    throws VisADException
  {
    if (rmt == null) {
      throw new VisADException("Cannot synchronize " + getClass().getName() +
                               " with null Control object");
    }

    if (!(rmt instanceof TextControl)) {
      throw new VisADException("Cannot synchronize " + getClass().getName() +
                               " with " + rmt.getClass().getName());
    }

    TextControl tc = (TextControl )rmt;

    boolean changed = false;

    if (!fontEquals(tc.font)) {
      changed = true;
      font = tc.font;
    }

    // abcd 5 February 2001
    //if (center != tc.center) {
    //  changed = true;
    //  center = tc.center;
    //}
    if (justification != tc.justification) {
      changed = true;
      justification = tc.justification;
    }
    // abcd 19 February 2003
    if (verticalJustification != tc.verticalJustification) {
      changed = true;
      verticalJustification = tc.verticalJustification;
    }

    if (!Util.isApproximatelyEqual(size, tc.size)) {
      changed = true;
      size = tc.size;
    }

    // WLH 31 May 2000
    if (sphere != tc.sphere) {
      changed = true;
      sphere = tc.sphere;
    }

    // WLH 16 June 2000
    if (!formatEquals(tc.format)) {
      changed = true;
      format = tc.format;
    }

    // abcd 1 February 2001
    if (!Util.isApproximatelyEqual(rotation, tc.rotation)) {
      changed = true;
      rotation = tc.rotation;
    }

    // SL 16 July 2003
    if (!Util.isApproximatelyEqual(characterRotation, tc.characterRotation)) {
      changed = true;
      characterRotation = tc.characterRotation;
    }

    // WLH 6 Aug 2001
    if (autoSize != tc.autoSize) {
      // changed = true;
      setAutoSize(tc.autoSize);
    }

    // SL 16 July 2003
    if (!Util.isApproximatelyEqual(scale, tc.scale)) {
      changed = true;
      scale = tc.scale;
    }

    // SL 16 July 2003
    for (int i=0; i<3; i++) {
      if (!Util.isApproximatelyEqual(offset[i], tc.offset[i])) {
        changed = true;
        offset[i] = tc.offset[i];
      }
    }

    if (changed) {
      try {
        changeControl(true);
      } catch (RemoteException re) {
        throw new VisADException("Could not indicate that control" +
                                 " changed: " + re.getMessage());
      }
    }
  }

  public boolean equals(Object o)
  {
    if (!super.equals(o)) {
      return false;
    }

    TextControl tc = (TextControl )o;

    if (!fontEquals(font)) {
      return false;
    }

    // abcd 5 February 2001
    //if (center != tc.center) {
    if (justification != tc.justification) {
      return false;
    }
    // abcd 19 March 2003
    if (verticalJustification != tc.verticalJustification) {
      return false;
    }

    // WLH 31 May 2000
    if (sphere != tc.sphere) {
      return false;
    }

    // WLH 16 June 2000
    if (!formatEquals(tc.format)) {
      return false;
    }

    // abcd 1 February 2001
    if (!Util.isApproximatelyEqual(rotation, tc.rotation)) {
      return false;
    }

    if (!Util.isApproximatelyEqual(size, tc.size)) {
      return false;
    }

    // WLH 6 Aug 2001
    if (autoSize != tc.autoSize) {
      return false;
    }

    // SL 18 July 2003
    if (!Util.isApproximatelyEqual(characterRotation, tc.characterRotation)) {
      return false;
    }

    // SL 18 July 2003
    if (!Util.isApproximatelyEqual(scale, tc.scale)) {
      return false;
    }

    // SL 18 July 2003
    for (int i=0; i<3; i++) {
      if (!Util.isApproximatelyEqual(offset[i], tc.offset[i])) {
        return false;
      }
    }

    return true;
  }

  /**
   * Gets the value of characterRotation
   *
   * @return the value of characterRotation
   */
  public double getCharacterRotation()  {
    return this.characterRotation;
  }

  /**
   * Sets the value of characterRotation
   *
   * @param argCharacterRotation Value to assign to this.characterRotation
   */
  public void setCharacterRotation(double argCharacterRotation) throws
    VisADException, RemoteException
  {
    this.characterRotation = argCharacterRotation;
    // Tell the control it's changed
    changeControl(true);
  }

  /**
   * Gets the value of scale
   *
   * @return the value of scale
   */
  public double getScale()  {
    return this.scale;
  }

  /**
   * Sets the value of scale
   *
   * @param argScale Value to assign to this.scale
   */
  public void setScale(double argScale)
    throws VisADException, RemoteException {
    this.scale = argScale;
    // Tell the control it's changed
    changeControl(true);
  }

  /**
   * Gets the value of offset
   *
   * @return the value of offset
   */
  public double[] getOffset()  {
    double[] aOffset = new double[]{
      this.offset[0], this.offset[1], this.offset[2]};
    return aOffset;
  }

  /**
   * Sets the value of offset
   *
   * @param argOffset Value to assign to this.offset
   */
  public void setOffset(double[] argOffset)
    throws VisADException, RemoteException {
    this.offset[0] = argOffset[0];
    this.offset[1] = argOffset[1];
    this.offset[2] = argOffset[2];
    // Tell the control it's changed
    changeControl(true);
  }

  class ProjectionControlListener implements ControlListener {
    private boolean pfirst = true;
    private MouseBehavior mouse;
    private ProjectionControl pcontrol;
    private TextControl text_control;
    private double base_scale = 1.0;
    private float last_cscale = 1.0f;
    private double base_size = 1.0;

    ProjectionControlListener(MouseBehavior m, TextControl t,
                              ProjectionControl p) {
      mouse = m;
      text_control = t;
      pcontrol = p;
    }

    public void controlChanged(ControlEvent e)
           throws VisADException, RemoteException {
      double[] matrix = pcontrol.getMatrix();
      double[] rot = new double[3];
      double[] scale = new double[1];
      double[] trans = new double[3];
      mouse.instance_unmake_matrix(rot, scale, trans, matrix);

      if (pfirst) {
        pfirst = false;
        base_scale = scale[0];
        last_cscale = 1.0f;
        base_size = text_control.getSize();
      }
      else {
        float cscale = (float) (base_scale / scale[0]);
        float ratio = cscale / last_cscale;
        if (ratio < 0.95f || 1.05f < ratio) {
          last_cscale = cscale;
          text_control.setSize(base_size * cscale);
        }
      }
    }
  }

}

