import java.lang. *;
import java.awt. *;
import java.awt.ImageObserver.*;
import java.util. *;

import WPoint;
import WTrig;
import WEdge;

class GCanvas extends Canvas
{
  private int dragi = -1;
  private int movex, movey;
  private WTrsf t = new WTrsf(1, 0, 0);
  private Label lbMag;
  public boolean paintCircles = false;
  public boolean paintVoronoi = false;
  int mode = 0;
  
  private Vector points;
  private WPoint point (int i)
  {
    return (WPoint) points.elementAt (i);
  }
  private Vector trigs;
  private WTrig trig (int i)
  {
    return (WTrig) trigs.elementAt (i);
  }  
  private Vector edges;
  private WEdge edge (int i)
  {
    return (WEdge) edges.elementAt (i);
  }
  private Hashtable edgeDB = new Hashtable();
  public GCanvas (Vector apoints, Vector atrigs, Label anlbMag)
  {
    points = apoints;
    trigs = atrigs;
    lbMag = anlbMag;
  }
  public void clear ()
  {
    points.removeAllElements ();
    trigs.removeAllElements ();
    repaint ();
  }
  public void scroll (int x, int y)
  {
    int i;
    for (i = 0; i < points.size (); i++)
        point (i).move (point (i).x + x,
			point (i).y + y);
      trigs.removeAllElements ();
      repaint ();
  }
  public boolean mouseDown (Event evt, int x, int y)
  {
    if ((evt.modifiers & Event.CTRL_MASK) != 0)
      mode = 1;
    else if ((evt.modifiers & Event.SHIFT_MASK) != 0)
      mode = 2;
    else
      mode = 0;
    switch(mode)
      {
        case 0:
	  int n = points.size ();
	  int i;
	  dragi = -1;
	  for (i = 0; i < n; i++)
	    if (   Math.abs (x - t.x(point (i).x)) < 4
	        && Math.abs (y - t.y(point (i).y)) < 4)
	        break;
          if (i != n)
  	    {
              trigs.removeAllElements ();
  	      if ((evt.modifiers & Event.ALT_MASK) != 0)
  	          points.removeElementAt(i);
  	      else
  	        dragi = i;
  	    }
  	  else if ((evt.modifiers & Event.ALT_MASK) == 0)
	    {
	      dragi = n;
	      points.addElement (new WPoint (t.xi(x), t.yi(y)));
              trigs.removeAllElements ();
	    }
          paint (getGraphics ());
          return true;
        case 1: 
          movex = x;
	  movey = y;
	  return true;
      }
    return super.mouseDown(evt, x, y);
  }
  public boolean mouseDrag (Event evt, int x, int y)
  {
    switch (mode)
      {
        case 0:
	  if (dragi >= 0 && point (dragi).move (t.xi(x), t.yi(y)))
	    {
	      trigs.removeAllElements ();
              paint (getGraphics ());
	    }
	  return true;
        case 1:
	  if (movex != x || movey != y)
	    {
	      t.xoff += x - movex;
	      t.yoff += y - movey;
	      movex = x;
	      movey = y;
              paint (getGraphics ());
	    }
	  return true;
      }
    return super.mouseDrag(evt, x, y);
  }
  public boolean mouseUp (Event evt, int x, int y)
  {
    switch (mode)
      {
        case 0:
        case 1:
          return mouseDrag(evt, x, y);
        case 2:
          Rectangle r = getGraphics().getClipRect ();
          if ((evt.modifiers & Event.ALT_MASK) != 0)
            if (t.mag > 1.0/256) 
              {
                t.mag /= 2;
                t.xoff = t.xoff / 2 - r.width / 4 + x;
                t.yoff = t.yoff / 2 - r.height / 4 + y;
              }
            else
              return true;
          else
            if (t.mag < 256)
              {
                t.mag *= 2;
                t.xoff = r.width / 2 - 2 * (x - t.xoff);
                t.yoff = r.height / 2 - 2 * (y - t.yoff);
              }
            else
              return true;
          lbMag.setText ("Magnification by " + Double.toString(t.mag));
          paint (getGraphics ());
          return true;
      }
    return super.mouseUp(evt, x, y);
  }
  public void paint (Graphics g)
  {
    int n = points.size ();
    Rectangle r = g.getClipRect ();
    g.setColor (Color.white);
    g.fillRect (r.x, r.y, r.width, r.height);
    g.setColor (Color.black);
    g.drawRect (r.x, r.y, r.width - 1, r.height - 1);

    if (trigs.size () == 0)
      {
        edgeDB.clear();
        for (int i = 0; i < n; i++)
	  {
	    WPoint pi = point(i);
	    double pix = pi.x;
	    double piy = pi.y;
	    double piz = pi.z;
	    for (int j = i + 1; j < n; j++)
	      {
	        WPoint pj = point(j);
	        double pjx = pj.x;
	        double pjy = pj.y;
	        double pjz = pj.z;
	        for (int k = i + 1; k < n; k++)
	          {
	            WPoint pk = point(k);
	            double pkx = pk.x;
	            double pky = pk.y;
	            double pkz = pk.z;
		    double zn = (pjx - pix) * (pky - piy) - (pkx - pix) * (pjy - piy);
		    if (j == k || zn > 0)
		      continue;
		    int m = 0;
		    double xn = (pjy - piy) * (pkz - piz) - (pky - piy) * (pjz - piz);
		    double yn = (pkx - pix) * (pjz - piz) - (pjx - pix) * (pkz - piz);
		    for (m = 0; m < n; m++)
		      {
		        WPoint pm = point(m);
		        if (m != i && m != j && m != k
		            && ((pm.x - pix) * xn + (pm.y - piy) * yn + (pm.z - piz) * zn) > 0)
		            break;
		      }
		    if (m == n)
		      {
		        WTrig newTrig = new WTrig (pi, pj, pk);
		        trigs.addElement (newTrig);
		        for(int p = 0; p < 3; p++)
		          {
		            WEdge work = new WEdge(pi, pj);
		            WEdge corr = (WEdge)edgeDB.get(work);
		            if (corr == null)
		              edgeDB.put (work, new WEdge (newTrig.pC, null));
		            else
		              corr.p2 = newTrig.pC;
		            WPoint tmp = pi;
		            pi = pj;
		            pj = pk;
		            pk = tmp;
		          }
		      }
		 }
              }
           }
      }
    if (paintCircles)
      {
        g.setColor (Color.darkGray);
        for (int i = 0; i < trigs.size (); i++)
          trig (i).paintCircle (g, t);
      }
    g.setColor (Color.green);
    if (paintVoronoi)
      {
        for(Enumeration e = edgeDB.keys(); e.hasMoreElements();)
          {
            WEdge v = (WEdge)e.nextElement();
            WEdge w = (WEdge)edgeDB.get(v);
          w.paint(g, t, v);
          }
      }
    g.setColor (Color.blue);
    for (int i = 0; i < trigs.size (); i++)
	  trig (i).paint (g, t);
    g.setColor (Color.red);
    for (int i = 0; i < n; i++)
      point (i).paint (g, t);
  }
}

