import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.util.EventListener;
import java.util.Vector;
import java.util.Enumeration;
import java.lang.Math;



public class CoEffDet extends Applet implements Runnable {
    static final double LIGHTSPEED = 29.8 * 3.0;  
    int h, w;    
    static int oriX, oriY;
    Electron electron;
    Photon photon;
    Thread animThread;
    final static double deltaT = 0.2;
    boolean hit = false;
    InPanel inPanel;                  //a Panel for interactive input
    ComCanvas comCanvas;
    PlotCanvas plotCanvas;            //a Canvas for plotting the detected photon angles
    PhDetect phDetect;                //a photon detector
    static boolean shoot;
    static int numOfPh, n;
    static int plotW, drawH;
    
    public void init() {
	w = getSize().width;
	h = getSize().height;
	oriX = w/3;
        oriY = h/3 + 40;	
	make_photon();
	make_electron();
	plotW = (w/3)+ 40;
	drawH = h-70;	 	 
	numOfPh = 1;
	inPanel = new InPanel(this);
	phDetect = new PhDetect();    	
	comCanvas = new ComCanvas();
	plotCanvas = new PlotCanvas();	        	  
//	panH = inPanel.getSize().height;	
	comCanvas.setSize(w-plotW, drawH);
	plotCanvas.setSize(plotW, drawH);	
	setLayout(new BorderLayout());
	add("Center", comCanvas);	
	add("East", plotCanvas);
	add("South", inPanel);	
    }     
    
    class PhDetect extends Applet { 
	int dw = 30;
	int dh = 80;    
	int xFront, yTop; 
	int xFrontPrim, yTopPrim, yBotPrim;        
    
        PhDetect() { 
	    xFront = oriX + w/5;
	    yTop = oriY - dh/2;
	    xFrontPrim = xFront- oriX;
	    yTopPrim = -(yTop - oriY);
	    yBotPrim = yTopPrim-dh;
	}
	public void detPos(int xfr, int ytop) {
	    xFront=xfr;
	    yTop=ytop;
	    xFrontPrim = xFront - oriX;
	    yTopPrim = -(yTop - oriY);
	    yBotPrim = yTopPrim-dh;
	}
	public void draw(Graphics g) {
	    g.setColor(Color.black);
	    g.fillRect(xFront, yTop, dw, dh);
	}
    }    

    public class PlotCanvas extends Canvas {
	int plOriX, plOriY, xMin, xMax, yMin, yMax, txtLev, angX, xcoord, ycoord;
	double xAxis, yAxis;
//	int []angles;    
	Vector angles;
	Enumeration e;
	boolean update;

	PlotCanvas() {
	    setBackground(Color.lightGray);
	    plOriX =  plotW/2; 	
	    plOriY = drawH - 40;
	    xMin = 5;
	    xMax = plotW - 5; 
	    yMin = plOriY+7;
	    yMax = 40;
	    xAxis = (double)(xMax-plOriX);
	    yAxis = (double)(plOriY-yMax);
	    txtLev = plOriY+12;       	   	    
	    angles = new Vector();
	    update = false;
	    e = angles.elements();
	    while(e.hasMoreElements()) {
		System.out.println(e.nextElement().toString());
	    }
	}

	public void addAngle(double angle) {
	    xcoord=(int)(angle*(xAxis/Math.PI));
	    angles.addElement(new Integer(xcoord));
	    e = angles.elements();
	    while(e.hasMoreElements()) {
		System.out.println(e.nextElement().toString());	     
	    }

	    update = true;
	    repaint();
	}

	public void resetPlot() {
	    int i; 
	    e = angles.elements();
	    while(e.hasMoreElements()) {
		i=angles.indexOf(angles.firstElement());
		angles.removeElementAt(i);			
	    }	    
	    update = false;
	    repaint();
	}
	
	public void paint(Graphics g) {	    
	    g.setColor(Color.black);	
	    g.drawLine(0,0,0,drawH);
	    g.drawLine(xMin, plOriY, xMax, plOriY);
	    g.drawLine(plOriX, yMin, plOriX, yMax);
	    for(double a=(-xAxis); a<=xAxis; a+=(xAxis/3.0)) 
		g.drawLine(plOriX+(int)a, plOriY-3, plOriX+(int)a, plOriY+3);	      
	    g.setColor(Color.magenta);
	    g.drawString("Num. of photons", plOriX - 20, yMax - 8);
	    g.drawString("Scatt. angle", xMax - 60, plOriY - 20);
	    g.drawString("[deg.]", xMax - 26, plOriY - 8);
	    g.drawString("90 ", xMax-5, txtLev);
	    g.drawString("-90", xMin-5, txtLev);
	    g.drawString(" 0", plOriX, txtLev);	  
	    if(update) {
		g.setColor(Color.yellow); 
		e = angles.elements();	      
		double n =1.5;
		while(e.hasMoreElements()) {
		    xcoord = ((Integer)e.nextElement()).intValue();		
		    g.fillOval(plOriX+xcoord-2, plOriY-(int)(yAxis/n)-2, 4, 4);
		}
	    }
	    update = false;
	}
    }   	
    
    
    class ComCanvas extends Canvas implements MouseListener, MouseMotionListener{ 
	Image offScrImg;
	boolean inDet = false;
	int xM, yM, xInDet, yInDet;
	ComCanvas() {  
	    setBackground(Color.lightGray);
	    addMouseListener(this);
	    addMouseMotionListener(this);
	}
	public void mouseClicked(MouseEvent e) {}
	
	public void mouseEntered(MouseEvent e) {}
	
	public void mouseExited(MouseEvent e) {}
	
	public void mousePressed(MouseEvent e) {
	    xM = e.getX();
	    yM = e.getY();
	    if(xM>phDetect.xFront && xM < (phDetect.xFront+phDetect.dw) &&
	       yM>phDetect.yTop && yM < (phDetect.yTop+phDetect.dh)) {
		inDet = true;
		xInDet = xM - phDetect.xFront;
		yInDet = yM - phDetect.yTop;
	    }	
	}	    
	public void mouseReleased (MouseEvent e) {
	    if(inDet)
		inDet = false;
	}
	
	public void mouseMoved (MouseEvent e) {
	   
	    
	}
  
	public void mouseDragged (MouseEvent e) {
	    int x = e.getX();
	    int y = e.getY();
	    if (inDet && x>0 && x<w-plotW && y>0 && y<drawH) {	   
		phDetect.detPos(x - xInDet, y - yInDet);
	    }	
	    repaint();	    
	}

	
	public void update(Graphics g) {
	    if (offScrImg == null) 
		offScrImg = createImage((w-plotW), drawH);
	    Graphics offg = offScrImg.getGraphics();
	    paint(offg);                //draw on an offscreen image
	    g.drawImage(offScrImg, 0, 0, this);
	    offg.dispose();             //to get rid of the offscreen graphics context
	}
	
	public void paint(Graphics g) {	  
	    g.setColor(getBackground());
	    g.fillRect(0, 0, (w- plotW) , drawH); 
	    photon.draw(g); 
	    electron.draw(g);	    
	    phDetect.draw(g);
	}	    	
    }
    
    public void make_photon() {
	int xPos = -w/3; 
	int yPos = 0;	
	double theta = 0.0;
	double lambda = 20.0;
	photon = new Photon(xPos, yPos, theta, lambda);  
    }

    public void make_electron() {
	int xPos = 0;
	int yPos = 0;
	double fi = 0.0;
	double v = 0.0;	
	electron = new Electron(xPos, yPos, fi, v);
    }

    boolean collision(Electron electron, Photon photon) {
	return((Math.abs(photon.xPos - electron.xPos)) < 6 );
    }       
    
    boolean visible(Electron electron, Photon photon) {
	return((((Math.abs(photon.xPos - (Math.cos(photon.trLength)))) < w/2) &&
		 ((Math.abs(photon.yPos - (Math.sin(photon.trLength)))) < h/2)) ||
		((Math.abs(electron.xPos) <= w/2) &&
		 (Math.abs(electron.yPos) <= h/2)));
    }

    boolean detection() {
	return((Math.abs(photon.xPos - phDetect.xFrontPrim) < 10) &&
	       (photon.yPos <= phDetect.yTopPrim +5) && (photon.yPos >= phDetect.yBotPrim-5));
    }    
    
    public void start() {
	if(animThread == null) {	   
	    animThread = new Thread(this);
	    animThread.start();
	}
    }
    
    public void stop() {
	animThread = null;
    }       
    
    public void run() {
	
	while(animThread != null) {
	    n = numOfPh;
	    while(shoot && visible(electron, photon) && numOfPh>0) {		
		try{
		    Thread.sleep(100);
		}
		catch(InterruptedException e) {}
		
		if(!hit && collision(electron, photon)) {
		    setCollisionValues(electron, photon);		    		
		    hit = true;
		}
		if(detection()) { 
		    photon.erase = true;		    
		    plotCanvas.addAngle(photon.theta);
		}
		electron.move(deltaT);
		photon.move(deltaT);
		comCanvas.repaint();		    
	    }			    	 
	    if(shoot) {              //resets when the particles are out of the screen
		hit = false;
		photon.erase = false;		
		electron.restart();
		photon.restart();	      
		comCanvas.repaint();
		if(numOfPh>0)
		    numOfPh--;		
		if(numOfPh==0) {
		    shoot = false;
		}
	    }
	}		
    }
    

    
    private void setCollisionValues(Electron electron, Photon photon) {	
//a random scattering angle for the photon	
	double theta = Math.random()*Math.PI/2.0;
	theta = Math.cos(theta); 
	if(Math.random()<=0.5) {
	    theta = -theta;
	}
	double new_lambda = photon.lambda*1.5;
	double new_f = photon.c0/new_lambda; 
	double fi = - Math.atan(new_f * Math.sin(theta) /
				(photon.f - new_f * Math.cos(theta)));
	double new_v = 60.0; 
	electron.setMotion(fi, new_v);
	photon.setMotion(theta, new_lambda);	
    }   
}

/*---------------------------------------------*/	
//The abstract base class for a particle:

abstract class Particle {
    
    double xPhysical, yPhysical;
    double v, vx, vy, angle;
    int xPos, yPos;
    int oriX, oriY;   
    static final double PI = Math.PI;
    
    Particle(int xPos, int yPos, double angle, double v) {
	this.xPos = xPos;   //Pos is always the position in the "created" coordinate system.
	this.yPos = yPos;
	this.angle = angle;
	this.v = v;
	oriX = CoEffDet.oriX;
	oriY = CoEffDet.oriY;
	xPhysical = xPos;
        yPhysical = yPos;  
	vx = v*Math.cos(angle);
	vy = v*Math.sin(angle);
    }    
    abstract public void setMotion(double new_angle, double newVOrL);	
    abstract public void move(double deltaT);     

    abstract public void draw(Graphics g);
    abstract public void restart();
}	


/*---------------------------------------------*/    	

class Photon extends Particle {
    double lambda = 22;
    double pre_lambda;
    double f;
    static double theta;
    double c0, c0x, c0y;    
    double cosTheta;
    double sinTheta;
    double time = 0;
    int trLength = 65;     //the length of the wave train
    int pre_trLength;
    double ampl = 15.0;       
    final int dx = 2;
    boolean erase;
    
    Photon(int xPos, int yPos, double theta, double lambda) {  	
	super(xPos, yPos, theta, CoEffDet.LIGHTSPEED);	
	this.lambda = lambda;
	c0 = v;	 
        c0x = vx;
	c0y = vy;	
	f = c0/lambda;
	cosTheta = Math.cos(theta);
	sinTheta = Math.sin(theta);
	erase = false;
    }
    
    public void setMotion(double new_angle, double new_lambda) {
	this.theta = new_angle;
	pre_lambda = lambda;
	lambda = new_lambda; 
	cosTheta = Math.cos(theta);
	sinTheta = Math.sin(theta); 
        c0x = c0*cosTheta;
	c0y = c0*sinTheta; 
	f = c0/lambda;
	pre_trLength = trLength;
	trLength = (int) (trLength * (lambda / pre_lambda));
    }

    public void move(double deltaT) {
	xPhysical = xPhysical + c0x*deltaT;
        yPhysical = yPhysical + c0y*deltaT;
        xPos = (int)xPhysical;
        yPos = (int)yPhysical; 
	time = time + deltaT;	
    }

    public void draw(Graphics g) {
	int xOldAppl, yOldAppl, xOld, xOldP, yOldP, xAppl, yAppl, yP, xP;
	double y;
	int sign = xPos >= 0 ? 1 : -1;
        int dist =(int)Math.sqrt(xPos*xPos + yPos*yPos);
        dist = sign*dist;
	xOld = dist;
	double yOld = ampl*(Math.sin((2.0*PI*f*time) + (2.0*(PI/lambda)*xOld))); 
	if (theta == 0) { 
	    g.setColor(Color.magenta);  //(0.7,0.0,0.3);
	    xOldAppl = oriX + xOld;
	    yOldAppl = oriY - (int)yOld;
	}
	else{	    
	    g.setColor(Color.red);
	    if(erase) {
		g.setColor(Color.lightGray);
	    }
	    xOldP = (int)(xOld*cosTheta - yOld*sinTheta);
	    yOldP = (int)(xOld*sinTheta + yOld*cosTheta);	
	    xOldAppl = oriX + xOldP;
	    yOldAppl = oriY - yOldP;
	}
	for(int x = dist ; x>= dist-trLength; x-=dx) { 	    
	    y = ampl*(Math.sin((2.0*PI*f*time) + (2.0*(PI/lambda)*x)));	 	 
	    xP = (int)(x*cosTheta - y*sinTheta);
	    yP = (int)(x*sinTheta + y*cosTheta);	
	    xAppl = oriX + xP;
	    yAppl = oriY - yP;	
	    g.drawLine(xOldAppl, yOldAppl, xAppl, yAppl);
	    xOldAppl = xAppl;
	    yOldAppl = yAppl;
	}
    }    	   
    
    public void restart() {
	xPos = -oriX;
	yPos = 0;
	xPhysical = xPos;
        yPhysical = yPos; 
	theta = 0.0;	     
	lambda = pre_lambda;
	trLength = pre_trLength;
	setMotion(theta, lambda);
	erase = false;	
    }    
}	


/*---------------------------------------------*/    	

class Electron extends Particle { 
    final int radius = 5;
    int xAppl, yAppl, xFill, yFill;
    
    Electron(int xPos, int yPos, double fi, double v) { 
	super(xPos, yPos, fi, v); 
    }
    
    public void setMotion(double new_fi, double new_v){
	angle = new_fi;
	v = new_v;
	vx = v*Math.cos(angle);
	vy = v*Math.sin(angle);	
    }
    
    public void move(double deltaT) {
        xPhysical = xPhysical + vx*deltaT;
        yPhysical = yPhysical + vy*deltaT;
        xPos = (int)xPhysical;
        yPos = (int)yPhysical;		
    }

    public void draw(Graphics g) {
	g.setColor(Color.blue);
	xAppl = oriX + xPos;
	yAppl = oriY - yPos;
	xFill = xAppl - radius;
	yFill = yAppl - radius;
	g.fillOval(xFill, yFill, 2*radius, 2*radius);
    }

    public void restart() {
	xPos = 0;
	yPos = 0;
	xPhysical = xPos;
        yPhysical = yPos;
	angle = 0.0;
	v = 0.0;
	setMotion(angle, v);
    }
}


