/*
 * @(#) Arrow.java 1.0 98/07/24 Larry Rodrigues
 **/
package com.vistech.shapes;
import java.awt.*;
import java.lang.Math;
import java.awt.geom.*;

/**
  * Represents an interactively drawable arrow.
  * @version 1.0  24  July 1996
  * @author Larry Rodrigues
  **/
public class Arrow extends Shapes implements Drawable{
    private Point old = new Point(0,0);
    protected boolean doubleHeaded = false;
    protected int tol = 10; //tolerance. 
    protected double slope= 1.0;


  /**
    * Initializes the Arrow parameters.
    * The arguments provide the starting position of the Arrow.
    * @param  x  the x coordinate of the starting position.
    * @param  y  the y coordinate of the starting poistion.
    **/
    public void init(int x , int y){
         st.x = x; st.y = y;
         cur.x = x; cur.y  =y;
         old.x =x; old.y =y;
    }
    /**
      * Sets the current position.
      * @param  x  the x coordinate of the current position.
      * @param  y  the y coordinate of the current poistion.
      *
      **/
    public void setCurPoint(int x , int y){
         cur.x = x; cur.y = y;
    }

    public void setDoubleHeaded(boolean onOrOff){
       doubleHeaded = true;
    }

   /**
    * Draws an Arrow interactively on a Graphics context g.
    * This API does not check whether the Arrow overflows
    * the borders of the component.
    * This  method uses the XOR paint mode. It erases the
    * previously drawn shape and draws a new shape in its
    * place.
    * @param g the graphics object on which the shape is to  be drawn.
    **/
    public void drawInteractive(Graphics2D g ){
       basicStroke = g.getStroke();
       g.setColor(Color.black);
       g.setXORMode(drawingColor);
       drawLine(g, old.x, old.y);
       drawArrow(st, old, g);
       old.x = cur.x; old.y = cur.y;
       drawLine(g, cur.x, cur.y);
       drawArrow(st, cur, g);
       old.x = cur.x; old.y = cur.y;
   }

   /**
    * Draws an Arrow on a Graphics context g.
    * This API does not check whether the Box overflows
    * the borders of the component.
    * @param g the graphics object on which the shape is to  be drawn.
    **/
    public void drawPermanent(Graphics2D g){
       Stroke savedStroke = g.getStroke();
       if(basicStroke != null) g.setStroke(basicStroke);
       g.setPaintMode();
       g.setColor(drawingColor);
       drawLine(g, cur.x, cur.y);
       drawArrow(st, cur, g);
       g.setStroke(savedStroke);
    }

    public void draw(Graphics2D g){
       Stroke savedStroke = g.getStroke();
       if(basicStroke != null) g.setStroke(basicStroke);
       g.setColor(Color.black);
       g.setXORMode(drawingColor);
       drawLine(g, cur.x, cur.y);
       drawArrow(st, cur, g);
       g.setStroke(savedStroke);
    }

    public void erase(Graphics2D g){
       Stroke savedStroke = g.getStroke();
       if(basicStroke != null) g.setStroke(basicStroke);
       g.setColor(Color.black);
       g.setXORMode(drawingColor);
       drawLine(g, cur.x, cur.y);
       drawArrow(st, cur, g);
       if(smallRectsOn) drawSmallRects(g);
       g.setStroke(savedStroke);
    }

    public boolean contains(Graphics2D g, int x, int y){
        GeneralPath pa = new GeneralPath();
        float lineWidth =1f;
        if(basicStroke !=  null) 
           lineWidth = ((BasicStroke)basicStroke).getLineWidth();
        double deg = Math.atan(slope);
        
        int boundsX = (int)((lineWidth+tol)*(Math.sin(deg)));
        int boundsY = (int)((lineWidth+tol)*(Math.cos(deg)));
   

        pa.moveTo((float)(st.x- boundsX), (float)(st.y-boundsY));
        pa.lineTo((float)(st.x+ boundsX), (float)(st.y+boundsY));
        pa.lineTo((float)(cur.x+boundsX), (float)(cur.y+boundsY));
        pa.lineTo((float)(cur.x-boundsX), (float)(cur.y-boundsY));
        pa.closePath();
        if(pa.contains((double)x, (double)y)) {
           drawSmallRects(g);
           return true;
        }else return false;
   }

   public synchronized void drawSmallRects(Graphics2D g){
      Stroke savedStroke = g.getStroke();
      if(basicStroke != null) g.setStroke(basicStroke);

      smallRectsOn = !smallRectsOn;
      g.setColor(Color.black);
      g.setXORMode(drawingColor);

      int xp[] = new int[4];
      int yp[] = new int[4];
      xp[0] = st.x; yp[0] = st.y;
      xp[1] = cur.x; yp[1] = st.y;
      xp[2] = cur.x; yp[2] = cur.y;
      xp[3] = st.x; yp[3] = cur.y;
      for(int i=0; i< xp.length;i++) {
          int x, y;
          if(xp[i] > xp[(i+2)%4]) x = xp[i]+1;
          else x = xp[i]-6;
          if(yp[i] > yp[(i+2)%4]) y = yp[i]+1;
          else y = yp[i]-6;
          g.drawRect(x, y, 5,5);
      }
      g.setStroke(savedStroke);
    }

   /**
    * Draws and returns itself.
    * shapeCount is set by the caller.
    * @param g the graphics object on which the shape is to be drawn.
    * @param shCnt the shape count.
    * @return Shape itself.
    **/
  /*
    public Shapes saveCurShape(Graphics2D g, int shCnt){
         g.setPaintMode();
         g.setColor(drawingColor);
         drawLine(g, cur.x, cur.y);
         drawArrow(st, cur, g);
         return (Shapes)this;
   }
  */

   public void move(Graphics2D g, int diffx, int diffy){
      Stroke savedStroke = g.getStroke();
      if(basicStroke != null) g.setStroke(basicStroke);
      g.setColor(Color.black);
      g.setXORMode(drawingColor);
      drawLine(g, cur.x, cur.y);
      drawArrow(st, cur, g);
      if(smallRectsOn) drawSmallRects(g);

      st.x += diffx; st.y += diffy;
      cur.x += diffx; cur.y += diffy;
      drawLine(g, cur.x, cur.y);
      drawArrow(st, cur, g);
      drawSmallRects(g);
      g.setStroke(savedStroke);

  }

  /**
    * Draws two inclined lines at the current position
    * in order to create an arrow head.
    * @param st the staring position of the arrow - arrow bottom.
    * @param end the end position of the arrow - arrow head.
    * @param g the graphics context.
    */
   public void drawArrow(Point st, Point end, Graphics2D g){
      Point upLine = new Point(0,0);
      Point downLine = new Point(0,0);
      int direction ;
      double theta;
        if((end.y -st.y) >= 0) direction = -1;
           else direction = 1;

        // Rotate the point by 30 deg

        double angle = Math.atan((double)(end.x-st.x)/(double)( end.y-st.y));
        int len = 10;
        Point rel = new Point(len, len);

        angle += Math.PI/2;
        theta = -(angle + Math.PI/6);
        //Rotate the line up

        upLine.x =end.x -(int)( len* Math.cos(theta)*direction);
        upLine.y =end.y -(int)( len* Math.sin(theta)*direction);
        // Rotate the line down

        theta = -(angle - Math.PI/6);
        downLine.x =end.x -(int)( len* Math.cos(theta)*direction);
        downLine.y =end.y -(int)( len* Math.sin(theta)*direction);

        // Now the draw the arrow lines
        GeneralPath arrowpath = new GeneralPath();
        arrowpath.moveTo((float)upLine.x, (float)upLine.y);
        arrowpath.lineTo((float)end.x, (float)end.y);
        arrowpath.lineTo((float) downLine.x, (float)downLine.y);
        arrowpath.closePath();
        g.draw(arrowpath);
        if(doubleHeaded) {
           arrowpath = new GeneralPath();
           //theta = -(angle + Math.PI/6);
           upLine.x = st.x -(int)( len* Math.cos(theta)*(-direction));
           upLine.y = st.y -(int)( len* Math.sin(theta)*(-direction));
           // Rotate the line down
           theta = -(angle + Math.PI/6);
           downLine.x =st.x -(int)( len* Math.cos(theta)*(-direction));
           downLine.y =st.y -(int)( len* Math.sin(theta)*(-direction));

           arrowpath.moveTo((float)upLine.x, (float)upLine.y);
           arrowpath.lineTo((float)st.x, (float)st.y);
           arrowpath.lineTo((float) downLine.x, (float)downLine.y);
           arrowpath.closePath();
           g.draw(arrowpath);
        }
   }

   private void drawLine(Graphics2D g, int endx, int endy) {
      Line2D.Float line = new Line2D.Float((float)st.x,
                                    (float)st.y,
                                    (float)endx,
                                    (float)endy);
      path = new GeneralPath(line);
      slope = (endy-st.y)/(double)(endx-st.x);
      g.draw(path);
   }
}








