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

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

public class Vertex
extends Selectable
implements Serializable
{

    public int loop=0;
  // edges is a list of edges that touch this vertex.
  public List inedges = new List(); //edge.start = this vertex
  public List outedges= new List(); //edge.end = this vertex
  public List myedges= new List();
  public boolean changeColor = false;
  public boolean placed = false;
   
  // position gives the coordinates of the center of the vertex.
  public Position position;

  // Vertices are drawn as circles or else as disks, depending on this flag.
  public boolean solid = true;
  
  public double x=0, y=0, z=0, theta=0;

    public int inDegree(){ 
	int sum=loop;
	int n=inedges.length;
	Object a[]=inedges.elements;
	for(int i=0;i<n;i++){
	    sum+=((Edge) a[i]).multi;
	}
	return sum;
    }

   public int outDegree(){
       int sum=loop;
       int n=outedges.length;
       Object a[]=outedges.elements;
	for(int i=0;i<n;i++){
	   sum+=((Edge) a[i]).multi;
	}
	return sum;
    }
    
    public int getDegree(){
        return inDegree() + outDegree();
    }
    
  // Serializable
  private void writeObject(ObjectOutputStream out)
  throws IOException {
    out.writeObject(inedges);
    out.writeObject(outedges);
    out.writeObject(position);
    out.writeBoolean(solid);
    out.writeInt(loop);
  }

  // Serializable
  private void readObject(ObjectInputStream in)
  throws IOException, ClassNotFoundException {
    inedges = (List)in.readObject();
    outedges = (List)in.readObject();
    position = (Position)in.readObject();
    solid = in.readBoolean();
    loop= in.readInt();
  }

  Vertex(int x,int y)
  /* constructor for placing a new Vertex at position (x,y) */
  {
    position = new Position(x,y);
    defaultLabel();
  }
    
  Vertex()
  /* default constructor */
  {
    position = new Position(0,0);
    defaultLabel();
  }  

  public final void alter() {
    solid = !solid;
    defaultLabel();
  }
  
  public final void move(int x,int y) {
    position.x = x;
    position.y = y;
    defaultLabel();
    int n = inedges.length;
    Object a[] = inedges.elements;
    for (int j=0;j<n;j++) {
	Edge et=((Edge)a[j]);
        et.revalidate();
        et.defaultLabel();
    }
    n = outedges.length;
    a = outedges.elements;
    for (int j=0;j<n;j++) {
	Edge et=((Edge)a[j]);
        et.revalidate();
        et.defaultLabel();
    }
  }

  // Is the point a,b inside this disk?
  public final boolean hitDisk(int a,int b) {
    int x = position.x();
    if ((a<x-size) || (a>x+size)) return false;
    int y = position.y();
    if ((b<y-size) || (b>y+size)) return false;
    int c = x-a;
    int d = y-b;
    if ((c*c+d*d)>(size*size)) return false;
    return true;
  }

  public final void adjustSize(int d) {
    int r = size + d;
    if (r>=0) size = r;
    defaultLabel();
  }

  // Place the label to the right of the disk, if it is solid,
  // and centered in the disk if it is not solid.
  public final void defaultLabel() {
    int x = position.x();
    int y = position.y();
    if (solid) moveLabel(x+size+2,y);
    else {
      moveLabel(x,y);
      validateLabelBounds();
      if (labelBounds.width>2) {
	x -= (labelBounds.width-1)/2-1;
	y += (labelBounds.height-1)/2-1;
	moveLabel(x,y);
      }
    }
  }    
    
  public final void paintDisk(Graphics g) {
    if (size<=0) return;
    int x = position.x();
    int y = position.y();
    int side = 2*size+2;
    if (changeColor == false)
        g.setColor(getColor(color));
    else
        g.setColor(Color.red);    
    if (solid) {
      g.fillArc(x-size,y-size,side,side,0,360);
    }
    else {
      g.setColor(Color.white);
      g.fillArc(x-size,y-size,side,side,0,360);
      g.setColor(getColor(color));
      side--;
      g.drawArc(x-size,y-size,side,side,0,360);
    }
  }
  
  public final void paint(Graphics g) {
    paintDisk(g);
  }

  public final void addInEdge(Edge e) {
      Edge edge;
    //check to see if the edge is in my list of edges
    if (myedges.length == 0)
        myedges.push(e);
    else
    {
        for (int i = 0; i < myedges.length; i++)
        {
           edge = (Edge)(myedges.elements[i]);
           if (!((edge.start.Compare(e.start) && edge.end.Compare(e.end)) ||
               (edge.start.Compare(e.end) && edge.end.Compare(e.start)) ||
               (edge.end.Compare(e.start) && edge.start.Compare(e.end))))
               myedges.push(e);
        }
    }

      for(int i=0;i<inedges.length;i++){
	  Edge et=inEdge(i);
	  if(e.start.equals(et.start)){
	      et.multi+=e.multi;
	      et.length=-1;
	       e.length = -1;
	      return;
	  }
      }
      inedges.push(e); 
      e.length = -1;
  }

  public final void addOutEdge(Edge e) {
      Edge edge;
    //check to see if the edge is in my list of edges
    if (myedges.length == 0)
        myedges.push(e);
    else
    {
        for (int i = 0; i < myedges.length; i++)
        {
           edge = (Edge)(myedges.elements[i]);
           if (!((edge.start.Compare(e.start) && edge.end.Compare(e.end)) ||
               (edge.start.Compare(e.end) && edge.end.Compare(e.start)) ||
               (edge.end.Compare(e.start) && edge.start.Compare(e.end))))
               myedges.push(e);
        }
    }

      for(int i=0;i<outedges.length;i++){ 
	  Edge et=outEdge(i);
	  if(e.end.equals(et.end)){
	      et.multi+=e.multi;
	      et.length=-1;
	       e.length = -1;
	      return;
	  }
      } 
    outedges.push(e);
    e.length = -1;
  }


  public final void delInEdge(Edge e) {
      for(int i=0;i<inedges.length;i++){
	 Edge et=inEdge(i);
	  if(e.start.equals(et.start)){
	    et.multi-=e.multi;
	  if(et.multi<=0) 
              inedges.delete(et);
	  }
      }
      for(int i=0;i<myedges.length;i++){
	  Edge et1=myEdge(i);
	  if(e.end.equals(et1.end)){
	    et1.multi-=e.multi;
	  if(et1.multi<=0) 
              myedges.delete(et1);
	  }
      }
  }

 public final void delOutEdge(Edge e) {
      for(int i=0;i<outedges.length;i++){
	  Edge et=outEdge(i);
	  if(e.end.equals(et.end)){
	    et.multi-=e.multi;
	  if(et.multi<=0) 
              outedges.delete(et);
	  }
      }
      for(int i=0;i<myedges.length;i++){
	  Edge et1=myEdge(i);
	  if(e.end.equals(et1.end)){
	    et1.multi-=e.multi;
	  if(et1.multi<=0) 
              myedges.delete(et1);
	  }
      }
    }

  public final Edge inEdge(int p) {
    return ((Edge)inedges.elements[p]);
  }

   public final Edge outEdge(int p) {
    return ((Edge)outedges.elements[p]);
  }
  
   public final Edge myEdge(int p) {
    return ((Edge)myedges.elements[p]);
  }
  
  public final void moveRelative(double a,double b) {
    int x = (int)a;
    int y = (int)b;
    moveLabelRelative(x,y);
    position.x+=x;
    position.y+=y;
  }
  
  public String toString() {
    String s = "(Vertex ";
    s += String.valueOf(hashCode());
    s += " "+position;
    s += " "+solid;
    s += " " + super.toString() + " ";
    s += " )";
    return s;
  }

  // How far is x,y from the center of the disk?
  public final double distance(double x,double y) {
    double a = x-position.x;
    double b = y-position.y;
    return Math.sqrt(a*a+b*b);
  }
  
  //compute the distance from two vertices
  public final double distance(Vertex v) {
    double a = position.x - v.position.x;
    double b = position.y - v.position.y;
    return Math.sqrt(a*a+b*b);
  }
  
  public boolean Compare(Vertex v)
  {
    return (position.x == v.position.x && position.y == v.position.y);
  }
  
  public void displayVertex()
  {
      System.out.println("********** Display Vertex ************");
      System.out.println("*** Vertex = (" + position.x + "," + position.y + ")");
      System.out.println("**************************************");
  }
}
