/*
   Programmer:  Konstantin Lukin
   E-mail    :  lukink@ug.cs.sunysb.edu 
*/

import java.awt.*;
import java.applet.*;
import java.net.*;

public class GraphCanvas extends Panel {  

private boolean loaded;
private Image degree, armstrong, theta, lambda;
private static final int WIDTH = 600;
private static final int HEIGHT = 250;
private static final int MARGIN = 90;
private int width = 600;
private int height = 350;
private boolean detailsOnScreen = false;
private Button button;
private int length; 
private Bragg bragg;
private SineWave[] sine;
private Segment[] beam;  
private Segment[] plane;
private Segment[] dash;
private Handle[] handle;
private Rectangle detailBox;
private Details details;
private Image offImage;
private Graphics offGraphics;
private Font font = new Font("Helvetica", Font.BOLD, 12);
private FontMetrics fm;
private UserInterface applet;


//--------------------CONSTRUCTOR------------------------   
public GraphCanvas(UserInterface _applet) {
      applet  = _applet;
      loaded  = false;
      bragg   =  new Bragg(Bragg.FORMULA, 3, 3, 30);
      sine    = new SineWave[4];
      beam    = new Segment[4];
      plane   = new Segment[2];
      dash    = new Segment[3];
      handle  = new Handle[2];
      details = new Details(10,10,WIDTH/2-20,HEIGHT-20);
      try {
         URL url;
         url = new URL(applet.getCodeBase(), "degree.gif");
         degree = applet.getImage(url);
         url = new URL(applet.getCodeBase(), "armstron.gif");
         armstrong = applet.getImage(url);
         url = new URL(applet.getCodeBase(), "theta.gif");
         theta = applet.getImage(url);
         url = new URL(applet.getCodeBase(), "lambda.gif");
         lambda = applet.getImage(url);
      }
      catch(MalformedURLException e) {applet.showStatus("Exception: "+e);}
}

//--------------------UPDATE----------------------------
public void paint(Graphics g) {
  if (!loaded) {
     loadLayout();
     loaded = true;
     offImage = this.createImage(WIDTH, HEIGHT);
     offGraphics = offImage.getGraphics();
     offGraphics.setFont(font);
     fm = offGraphics.getFontMetrics();
  }
  update(g);
}


public void update(Graphics g) {
   drawBackground();
   if (bragg==null) drawStart(); 
   else  {  
      drawGraph();        
      if(detailsOnScreen)  details.draw(offGraphics, sine[2], sine[3], bragg);
   }
   g.drawImage(offImage, 0, 0, null);
}
   

//-------------------------SET GRAPH---------------------------   
public synchronized void setGraph(Bragg be)  {  
  bragg = be;
  repaint();
}


//-------------------------MOUSE ACTIONS----------------------
public boolean action(Event evt, Object arg) {
   if(evt.target instanceof Button) {
      detailsOnScreen =! detailsOnScreen;
      if(detailsOnScreen) button.setLabel("close");
      else button.setLabel("details");
   }
   else return super.action(evt, arg);
   repaint();
   return true;
}


public boolean mouseDown(Event evt, int x, int y)   {  
   for(int i=0; i<handle.length; i++) {
      if(handle[i].inside(x,y)) {
         handle[i].setDragable(true);
         return true;
      }
   }
   return super.mouseDown(evt, x, y);
}


public boolean mouseUp(Event e, int x, int y) {
   for(int i=0; i<handle.length; i++) {
      if(handle[i].isDragable()) {
         handle[i].setDragable(false);
         return true;
      }
   }
   return super.mouseUp(e, x, y);
}


public boolean mouseDrag(Event e, int x, int y) {
  if(handle[0].isDragable()) {
    int distance = 2*(height/2-y);
    if(Bragg.DistanceOutOfRange(distance, Bragg.GRAPH)) {
       if(distance < Bragg.distanceLimit.x * Bragg.FormulaToGraph) 
          distance = (int)(Bragg.distanceLimit.x * Bragg.FormulaToGraph);
       else 
          distance = (int)(Bragg.distanceLimit.y * Bragg.FormulaToGraph);
    }
    bragg = new Bragg(Bragg.GRAPH, bragg.getLambda(Bragg.GRAPH),
                                     distance, bragg.getTheta());
    setDistComponent(distance);
  }
  else if(handle[1].isDragable()) {
    float angleInDegrees = -MyMath.getAngle(beam[1].getStart(),
                                              new Point(x, y));
    if(Bragg.ThetaOutOfRange(angleInDegrees)) {
      if(angleInDegrees < Bragg.thetaLimit.x) 
         angleInDegrees = Bragg.thetaLimit.x;
      else
         angleInDegrees = Bragg.thetaLimit.y;
    }
    bragg = new Bragg(Bragg.FORMULA, bragg.getLambda(Bragg.FORMULA), 
                      bragg.getDistance(Bragg.FORMULA), angleInDegrees);
    setThetaComponent(angleInDegrees);
  }
  else  return super.mouseDrag(e,x,y);
  repaint();
  return true;
}     

//----------------------SET COMPONENTS-----------------------
private void setDistComponent(float value) {
  value = MyMath.round(value/20, 2);
  applet.setDistTf(""+value);
}

private void setThetaComponent(float angle) {
  angle = MyMath.round(angle, 2);
  applet.setThetaTf(""+angle);
}

      
//-------------------------------DRAW GRAPH---------------------  
public void drawGraph()   {  
  if (bragg == null) return;      
  drawPlanes(offGraphics);
  drawXray1(offGraphics);
  drawXray2(offGraphics);
  drawBox(offGraphics);
  draw90Angles(offGraphics);
  drawAtoms(offGraphics);
  drawDistMeasure(offGraphics);
  drawLambdaMeasure(offGraphics);
  drawTheta(offGraphics);
  drawWaves(offGraphics);
  drawDistanceBox(offGraphics);
  drawThetaBox(offGraphics);
}

    
// --------------------GET  DISTANCE-----------------
private float getDistance()
   {  float distance = bragg.getDistance(Bragg.GRAPH);
      float theta = MyMath.toRadians(bragg.getTheta()); // in Radians
      float d = distance * (float)Math.sin(theta);
      return d;
   }
 
//---------------------------DRAW HANDLES-----------------
private void drawDistanceBox(Graphics g) {
   // width = 8, height = 18
   if(handle[0]==null) {
      handle[0]=new Handle(Handle.VERTICAL, plane[0].getStop().x-30, 
                           plane[0].getStop().y-9, 8, 18);
   }
   handle[0].move(handle[0].x, plane[0].getStop().y-handle[0].height/2);
   g.setColor(Color.white);
   handle[0].draw(offGraphics);
}


public void drawThetaBox(Graphics g) {
   // width = 8, height = 18
   int x = beam[1].getStop().x - 30;
   int y = beam[1].getY( x );

   if(handle[1]==null) {
      handle[1] = new Handle(Handle.VERTICAL, x-4, y-9, 8, 18);
   }
   handle[1].move(x-4, y-9);
   g.setColor(Color.white);
   handle[1].draw(g);
}
 
     
//-------------------------GRAPH IMPLEMENTATION---------
private void drawPlanes(Graphics g)  {  
  int h1 = (height-(int)bragg.getDistance(Bragg.GRAPH))/2;
  int h2 = (height+(int)bragg.getDistance(Bragg.GRAPH))/2;
  g.setColor(Color.black);
  plane[0].moveTo(MARGIN, h1, width-MARGIN, h1);
  plane[0].drawThick(g, 3);
  plane[1].moveTo(MARGIN, h2, width-MARGIN, h2);
  plane[1].drawThick(g, 3);
}


private void drawDistMeasure(Graphics g) {  
  int w = MARGIN+10;
  int h1 = plane[0].getStart().y;
  int h2 = plane[1].getStart().y;

  Arrow arrow1 = new Arrow(w-3, h1, 6, 15, Arrow.NORTH);
  Arrow arrow2 = new Arrow(w-3, h2-15, 6, 15, Arrow.SOUTH);
  g.setColor(Color.black);
  g.drawLine(w, h1, w, h2);
  arrow1.fill(g);
  arrow2.fill(g);

  String s="d = "+MyMath.round(bragg.getDistance(Bragg.FORMULA), bragg.precision);

  g.setColor(Color.blue);
  g.drawString(s, w+5, (h2+h1)/2+3);
  int i = fm.stringWidth(s);
  g.drawImage(armstrong, w+5+i, (h2+h1)/2-12, 22, 22, this);
}


private void drawXray1(Graphics g)   {  
  // find the starting point
  int i = (int)((width/2-MARGIN)*Math.tan(MyMath.toRadians(bragg.getTheta() )));
  int high = plane[0].getStart().y-i;
  Segment temp = new Segment(MARGIN, high, width/2, plane[0].getStop().y);
  Point start = new Point(0,0);
  if (temp.getStart().y < 0)  { 
    start.x = temp.getX(0);
    start.y =0;
  }
  else   {  
    start.x = temp.getStart().x;
    start.y = temp.getStart().y;
  }
  temp = null; //freeing memory
  g.setColor(Color.green);   
  beam[0].moveTo(start.x, start.y, width/2, plane[0].getStart().y);
  beam[0].drawThick(g, 2);
  beam[1].moveTo(width/2, plane[0].getStart().y, width-start.x, start.y);
  beam[1].drawThick(g, 2);
}

      
private void drawXray2(Graphics g) {
   int d = (int)(bragg.getDistance(Bragg.GRAPH));
   this.length = Math.round((float)(d * Math.cos(
                             MyMath.toRadians(bragg.getTheta()) )));
   Point center = beam[0].getStart();
   Point ray2Start = MyMath.rotate(center, bragg.getTheta()+90, length);
        
   g.setColor(Color.green);   
   beam[2].moveTo(ray2Start.x, ray2Start.y, width/2, plane[1].getStart().y);
   beam[2].drawThick(g, 2);
         
   beam[3].moveTo(width/2, plane[1].getStart().y, width-ray2Start.x, ray2Start.y);
   beam[3].drawThick(g, 2);
}
   
private void drawTheta(Graphics g) {  
   String s;
   Point center, start;
   float temp;
   center = new Point(width/2, plane[0].getStart().y);
   start = MyMath.rotate(center, -bragg.getTheta()/3, 50);

   g.setColor(Color.blue); 
   temp = MyMath.round(bragg.getTheta(), bragg.precision);
   s = Float.toString(temp);
   g.drawImage(theta, start.x, start.y-12, 12, 14, this);
   g.drawString("= "+s, start.x+14, start.y);
   g.drawImage(degree, start.x+24+fm.stringWidth(s), start.y-15, 9, 9, this); 

   g.setColor(Color.black);
   g.drawArc(center.x-45, center.y-45, 90, 90, 0, (int)bragg.getTheta());
} 
   
   
private void drawBox(Graphics g)   {  
   Point center = new Point(width/2, plane[0].getStart().y);
   Point stop1 = MyMath.rotate(center, 90+bragg.getTheta(), length);
   g.setColor(Color.black);
   dash[0].moveTo(stop1.x, stop1.y, center.x, center.y);
     dash[0].drawDashed(g, 5);
   Point stop2 = MyMath.rotate(center, 90-bragg.getTheta(), length);   
   dash[1].moveTo(stop2.x, stop2.y, center.x, center.y);
     dash[1].drawDashed(g, 5);
   dash[2].moveTo(center.x, center.y, center.x-1, plane[1].getStart().y);
     Segment.drawVertDashed(g, center.x, center.y, width/2, plane[1].getStart().y, 5);
}
   
private void draw90Angles(Graphics g)   {  
   Point center = dash[0].getStart();
   Point newCenter = MyMath.rotate(center, 225+bragg.getTheta(), 12);
   Point left = MyMath.rotate(newCenter, bragg.getTheta(), 8);
   g.drawLine(newCenter.x, newCenter.y, left.x, left.y);
   Point right = MyMath.rotate(newCenter, bragg.getTheta()+90, 8);
   g.drawLine(newCenter.x, newCenter.y, right.x, right.y);
   center = dash[1].getStart();
   newCenter = MyMath.rotate(center, 315-bragg.getTheta(), 12);
   left = MyMath.rotate(newCenter, 90-bragg.getTheta(), 8);
   g.drawLine(newCenter.x, newCenter.y, left.x, left.y);
   right = MyMath.rotate(newCenter, 180-bragg.getTheta(), 8);
   g.drawLine(newCenter.x, newCenter.y, right.x, right.y);
}
   
private void drawAtoms(Graphics g)   {
   int h1 = plane[0].getStop().y;
   int h2 = plane[1].getStop().y;
   int w = 10;  // width and height of each atom 
   int half = w/2;

   // two middle atoms
   g.setColor(Color.red);
   g.fillOval(width/2 - half, h1 - half, w, w);
   g.fillOval(width/2 - half, h2 - half, w, w);

   // fill plane[0] with atoms
   g.setColor(new Color(90, 90, 90)); //dark gray
   g.fillOval(width/6  -half, h1-half, w, w);
   g.fillOval(width/3  -half, h1-half, w, w);
   g.fillOval(2*width/3-half, h1-half, w, w);
   g.fillOval(5*width/6-half, h1-half, w, w);

   // fill plane 2 with atoms
   g.fillOval(width/6  -half, h2-half, w, w);
   g.fillOval(width/3  -half, h2-half, w, w);
   g.fillOval(2*width/3-half, h2-half, w, w);
   g.fillOval(5*width/6-half, h2-half, w, w);
      
   g.setColor(Color.black);
   g.drawOval(width/6  -half, h1-half, w, w);
   g.drawOval(width/3  -half, h1-half, w, w);
   g.drawOval(width/2  -half, h1-half, w, w);
   g.drawOval(2*width/3-half, h1-half, w, w);
   g.drawOval(5*width/6-half, h1-half, w, w);
     
   g.drawOval(width/6  -half, h2-half, w, w);
   g.drawOval(width/3  -half, h2-half, w, w);
   g.drawOval(width/2  -half, h2-half, w, w);
   g.drawOval(2*width/3-half, h2-half, w, w);
   g.drawOval(5*width/6-half, h2-half, w, w);
}
   
   
private void drawLambdaMeasure(Graphics g)   {  
   g.setColor(Color.blue);   
   g.drawImage(lambda, 10, 101-fm.getHeight(), 15, 15,this);
   String s = "= " + Float.toString(MyMath.round(
                     bragg.getLambda(Bragg.FORMULA), 2));
   g.drawString(s, 26, 100); 
   g.drawImage(armstrong, 26+fm.stringWidth(s), 99-fm.getHeight(), 22, 22, this);
}
  

private void drawWaves(Graphics g)   {  
   int bPHASE = 45;
   int AMPLITUDE = 15;
   g.setColor(Color.darkGray);
   sine[0] = new SineWave(bragg.getLambda(Bragg.GRAPH), AMPLITUDE, bPHASE);
     sine[0].draw(g, beam[0].getStart(), beam[0].getStop());
   sine[1] = new SineWave(bragg.getLambda(Bragg.GRAPH), AMPLITUDE, bPHASE);
     sine[1].draw(g, beam[2].getStart(), beam[2].getStop());
      
   float phase3 = sine[0].getPhase(beam[0].length());
   sine[2] = new SineWave(bragg.getLambda(Bragg.GRAPH), AMPLITUDE, phase3);
     sine[2].draw(g, beam[1].getStart(), beam[1].getStop());
      
   float phase4 = sine[1].getPhase(beam[0].length()+getDistance());
   sine[3] = new SineWave(bragg.getLambda(Bragg.GRAPH), AMPLITUDE, phase4);
     sine[3].draw(g, beam[3].getStart(), beam[3].getStop());
}


//------------------------BACKGROUND------------------------------   
private void drawBackground()   { 
  // draw background color      
  offGraphics.setColor(Color.gray);
  offGraphics.fill3DRect(0, 0, WIDTH, HEIGHT, true);
  offGraphics.setColor(Color.black);
  offGraphics.drawRect(0, 0, WIDTH-1, HEIGHT-1);

  // draw all text strings 
  offGraphics.drawString("Incident", 10, 20);
  offGraphics.drawString("beam", 10, 35);
  String s = "Scattered";
  offGraphics.drawString(s, WIDTH-this.fm.stringWidth(s)-10, 20);
  offGraphics.drawString("beam", WIDTH-this.fm.stringWidth(s)-10, 35);
  offGraphics.drawString("Atomic planes", 90, HEIGHT-10);
 
  // draw arrows      
  offGraphics.drawLine(10, 45, 44, 45); // arrows
  offGraphics.drawLine(10, 53, 44, 53);
  offGraphics.drawLine(42, 39, 56, 49);
  offGraphics.drawLine(42, 59, 56, 49);
      
  offGraphics.drawLine(10, 46, 44, 46); // arrows
  offGraphics.drawLine(10, 54, 44, 54);
  offGraphics.drawLine(42, 40, 56, 50);
  offGraphics.drawLine(42, 60, 56, 50);
      
  offGraphics.drawLine(WIDTH-66, 45, WIDTH-32, 45);  // arrows
  offGraphics.drawLine(WIDTH-66, 53, WIDTH-32, 53);
  offGraphics.drawLine(WIDTH-34, 39, WIDTH-20, 49);
  offGraphics.drawLine(WIDTH-34, 59, WIDTH-20, 49);
      
  offGraphics.drawLine(WIDTH-66, 46, WIDTH-32, 46);  
  offGraphics.drawLine(WIDTH-66, 54, WIDTH-32, 54);
  offGraphics.drawLine(WIDTH-34, 40, WIDTH-20, 50);
  offGraphics.drawLine(WIDTH-34, 60, WIDTH-20, 50); 
}   
    
//-------------------------DRAW DEFAULT-------------------------
   private void drawStart() {  
      // draw planes
      offGraphics.drawLine(70, HEIGHT/2-40, WIDTH-70, HEIGHT/2-40);
      offGraphics.drawLine(70, HEIGHT/2, WIDTH-70, HEIGHT/2);
      offGraphics.drawLine(70, HEIGHT/2+40, WIDTH-70, HEIGHT/2+40);

      // draw rays for each plane
      offGraphics.drawLine(70, 30, WIDTH/2, HEIGHT/2-40);
      offGraphics.drawLine(WIDTH-70, 30, WIDTH/2, HEIGHT/2-40);
      
      offGraphics.drawLine(70, 70, WIDTH/2, HEIGHT/2);
      offGraphics.drawLine(WIDTH-70, 70, WIDTH/2, HEIGHT/2);
      
      offGraphics.drawLine(70, 110, WIDTH/2, HEIGHT/2+40);
      offGraphics.drawLine(WIDTH-70, 110, WIDTH/2, HEIGHT/2+40);

      // draw perpendicular line
      offGraphics.drawLine(WIDTH/2, HEIGHT/2-40, WIDTH/2, HEIGHT/2+40);

      // write the equation
      String s = "Demonstration of Bragg's Law";
      offGraphics.drawString(s, WIDTH/2-this.fm.stringWidth(s)/2, 30);
      s = " Y = 2d sin(\u03D8) ";
      offGraphics.drawString(s, WIDTH/2-this.fm.stringWidth(s)/2, 50);
   }



private void loadLayout() {
   for(int i=0; i<beam.length; i++) {
      sine[i] = new SineWave(0,0,0);
      beam[i] = new Segment(0,0,0,0);
   }
   for(int i=0; i<plane.length; i++) {
      plane[i] = new Segment(0,0,0,0);
   }
   for(int i=0; i<dash.length; i++) {
      dash[i] = new Segment(0,0,0,0);
   }
   button = new Button("details");
   add(button);
   button.reshape(WIDTH-72, 125, 65, 25);    
}








}



