import java.applet.*;
import java.awt.*;
import java.util.*;

public class ChargeApplet extends NoFlickerApplet implements Runnable{
     ChargeField cf;
     boolean taint=false;

     public void init(){
	  cf = new ChargeField();
     }
     public void start(){
	  (new Thread(this)).start();
     }
     public void run(){
	  Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
	  while(true) {
	       System.out.println("Testing...");
	       if(taint) {
		    taint=false;
		    cf.fieldLines = new Vector();
		    doFieldLines();
		    repaint();
	       }
	       try {Thread.currentThread().sleep(3000); }
	       catch (Exception e) {}
	       Thread.yield();
	  }
     }
     public void stop(){
     }
     public void paint(Graphics g){
	  int i;
	  for(i = 0; i < cf.charges.size(); i++){
	       ((Charge) cf.charges.elementAt(i)).draw(g);
	  }
	  for(i = 0; i < cf.fieldLines.size(); i++){
	       ((FieldLine) cf.fieldLines.elementAt(i)).draw(g);
	  }
     }

     public synchronized boolean mouseDown(Event e, int x, int y){
	  cf.charges.addElement(new Charge(x, y, 1));
	  System.out.println("Click!");
	  taint = true;
	  return true;
     }
     
     public void doFieldLines() {
	  int x, y;
	  for(x=0; x < 250; x+=40){
	       for(y=0; y<250;y+=40){
		    cf.fieldLines.addElement(new FieldLine(cf, x, y));
	       }
	  }
	  repaint();
     }
}

class ChargeField {
     Vector charges = new Vector();
     Vector fieldLines = new Vector();
     
     public Field field(double x, double y) throws TooCloseException {
	  Field f = new Field(0,0);
	  for(int i=0; i < charges.size(); i++){
	       f = f.plus(((Charge) charges.elementAt(i)).field(x,y));
	  }
	  return f;
     }
}

class Charge {
     double value;
     double x, y;

     Charge(int x, int y, int v){
	  value = v;
	  this.x = x;
	  this.y = y;
     }

     public Field field(double x, double y) throws TooCloseException {
	  double dx, dy;
	  double d, fx, fy;
	  dx = this.x-x;
	  dy = this.y-y;
	  d = Math.sqrt((dx*dx) +(dy*dy));
	  fx = (3000*value*dx)/(d*d*d);
	  fy = (3000*value*dy)/(d*d*d);
	  if((fx*fx < 75) && (fy*fy <75)){
	       return new Field(fx, fy);
	  }
	  else{
	       throw new TooCloseException();
	  }
     }
     public void draw(Graphics g){
	  g.fillArc((int) x-4, (int) y-4, 8, 8, 0, 360);
     }
}

class FieldLine {
     Vector segments = new Vector();

     FieldLine(ChargeField f, double x, double y){
	  double forwx, forwy;
	  double backx, backy;

	  forwx = x; forwy = y;
	  backx = x; backy = y;

	  try {
	       for(int i=0;i<250;i++){
		    Field ff = f.field(forwx,forwy);
		    segments.addElement(new LineSegment(forwx,forwy,forwx+ff.x,forwy+ff.y));
		    forwx += ff.x;
		    forwy += ff.y;
	       }
	  } catch (TooCloseException e) {
	  }

	  try {
	       for(int i=0;i<250;i++){
		    Field ff2 = f.field(backx,backy);
		    segments.addElement(new LineSegment(backx,backy,backx-ff2.x,backy-ff2.y));
		    backx -= ff2.x;
		    backy -= ff2.y;
	       }
	  } catch (TooCloseException e) {
	  }

     }

     public void draw(Graphics g){
	  for(int i=0;i<segments.size();i++){
	       ((LineSegment) segments.elementAt(i)).draw(g);
	  }
     }
}

class Field {
     double x, y;

     Field(double x, double y){
	  this.x = x;
	  this.y = y;
     }
     
     public Field plus(Field f){
	  if(f == null){
	       return new Field(this.x, this.y);
	  }
	  return new Field(f.x+this.x, f.y+this.y);
     }
}

class LineSegment {
     double startx, starty;
     double endx, endy;

     LineSegment(double a, double b, double c, double d){
	  startx = a; starty = b;
	  endx = (int) c; endy = (int) d;
     }
     LineSegment(int a, int b, int c, int d){
	  startx = a; starty = b;
	  endx = c; endy =d;
     }

     public void draw (Graphics g){
	  g.drawLine((int) startx, (int) starty, (int) endx, (int) endy);
     }
}

class TooCloseException extends Exception {

}
