import java.awt.*;
import ReferenceFrame;

public class MyCanvas extends Canvas
{
  public ReferenceFrame f, displayed_frame, recent_frame;
  public double scale;  // how many units in an axis
  public Universe u;

  Image offImage;
  public Graphics offGraphics; 
  boolean showStandardFrame;
  boolean showRecentFrame;
  
  int N;  // number of animation shots

  MyCanvas()
   {
    offImage = null;
    offGraphics = null;
    f = new ReferenceFrame();
    displayed_frame = new ReferenceFrame();
    recent_frame = new ReferenceFrame();
    showStandardFrame = true;
    showRecentFrame = false;
    scale = 5;
    N = 10;
   }

  public void paint(Graphics g)
   {
    if (offImage == null) 
     {
      setSize(getSize().height,getSize().height);
      offImage = createImage(getSize().width, getSize().height);
      offGraphics = offImage.getGraphics();
      draw();
     }
    g.drawImage(offImage,0,0,this);
   }

  int xscale(double x)
   {
    return (int) (((x / scale) + 1) * getSize().width / 2.0);
   }

  int x(SpacetimePoint p)
   {
    return xscale(f.x(p));
   }

  int yscale(double y)
   {
    return (int) ((1 - (y / scale)) * getSize().height / 2.0);
   }
 
  int y(SpacetimePoint p)
   {
    return yscale(f.t(p));
   }

  void drawFrame(ReferenceFrame F)
   {
    int i,j;
    SpacetimePoint p;

    double xmin = 100, xmax = -100, tmin = 100, tmax = -100;
    double x,t;

    for (i = -1; i <= 1; i+=2)
      for (j = -1; j <= 1; j+=2)
       {
        p = f.location(i*scale,j*scale);
        x = F.x(p); t = F.t(p);
        if (x < xmin) xmin = x;
        if (x > xmax) xmax = x; 
        if (t < tmin) tmin = t;
        if (t > tmax) tmax = t;
       }

    double xtmin = (xmin < tmin) ? xmin : tmin;
    double xtmax = (xmax > tmax) ? xmax : tmax;
    double x_tmin = (xmin < -tmax) ? xmin : -tmax;
    double x_tmax = (xmax > -tmin) ? xmax : -tmin;

// draw co-ordinate axes and light rays

    offGraphics.setColor(Color.black);
    drawLine(F.location(xmin,0), F.location(xmax,0));
    drawLine(F.location(0,tmin), F.location(0,tmax));
    drawLine(F.location(xtmin,xtmin), F.location(xtmax,xtmax));
    drawLine(F.location(x_tmin,-x_tmin), F.location(x_tmax,-x_tmax));

    int X,Y;

    for (i = (int) xmin; i <= xmax; i++)
      for (j = (int) tmin; j <= tmax; j++)
       {
        if (i==j) continue;
        if (i==-j) continue;

        p = F.location(i,j);  X = x(p);   Y = y(p);

        if (j == 0)
          offGraphics.drawLine(X,Y-3,X,Y+3); 
        else if (i == 0)
          offGraphics.drawLine(X-3,Y,X+3,Y); 
        else
         {
          offGraphics.drawLine(X-2,Y-2,X+2,Y+2);
          offGraphics.drawLine(X-2,Y+2,X+2,Y-2);
         }
       }
   }

  public void draw()
   {
    int i,j;
    if (offGraphics == null) return;

// clear screen
    offGraphics.setColor(Color.white);
    offGraphics.fillRect(0,0,getSize().width,getSize().height);

    if (showStandardFrame)
     {
// co-ordinate axes
      offGraphics.setColor(Color.black);
      offGraphics.drawLine(xscale(0),0,xscale(0),getSize().height);
      offGraphics.drawLine(0,yscale(0),getSize().width,yscale(0));
      offGraphics.drawLine(0,0,getSize().width,getSize().height);
      offGraphics.drawLine(0,getSize().height,getSize().width,0);


// grid marks
 
      for (i = (int) -scale; i < scale; i++)
       {
        offGraphics.drawLine(xscale(0)-3,yscale(i),xscale(0)+3,yscale(i));
        offGraphics.drawLine(xscale(i),yscale(0)-3,xscale(i),yscale(0)+3);
        for (j = (int) -scale; j < scale; j++)
          if (i != 0  &&  j != 0 && i != j && i != -j)
           {
            offGraphics.drawLine(xscale(i)-2,yscale(j)-2,xscale(i)+2,yscale(j)+2);
            offGraphics.drawLine(xscale(i)-2,yscale(j)+2,xscale(i)+2,yscale(j)-2);
           }
       }
     }
    else if (showRecentFrame)
      drawFrame(recent_frame);   // frame recently selected
    else
      drawFrame(displayed_frame); // frame selected by frameChoice

// Draw inertial objects
    for (i = 0; i < u.inertialObjects.size(); i++)
     {
      InertialObject o = u.inertialAt(i);
      o.display(this);
     }

// Draw events
    for (i = 0; i < u.events.size(); i++)
     {
      SpacetimeEvent e = u.eventAt(i);
      offGraphics.setColor(e.color);
      drawDot(e);
     }
   }
 
  public void redraw()
   {
    draw();
    if (offGraphics != null)
      getGraphics().drawImage(offImage,0,0,this);
   } 

  public void setOrigin(SpacetimePoint p)
   {
    setFrame(new ReferenceFrame(p, f.v));
   }

  public void setFrame(ReferenceFrame f)
   {
    ReferenceFrame f_old = this.f;
    int i;

    for (i = 1; i <= N; i++)
     {
      this.f.x0 = ( (N-i)*f_old.x0 + i * f.x0 ) / N;
      this.f.t0 = ( (N-i)*f_old.t0 + i * f.t0 ) / N;
      this.f.v  = ( (N-i)*f_old.v  + i * f.v  ) / N;
      redraw();
     }
   }

  public void setStickyFrame(ReferenceFrame f)
   {
    setFrame(f);
    recent_frame = f;
    redraw();
   }
   

  public void zoomIn(double factor)
   {
    if (scale < 0.05) return;
    zoom(scale/factor);
   }

  void zoom(double newscale)
  {
    double oldscale = scale;

    for (int i = 1; i < 5; i++)
     {
      scale = ( (5-i) * oldscale + i * newscale)/5;
      redraw();
     }
  }

  public void zoomOut(double factor)
   {
    if (scale > 15) return;
    zoom(scale*factor);
   }

  public void shift(double dx, double dt)
   {
    setOrigin(f.location(dx,dt));
   }

  public void boost(double v)
   {
    setFrame(new ReferenceFrame(f.x0,f.t0,f.abs_v(v)));
   }
 
  public void setColor(Color col)
   {
    offGraphics.setColor(col);
   }
 
  public void drawDot(SpacetimePoint p)
   {
    int x = x(p);
    int y = y(p);
 
    offGraphics.fillOval(x-5,y-5,10,10);
   }

  public void drawLine(SpacetimePoint p, SpacetimePoint q)
   {
    int x1 = x(p), y1 = y(p);
    int x2 = x(q), y2 = y(q);
 
    offGraphics.drawLine(x1,y1,x2,y2);
   }
 
  public void drawForwardLine(SpacetimePoint p, double abs_v)
   {
    int x1 = x(p), y1 = y(p);
 
    if (y1 <= 0) return;
 
    double v = f.v(abs_v);
 
    int x2 = x1 + (int) (y1 * v);
 
    offGraphics.drawLine(x1,y1,x2,0);
   }
 
  public void drawBackwardLine(SpacetimePoint p, double abs_v)
   {
    int x1 = x(p), y1 = y(p);
 
    if (y1 >= getSize().height) return;
 
    double v = f.v(abs_v);
 
    int x2 = x1 + (int) ((y1-getSize().height) * v);
    offGraphics.drawLine(x1,y1,x2,getSize().height);
   }
 
 
 }
