// Edge.java
// by Brian Goldsmith and Alexander Smith
// Adapted from DirectedEdge.java
// Copyright (C) 2000 Linyuan Lu
// Code based on David Binger's Edge.java 
// Copyright (C) 1997 David Binger

import java.awt.*;
import java.io.*;

public class Edge
extends Selectable
implements Serializable
{
    
    public int multi=1;
    public boolean alreadyDrawn = false;
  // 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];

  Vertex start;
  Vertex end;
  List adjEdges;
  public boolean changeColor = false;
  
  // 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 = (Vertex)in.readObject();
    end = (Vertex)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 Edge(Vertex a, Vertex b) {
      super();
    start = a;
    end = b;
    adjEdges = new List();
    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();
    if (changeColor == false)
        g.setColor(getColor(color));
    else
        g.setColor(Color.red);    
    Position p1 = start.position;
    Position p2 = end.position;
    // To enable thickness of edge, the following line
    //g.drawLine(p1.x(),p1.y(),p2.x(),p2.y());
    //are changed to
    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;
	xtemp[1]=p2.x()-wx;
	xtemp[2]=p2.x()+wx;
	xtemp[3]=p1.x()+wx;
	ytemp[0]=p1.y()-wy;
	ytemp[1]=p2.y()-wy;
	ytemp[2]=p2.y()+wy;
	ytemp[3]=p1.y()+wy;

        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();
    if (changeColor == false)
        g.setColor(getColor(color));
    else
        g.setColor(Color.red);    
    g.fillPolygon(xvals,yvals,4);
    g.setColor(temp);
  } */

  /* public final void paintArrows(Graphics g) {
     return;
  } */
  
  // 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 final void paintMyLine(Graphics g)
  {
      paintLine(g);
  }



/*************************************************************/
/*********************** Dual Graph  *************************/
/*************************************************************/

  public int quadrant(Vertex centerPt) 
  {
    Position a;
    Position b;

    //check if the center point is at the start
    //a = center point
    //b = end-point
    if (start.Compare(centerPt))
    {
        a = start.position;
        b = end.position;
    }
    //check if the center point is at the end
    //a = center point
    //b = end-point
    else
    {
        a = end.position;
        b = start.position;
    }

    //ALL POINTS ACCORDING TO A AS CENTER
    //end-point of edge lies on quadrant I
    if ((a.x < b.x) && (a.y > b.y))
        return 1;
    //end-point of edge lies on quadrant II
    else if ((a.x > b.x) && (a.y > b.y))
        return 2;
    //end-point of edge lies on quadrant III
    else if ((a.x > b.x) && (a.y < b.y))
        return 3;
    //end-point of edge lies on quadrant IV
    else if ((a.x < b.x) && (a.y < b.y))
        return 4;
    //edge lies horizontally between quadrant I & IV
    else if ((a.x < b.x)  && (a.y == b.y))
        return 5;
    //edge lies horizontally between quadrant II & III
    else if ((a.x > b.x) && (a.y == b.y))
        return 6;
    //edge lies vertically between quadrant I & II
    else if ((a.x == b.x)  && (a.y > b.y))
        return 7;
    //edge lies vertically between quadrant III & IV
    else if ((a.x == b.x)  && (a.y < b.y))
        return 8;
    else
        return 0;   //dummy case
  }

  public double angle(Vertex vCenter)
  {
    double x, y, x1, y1, hypotenuse, oppositeSide, result;
    Vertex vStart, vEnd, vNewPoint;

    //vNewPoint = new Vertex(0,0);
    if (vCenter.Compare(start))
    {
	vStart = start;
        vEnd = end;
    }
    else
    {
        vStart = end;
    	vEnd = start;
    }
    x = vEnd.position.x - vStart.position.x;
    y = vEnd.position.y - vStart.position.y;
    if (quadrant(vCenter) == 1)
    {
        //x = vEnd.position.x - vStart.position.x;
        //y = vEnd.position.y - vStart.position.y;
        //vNewPoint.position.x = vEnd.position.x;
        //vNewPoint.position.y = vStart.position.y;
        x1 = vEnd.position.x - vEnd.position.x;  // x1 is always = 0
        y1 = vEnd.position.y - vStart.position.y;
	hypotenuse = Math.sqrt((x*x) + (y*y));
        oppositeSide = Math.sqrt((x1*x1) + (y1*y1));
	result = (Math.asin(oppositeSide/hypotenuse) * 180) / 3.14;
    }
    else if (quadrant(vCenter) == 2)
    {
        x1 = vEnd.position.x - vStart.position.x;
        y1 = vEnd.position.y - vEnd.position.y;  // y1 is always = 0
	hypotenuse = Math.sqrt((x*x) + (y*y));
        oppositeSide = Math.sqrt((x1*x1) + (y1*y1));
	result = 90 + ((Math.asin(oppositeSide/hypotenuse) * 180) / 3.14);
    }
    else if (quadrant(vCenter) == 3)
    {
        x1 = vEnd.position.x - vEnd.position.x;  // x1 is always = 0
        y1 = vEnd.position.y - vStart.position.y;
	hypotenuse = Math.sqrt((x*x) + (y*y));
        oppositeSide = Math.sqrt((x1*x1) + (y1*y1));
	result = 180 + ((Math.asin(oppositeSide/hypotenuse) * 180) / 3.14);
    }
    else if (quadrant(vCenter) == 4)
    {
        x1 = vEnd.position.x - vStart.position.x;
        y1 = vEnd.position.y - vEnd.position.y;  // y1 is always = 0
	hypotenuse = Math.sqrt((x*x) + (y*y));
        oppositeSide = Math.sqrt((x1*x1) + (y1*y1));
	result = 270 + ((Math.asin(oppositeSide/hypotenuse) * 180) / 3.14);
    }
    else if (quadrant(vCenter) == 5)
    {
	// on x-axis 
        result = 0.0;
    }
    else if (quadrant(vCenter) == 6)
    {
	// on y-axis
        result = 180.0;
    }
    else if (quadrant(vCenter) == 7)
    {
	// on y axis
        result = 90.0;
    }
    else
    {
	// on y axis
        result = 270.0;
    }
    return result;
  }

  public void displayEdge ()
  {
      System.out.println("********** Display Edge ************");  
      System.out.println("*** Start Point: (" + start.position.x + ", " + start.position.y + ")");
      System.out.println("*** End Point  : (" + end.position.x + ", " + end.position.y + ")");
      System.out.println("************************************");
  }
}
