
import java.awt.*;
import java.awt.event.*;
import java.applet.Applet;

abstract public class PhysicsApplet extends Applet
                                              implements Runnable {

    //  data arrays on a 2-d lattice:   0 .. Nx ,  0 .. Ny
  int Nx, Ny;
  int pixelsize;
  int halfpix;
  int xsize, ysize;   // actual size of applet's main panel
  int leftmargin = 90;
  String title;
  TitleCanvas Title;
  Panel Picture;
  Panel Controls;
  int mouseMode;
  Thread solver;
  boolean timetostop;  

  PhysicsApplet(String thetitle, int nx, int ny, int Pixelsize){
    Nx = nx;  Ny = ny;
    pixelsize = Pixelsize;
    halfpix = pixelsize/2;
    xsize = 100 + nx*pixelsize;
    ysize = 100 + ny*pixelsize;
    title = thetitle;
    mouseMode = 0;
    timetostop = false;
  }

  public void init(){
    setLayout(new BorderLayout(0,0));
    Title = new TitleCanvas(title,24);
    add(Title,"North");
    Picture = new Panel();
    Picture.setBackground(Color.white);
    Picture.setLayout(new BorderLayout(0,10));
    buildPicture();
    add(Picture,"Center");
    Controls = new Panel();
    buildControls();
    add(Controls,"South");
  }


     //  Thread stuff

  public void startThread(){
     if (solver == null){
       timetostop = false;
       solver = new Thread(this);
       solver.start();
     }
  }
  
  public void stopThread(){
      timetostop = true; 
       /*  check for this condition in the loop of run() or solve()  */
  }

  public void run(){
      solve();
      solver = null;
  }
      
 
     //  classes for creating a GUI:

  public class TitleCanvas extends Canvas{
    
    String mytitle;
    Font TitleFont;
    FontMetrics fm;
    int Fontsize;

    TitleCanvas(String title, int fontsize){
      mytitle = title;
      Fontsize = fontsize;
      TitleFont = new Font("SansSerif",Font.PLAIN,fontsize);
      setBackground(Color.white);
      setFont(TitleFont);
      setSize(xsize,Fontsize+20);
      fm = getFontMetrics(TitleFont); 
   }
   
    public void paint(Graphics g){
      int width = fm.stringWidth(mytitle);
      int x = (xsize - width)/2;
      g.drawString(mytitle,x,Fontsize + 5);
    }

    public void write(String S){
      mytitle = S;
      repaint();
    }
  }

  class ArrayDisplay extends Canvas 
                     implements MouseListener, MouseMotionListener {

     double[][]  A;
     int[][]  ID;
   // ID describes the state of each grid point on the lattice
     Color Color1, Color2;
     Color[] DisplayColors;
     Color[] altDisplayColors;

     public ArrayDisplay(double[][]  AA, int[][] id,
           Color COLOR1, Color COLOR2, Color MAXDisplayColor, Color BASEalt){
       A = AA;
       ID = id;
       Color1 = COLOR1;
       Color2 = COLOR2;
       DisplayColors = new Color[11];
       altDisplayColors = new Color[11];
       int dR = 255 - MAXDisplayColor.getRed();
       int dG = 255 - MAXDisplayColor.getGreen();
       int dB = 255 - MAXDisplayColor.getBlue();
       int BRa = 255 - BASEalt.getRed();
       int BGa = 255 - BASEalt.getGreen();
       int BBa = 255 - BASEalt.getBlue();
       int dRa = ((255-BRa)* dR)/2550;
       int dGa = ((255-BGa)* dG)/2550;
       int dBa = ((255-BBa)* dB)/2550;
       for (int m = 0; m<= 10; m++){
         DisplayColors[m] = new Color(255-m*(dR/10),
                                               255-m*(dG/10),255-m*(dB/10));
         altDisplayColors[m] = new Color(255 - (BRa + dRa * m),
                        255 - (BGa + dGa * m), 255 - (BBa + dBa * m));
      }
      addMouseListener(this);
      addMouseMotionListener(this);
     }

    public void paint(Graphics g){
       g.setColor(Color.black);
       g.fillRect(leftmargin,0,2*pixelsize,(Ny+3)*pixelsize); 
       g.fillRect(leftmargin,0,(Nx+3)*pixelsize, 2*pixelsize); 
       g.fillRect(leftmargin,(Ny+1)*pixelsize,(Nx+3)*pixelsize,
                                              2*pixelsize); 
       g.fillRect(leftmargin+(Nx+1)*pixelsize, 0,2*pixelsize,
                                              (Ny+3)*pixelsize); 
       for (int i = 1; i <=  Nx-1; i++){
         for(int j = 1; j <= Ny-1; j++){
           paintCell(g,i,j);
         }
       }
       int hp = pixelsize/2;
       int nnx = Nx/10;
       int nny = Ny/10;
       g.setColor(Color.black);
       for(int i = 1 ; i <= nnx -1; i++){
         for (int j = 1; j <= nny -1; j++){
           g.fillRect(leftmargin + (10*i + 1)* pixelsize + hp, 
                                     (10*j+1)*pixelsize + hp, 2, 2);
         }
       }
   }

   public void update(Graphics g){
     paint(g);
   }

   public void refresh(){
     Graphics g = getGraphics();
     paint(g);
     g.dispose();
   }

   void paintCell(Graphics g, int i, int j){
      Color c = findColor(A[i][j],ID[i][j]);
      g.setColor(c);
      g.fillRect(leftmargin+(i+1)*pixelsize, (j+1)*pixelsize,pixelsize,
                                                      pixelsize);
   }

    //  mouse stuff

   public void mouseClicked(MouseEvent e){
     int i = (e.getX()-leftmargin)/pixelsize -1;
     int j = e.getY()/pixelsize -1;
     if ( i > 0 && i < Nx-1 && j > 0 && j < Ny-1){
       writeFieldValue(A[i][j], mouseMode);
     }
   }

   public void mouseDragged(MouseEvent e){
     int i = (e.getX()-leftmargin)/pixelsize -1;
     int j = e.getY()/pixelsize -1;
     if ( i > 1 && i < Nx-2 && j > 1 && j < Ny-2){
       Graphics g = getGraphics();
       for (int k = -1; k <= 1; k++){
	  for (int l = -1; l <= 1; l++){
            int ik = i + k;
            int jl = j + l;
            setArrays(ik,jl, mouseMode);
            paintCell(g,ik,jl);
          }
        }
     g.dispose();
     }
  }
        
   public void mousePressed(MouseEvent e){}
   public void mouseReleased(MouseEvent e){}
   public void mouseEntered(MouseEvent e){}
   public void mouseExited(MouseEvent e){}
   public void mouseMoved(MouseEvent e){}
 }
 
  class VectorDisplay extends Canvas 
                     implements MouseListener, MouseMotionListener {

      /* the grid square (i,j) has its uppper left-hand corner 
          at  (leftmargin + i*pixelsize, (Ny-j+1)*pixelsize)
          and its center at  
	(leftmargin + i*pixelsize + halfp, (Ny-j+1)*pixelsize + halfpix) */

     double[][]  Bx;
     double[][]  By;
     int[][]  ID;
   // ID describes the state of each grid point on the lattice
     double Bscale;
     Color Color1, Color2;

     public VectorDisplay(double[][]  BBx, double[][] BBy, int[][] id,
           double BScale, Color COLOR1, Color COLOR2){
       Bx = BBx;   By = BBy;
       ID = id;
       Bscale = BScale;
       Color1 = COLOR1;
       Color2 = COLOR2;
       addMouseListener(this);
       addMouseMotionListener(this);
     }

    public void paint(Graphics g){
       g.setColor(Color.black);
       g.fillRect(leftmargin,0,2*pixelsize,(Ny+3)*pixelsize); 
       g.fillRect(leftmargin,0,(Nx+3)*pixelsize, 2*pixelsize); 
       g.fillRect(leftmargin,(Ny+1)*pixelsize,(Nx+3)*pixelsize,
                                              2*pixelsize); 
       g.fillRect(leftmargin+(Nx+1)*pixelsize, 0,2*pixelsize,
                                              (Ny+3)*pixelsize); 
       for (int i = 1; i <=  Nx-1; i++){
         for(int j = 1; j <= Ny-1; j++){
           paintCell(g,i,j);
         }
       }
       for(int i = 10 ; i < Nx; i+= 10){
         for (int j = 10; j < Ny; j+= 10){
	   g.setColor(Color1);
           int X = (int) (Bx[i][j]/Bscale);
           int Y = (int) (By[i][j]/Bscale);
           int startx = leftmargin + (i+1)*pixelsize + halfpix - X/2;
           int starty = (Ny + 1 - j)*pixelsize + halfpix + Y/2;
           int endx = startx + X;
           int endy = starty - Y;
           g.setColor(Color1);
           g.drawLine(startx, starty, endx, endy);
           g.setColor(Color2);
           g.fillRect(endx-1, endy-1, 3,3);
           g.setColor(Color.black);
           g.fillRect(leftmargin + (i + 1)* pixelsize + halfpix-1, 
                                   (Ny+1-j)*pixelsize + halfpix-1, 3, 3);
         }
       }
     }

   public void update(Graphics g){
     paint(g);
   }

   public void refresh(){
     Graphics g = getGraphics();
     paint(g);
     g.dispose();
   }

   void paintCell(Graphics g, int i, int j){
      Color c = findColor(1.0,ID[i][j]);
      g.setColor(c);
      g.fillRect(leftmargin+(i+1)*pixelsize, (Ny+1-j)*pixelsize,pixelsize,
                                                      pixelsize);
   }

   public void resetscale(double Bsc){
     Bscale = Bsc;
   }

    //  mouse stuff

   public void mouseClicked(MouseEvent e){
     int i = (e.getX()-leftmargin)/pixelsize -1;
     int j = (Ny+2) - e.getY()/pixelsize;
     if ( i > 0 && i < Nx-1 && j > 0 && j < Ny-1){
       writeVectorValue(Bx[i][j], By[i][j],mouseMode);
     }
   }

   public void mouseDragged(MouseEvent e){
     int i = (e.getX()-leftmargin)/pixelsize -1;
     int j = (Ny+2) - e.getY()/pixelsize;
     if ( i > 1 && i < Nx-2 && j > 1 && j < Ny-2){
       Graphics g = getGraphics();
       for (int k = -1; k <= 1; k++){
	  for (int l = -1; l <= 1; l++){
            int ik = i + k;
            int jl = j + l;
            setArrays(ik,jl, mouseMode);
            paintCell(g,ik,jl);
          }
        }
     g.dispose();
     }
  }
        
   public void mousePressed(MouseEvent e){}
   public void mouseReleased(MouseEvent e){}
   public void mouseEntered(MouseEvent e){}
   public void mouseExited(MouseEvent e){}
   public void mouseMoved(MouseEvent e){}
 }
 
  class CurveDisplay extends Canvas 
                     implements MouseListener, MouseMotionListener {

     int height, zero;
     double[] f;
     double[] h;
     double fscale, hscale;
     Color fcolor, hcolor;
     int lastx, lasty;

     public CurveDisplay(int Height, int ZeroPosition, double [] F, double[] H,
                double Fscale, double Hscale, Color FColor, Color HColor){
       height = Height;
       zero = height + pixelsize - ZeroPosition;
       f = F;
       h = H;
       fscale = Fscale;
       hscale = Hscale;
       fcolor = FColor;
       hcolor = HColor;
       addMouseListener(this);
       addMouseMotionListener(this);
     }


    public void paint(Graphics g){
       g.setColor(Color.black);
       g.fillRect(leftmargin,0,pixelsize,height+2*pixelsize); 
       g.fillRect(leftmargin,0,(Nx+2)*pixelsize, pixelsize); 
       g.fillRect(leftmargin, height + pixelsize,(Nx+2)*pixelsize,
                                              pixelsize); 
       g.fillRect(leftmargin+(Nx+1)*pixelsize, 0,pixelsize,
                                              (Ny+2)*pixelsize); 
       g.drawLine(leftmargin, zero, leftmargin + (Nx+2)*pixelsize, zero);
       for (int i = 1; i <=  Nx; i++){
           paintCell(g,i);
       }
   }

   public void update(Graphics g){
     paint(g);
   }

   public void refresh(){
     Graphics g = getGraphics();
     paint(g);
     g.dispose();
   }

   void paintCell(Graphics g, int i){
      int left = leftmargin + i*pixelsize;
      g.setColor(fcolor);
      int flstep = (int) (f[i]/fscale);
      int frstep = (int) (f[i+1]/fscale);
      g.drawLine(left, zero - flstep, left + pixelsize, zero - frstep);
      g.setColor(hcolor);
      int hlstep = (int) (h[i]/hscale);
      int hrstep = (int) (h[i+1]/hscale);
      g.drawLine(left, zero - hlstep, left + pixelsize, zero - hrstep);
   }

    //  mouse stuff

   public void mousePressed(MouseEvent e){
     lastx = e.getX();
     lasty = e.getY();
   }

   public void mouseDragged(MouseEvent e){
     int newx = e.getX();
     int newy = e.getY();
     int lasti = (lastx - leftmargin)/pixelsize -1;
     double lastf = (zero -lasty) * fscale;
     int newi = (newx - leftmargin)/pixelsize - 1;
     double newf = (zero- newy) * fscale;
     double finc = (newf - lastf)/(newi - lasti);
     Graphics g = getGraphics();
     if (newi > lasti){
       if (lasti < 0) { 
           lastf += - finc * lasti;
           lasti = 0;
       }
       if (newi > Nx){
           newf -= finc * (newi - Nx);
           newi = Nx;
       }
       double ff = lastf;
       for (int i = lasti+1; i <= newi; i++){
           ff += finc;
           f[i] = ff;
           paintCell(g,i);
       }
    } else if ( newi < lasti) { 
       if (lasti > Nx) { 
           lastf -= finc * (lasti - Nx);
           lasti = Nx;
       }
       if (newi < 0){
           newf += - finc * newi;
           newi = 0;
       }
       double ff = lastf;
       for (int i = lasti-1; i >= newi; i--){
           ff += finc;
           f[i] = ff;
           paintCell(g,i+1);
       }
     }
   }
        
   public void mouseClicked(MouseEvent e){}
   public void mouseReleased(MouseEvent e){}
   public void mouseEntered(MouseEvent e){}
   public void mouseExited(MouseEvent e){}
   public void mouseMoved(MouseEvent e){}
 }

  class CommandButton extends Button{

    int Code;

    CommandButton(String name, int CommandCode){
      super(name);
      Code = CommandCode;
      addActionListener(new ActionListener(){
       public void actionPerformed(ActionEvent e){  
          doAction(Code); mouseMode = 0;}
      });
    }
  }
    
  class ModeButton extends Button{

    int Code;

    ModeButton(String name, int ModeCode){
      super(name);
      Code = ModeCode;
      addActionListener(new ActionListener(){
       public void actionPerformed(ActionEvent e){  
          mouseMode = Code; }
      });
    }
  }
    
  class BoldLabel extends Label{

   BoldLabel(String text){
      super(text);
      setFont(new Font("SansSerif",Font.PLAIN,12));
   }
  }
    
  class PhysicsScrollbar extends Scrollbar{

     int Code;
     double Scale;

     PhysicsScrollbar(double Value, double Minimum, double Maximum,
                                                     int CommandCode){
       super(Scrollbar.HORIZONTAL);
       Scale = (Maximum - Minimum)/200.0;
       int val = (int) (Value/Scale);
       int min = (int) (Minimum/Scale);
       int max = (int) (Maximum/Scale);
       setValues(val, 0, min, max);
       Code = CommandCode;
       addAdjustmentListener(new AdjustmentListener(){
          public void adjustmentValueChanged(AdjustmentEvent e){
                doAction(Code);
       } });
     }
    
     double getReading(){
       int val = super.getValue();
       double value = val * Scale;
       return value;
     }
  }

  abstract void buildPicture();
  abstract void resetArrays();
  abstract void resetAll();
  abstract void refreshPicture();
  abstract void buildControls();
  abstract Color findColor(double A, int S);
  abstract void writeFieldValue(double Val, int mode);
  abstract void writeVectorValue(double Vx, double Vy, int mode);
  abstract void setArrays(int i, int j, int mode);
  abstract void doAction(int Code);
  abstract void solve();
}























  
