// DirectedEdge.java
// Copyright (C) 2000 Linyuan Lu
// Code based on David Binger's Edge.java 
// Copyright (C) 1997 David Binger
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program 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 program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, 
// Boston, MA  02111-1307, USA.

// Version 0.1
import java.awt.*;
import java.io.*;

public class DirectedEdge
extends Selectable
implements Serializable
{
    
    public int multi=1;
  // The current calculated <cos,sin> and length of this edge.
    public transient Position rotation = new Position(0,0,0);
    public transient double length = -1;

  // These are used for drawing arrows.
  // They are declared here to improve speed. 
  static int[] xvals = new int[4];
  static int[] yvals = new int[4];

  DirectedVertex start;
  DirectedVertex end;

  // Serializable
  private void writeObject(ObjectOutputStream out)
  throws IOException {
    out.writeObject(start);
    out.writeObject(end);
    out.writeInt(multi);
  }

  // Serializable
  private void readObject(ObjectInputStream in)
  throws IOException, ClassNotFoundException {
    start = (DirectedVertex)in.readObject();
    end = (DirectedVertex)in.readObject();
    multi=in.readInt();
    length = -1;
    rotation = new Position(0,0,0);
  }

  // Calculate length and rotation.
  public final void validate() {
    if (length<0) {
      Position p1 = start.position;
      Position p2 = end.position;
      length = Position.distance(p1,p2);
      if (length==0) return;
      double dy = p2.y - p1.y;
      double dx = p2.x - p1.x;
      rotation.x = dx/length;
      rotation.y = dy/length; 
    }
  }
  
  // Force recalculation of length and rotation.
  public final void revalidate() {
    length = -1;
    validate();
  }

  public DirectedEdge(DirectedVertex a, DirectedVertex b) {
      super();
    start = a;
    end = b;
    //    a.addOutEdge(this);
    // b.addInEdge(this);
     revalidate();
  }
  
  public String toString() {
    String s = "(Edge";
    s += " " + String.valueOf(start.hashCode());
    s += " " + String.valueOf(end.hashCode());
    s += " " + multi;
    s += " " + super.toString() + " ";
    return s+" )";
  }
  
  // What is the length of the shortest segment perpendicular to this edge
  // that touches the edge and the point x,y?
  // Return a number >10000 if there is no such segment.
  public final double distance(double x,double y) {
    validate();
    Position p = start.position;
    x -= p.x;
    y -= p.y;
    double cos = rotation.x;
    double sin = rotation.y;
    double tx = x*cos+y*sin;
    if ((length==0) || (tx<0) || (tx>length)) return length+10000;
    double ty = -x*sin+y*cos;
    ty = Math.abs(ty);
    return ty;
  }
  
  public final boolean nearStart(int x,int y) {
    int limit = 10+size+start.size;
    return start.near(x,y,limit);
  }

  public final boolean nearEnd(int x,int y) {
    int limit = 10+size+end.size;
    return end.near(x,y,limit);
  }

  public final void paintLine(Graphics g) {
    validate();
    g.setColor(getColor(color));
    Position p1 = start.position;
    Position p2 = end.position; int[] xtemp=new int[4];
    int[] ytemp=new int[4];
    int dx,dy,wx,wy,sx,sy,tx,ty;
    dx=p1.x()-p2.x();
    dy=p1.y()-p2.y();
    int re = end.size;
    int rs = start.size;
    double radius= Math.sqrt(dx*dx+dy*dy);
    wx=(int ) (((double) dy) / radius *  size/4.0);
    wy=(int ) (-((double) dx) / radius *  size/4.0);
    sx=(int ) (((double) dx) / radius *  rs); 
    sy=(int ) (((double) dy) / radius *  rs);
    tx=(int ) (((double) dx) / radius *  (re+size/1.4)); 
    ty=(int ) (((double) dy) / radius *  (re+size/1.4));
    if(wx==0 && wy==0){
        g.drawLine(p1.x(),p1.y(),p2.x(),p2.y());
    }
    else{
        xtemp[0]=p1.x()-wx -sx;
        xtemp[1]=p2.x()-wx +tx;
        xtemp[2]=p2.x()+wx +tx;
        xtemp[3]=p1.x()+wx -sx;
        ytemp[0]=p1.y()-wy -sy;
        ytemp[1]=p2.y()-wy +ty;
        ytemp[2]=p2.y()+wy +ty;
        ytemp[3]=p1.y()+wy -sy;
        g.fillPolygon(xtemp,ytemp,4);
    }

  }

  public final void adjustSize(int d) {
    int r = size + d;
    if (r>2) size = r;
  }
  
  public final void paintEndArrow(Graphics g) {
    Position p1 = start.position;
    int r = end.size;
    int s = size;
    double ax = p1.x+0.5;
    double ay = p1.y+0.5;
    double sin = rotation.y;
    double cos = rotation.x;
    double px = length-(r+1);
    double py = 0;
    double ly = s;
    double lx = length-(r+s+s);
    double rx = lx;
    double ry = -ly;
    double mx = length-(r+s+s/2);
    double my = 0;
    double tx; double ty;
    tx = px*cos-py*sin;
    ty = px*sin+py*cos;
    px = tx; py = ty;
    tx = lx*cos-ly*sin;
    ty = lx*sin+ly*cos;
    lx = tx; ly = ty;
    tx = rx*cos-ry*sin;
    ty = rx*sin+ry*cos;
    rx = tx; ry = ty;
    tx = mx*cos-my*sin;
    ty = mx*sin+my*cos;
    mx = tx; my = ty;
    px+=ax; py+=ay;
    mx+=ax; my+=ay;
    lx+=ax; ly+=ay;
    rx+=ax; ry+=ay;
    xvals[0]=(int)px;
    xvals[1]=(int)lx;
    xvals[2]=(int)mx;
    xvals[3]=(int)rx;
    yvals[0]=(int)py;
    yvals[1]=(int)ly;
    yvals[2]=(int)my;
    yvals[3]=(int)ry;
    Color temp=g.getColor();
    g.setColor(getColor(color));
    g.fillPolygon(xvals,yvals,4);
    g.setColor(temp);
  }

  public final void paintArrows(Graphics g) {
   revalidate();
    if (length==0) return;
      paintEndArrow(g);
  
  }
  
  // Put the left end of the base right on the midpoint of the line.
  // Better placement could be obtained by calling validateLabelBounds(),
  // and using those dimensions more carefully.
  public final void defaultLabel() {
    revalidate();
    int x = ((int)(start.position.x+end.position.x)) >> 1;
    int y = ((int)(start.position.y+end.position.y)) >> 1;
    moveLabel(x,y);
  }

  // Shift the position.
  public final void moveRelative(double x,double y) {
    moveLabelRelative((int)x,(int)y);
  }

  public final void paint(Graphics g) {
    paintLine(g);
    paintArrows(g);
  }

    /* public static void main(String args[]) {     
        DirectedEdge s=DirectedEdge(u,v);  
        DirectedEdge t=DirectedEdge(v,w); 
        System.out.println(""+s+t); 
    }
      */
  //    protected void finalize(){
//  	start.delOutEdge(this);
//  	end.delInEdge(this);
//      }
}






