/*
 * Open Source Physics software is free software as described near the bottom of this code file.
 *
 * For additional information and documentation on Open Source Physics please see:
 * <http://www.opensourcephysics.org/>
 */

package org.opensourcephysics.display;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.text.DecimalFormat;

/**
 * A Protractor with an arrow that can be used to measure angles.
 *
 * @author W. Christian
 * @version 1.0
 */
public class Protractor extends InteractiveCircle implements Drawable {
  static final double PI2 = Math.PI*2;
  static final String thetaStr = "$\\theta$=";          //$NON-NLS-1$
  int protractorRadius, protractorRadius2, arrowLengthPix;
  protected Tip tip = new Tip();
  protected double arrowTheta = 0, orientation = 0;
  protected DecimalFormat f = new DecimalFormat("000"); //$NON-NLS-1$
  protected boolean showTheta = false;
  protected InteractiveLabel tauBox = new InteractiveLabel(thetaStr+f.format(getTheta()));

  /**
   * Constructs a protractor with the given pixel size.
   *
   * @param protractorRadius int
   */
  public Protractor(int protractorRadius) {
    this.protractorRadius = protractorRadius;
    protractorRadius2 = protractorRadius*2;
    arrowLengthPix = protractorRadius;
    tip.color = Color.BLUE;
    tauBox.setOffsetX(-20);
    tauBox.setOffsetY(5);
  }

  /**
   * Constructs a protractor with a default radius of 40 pixels.
   */
  public Protractor() {
    this(40);
  }

  /**
   * Sets the angle of the arrow on the protractor.
   * @param theta double
   */
  public void setTheta(double angle) {
    this.arrowTheta = angle+orientation;
  }

  /**
   * Gets the angle of the arrow on the protractor.
   * @return double
   */
  public double getTheta() {
    // return PBC.separation(arrowTheta-orientation,Math.PI*2);
    // inline the method for efficiency and to decouple from the numerics package
    double dr = arrowTheta-orientation;
    return dr-PI2*Math.floor(dr/PI2+0.5);
  }

  /**
   * Sets the orientation of the protractor.
   * @param angle double
   */
  public void setOrientation(double angle) {
    this.orientation = angle;
  }

  /**
   * Gets the orientation of the protractor.
   * @return double
   */
  public double getOrientation() {
    return orientation;
  }

  /**
   * Shows theta when the protractor is drawn when true.
   * @param show boolean
   */
  public void setShowTheta(boolean show) {
    showTheta = show;
  }

  /**
   * Gets the show theta property.
   * @return boolean
   */
  public boolean isShowTheta() {
    return showTheta;
  }

  public Interactive findInteractive(DrawingPanel panel, int xpix, int ypix) {
    Interactive interactive = super.findInteractive(panel, xpix, ypix);
    if(interactive!=null) {
      return interactive;
    }
    interactive = tip.findInteractive(panel, xpix, ypix);
    if(interactive!=null) {
      return interactive;
    }
    return tauBox.findInteractive(panel, xpix, ypix);
  }

  /**
   * Draws the protractor on the given drawing panel.
   *
   * @param panel DrawingPanel
   * @param g Graphics
   */
  public void draw(DrawingPanel panel, Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    double i1 = panel.xToPix(x);
    double j1 = panel.yToPix(y);
    //draw the vector with angle ticks
    g2.setColor(new Color(240, 40, 40, 40));
    // replace the circle with two half circles to indicate angle
    //g2.fill(new Double(i1-pixRadius,j1-pixRadius,pixRadius2,pixRadius2));  // draws circle
    double start = Math.toDegrees(orientation);
    g2.fill(new Arc2D.Double(i1-protractorRadius, j1-protractorRadius, protractorRadius2, protractorRadius2, start, 180, Arc2D.PIE));
    g2.setColor(new Color(40, 40, 240, 40));
    g2.fill(new Arc2D.Double(i1-protractorRadius, j1-protractorRadius, protractorRadius2, protractorRadius2, start+180, 180, Arc2D.PIE));
    g2.setColor(Color.gray);
    g2.setStroke(new BasicStroke(0.5f));
    for(int i = 0; i<36; i++) {
      AffineTransform at = g2.getTransform();
      at.rotate(-i*Math.PI/18, i1, j1);
      g2.setTransform(at);
      g2.draw(new Line2D.Double(i1+protractorRadius-5, j1, i1+protractorRadius, j1));
      at.rotate(+i*Math.PI/18, i1, j1);
      g2.setTransform(at);
    }
    tauBox.setText(thetaStr+f.format(Math.toDegrees(getTheta())), x, y);
    if(showTheta) {
      tauBox.draw(panel, g);
    }
    //tip.draw(panel,g);  // draws a small circle at the tip
    //drawing the arrow with the red head
    AffineTransform at = g2.getTransform();
    at.rotate(-arrowTheta, i1, j1);
    g2.setTransform(at);
    g2.setColor(Color.RED);
    Stroke currentStroke = g2.getStroke();
    g2.setStroke(new BasicStroke(1.5f));
    g2.draw(new Line2D.Double(i1, j1, i1+arrowLengthPix, j1));
    GeneralPath arrowHead = new GeneralPath();
    g2.draw(new Line2D.Double());
    arrowHead.moveTo((float) (i1+arrowLengthPix), (float) j1);
    arrowHead.lineTo((float) (i1+arrowLengthPix-15), (float) (j1-5));
    arrowHead.lineTo((float) (i1+arrowLengthPix-15), (float) (j1+5));
    arrowHead.closePath();
    g2.fill(arrowHead);
    g2.draw(arrowHead);
    at.rotate(+arrowTheta, i1, j1);
    g2.setTransform(at);
    g2.setStroke(currentStroke);
    double length = arrowLengthPix/panel.getXPixPerUnit();
    tip.setXY(x+length*Math.cos(arrowTheta), y+length*Math.sin(arrowTheta));
  }

  public class Tip extends InteractiveCircle {
    public void setXY(double x, double y) {
      this.x = x;
      this.y = y;
      arrowTheta = Math.atan2(y-Protractor.this.y, x-Protractor.this.x);
    }

  }

}

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

 * Code that uses any portion of the code in the org.opensourcephysics package
 * or any subpackage (subdirectory) of this package must must also be be released
 * under the GNU GPL license.
 *
 * This software 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
 * or view the license online at http://www.gnu.org/copyleft/gpl.html
 *
 * Copyright (c) 2007  The Open Source Physics project
 *                     http://www.opensourcephysics.org
 */
