// package GravitySimulation;
import java.util.*;
import java.awt.*;
import java.awt.event.*;

import edu.neu.ccs.util.JPTUtilities;
import edu.neu.ccs.gui.*;

public class Mover implements Runnable{
	private volatile Thread me;     //this thread (stored so it can safely terminate later)
	public final int max = 1000;    //max number of balls allowed on canvas
	//Ball ball[] = new Ball[max];    //array of ball objects on the canvas
	ArrayList ball = new ArrayList();
	boolean dragging = false;       //is a ball being dragged
	Controls control;      //the panel responsible for controlling variables
	int curBall = 0;       //current ball being moved(if applicable)
	int numBalls = 0;      //total number of balls on canvas
	int width, height;     //width and height of canvas
	int mx, my;            //current mouse x and y position
	double grav = 0.1;     //value of gravity
	boolean eMouse = true; //enable use of mouse if true
	boolean pauseAnim;     //flag that pauses the animation
	BufferedPanel b;       //buffered panel to paint on
	boolean remBall = false;
    
    @SuppressWarnings("serial") MouseAction mousePressedAction =
        new MouseAction() {
            public void mouseActionPerformed(MouseEvent evt) {
                mousePressed(evt);
            }
    };
        
    @SuppressWarnings("serial") MouseAction mouseDraggedAction =
        new MouseAction() {
            public void mouseActionPerformed(MouseEvent evt) {
                mouseDragged(evt);
            }
    };
    
    @SuppressWarnings("serial") MouseAction mouseReleasedAction = 
    	new MouseAction(){
    		public void mouseActionPerformed(MouseEvent evt) {
    			mouseReleased(evt);
    		}
    };
    
	Mover(int w, int h, BufferedPanel b, int mode){
		//initialize variables
		width = w;
		height = h;
		this.b = b;
		
		//add balls to canvas
		Random r = new Random();
		int nx, ny;
		double vx, vy;
		
		//choose mode (exists for demo purposes)
		switch(mode){
		case 0:
			this.addBall(new Ball(180 ,20, .45, 0, 50, 10, true, Color.blue, this));
			eMouse = false;
			break;
		case 1:
			for(int i=0; i<40; i++){
				nx = r.nextInt(width);
				ny = r.nextInt(height);
				vx = r.nextDouble()*2 - 1;
				vy = r.nextDouble()*2 - 1;
				this.addBall(new Ball(nx, ny, vx, vy, 50, 10, true, Color.blue, this));
			}
			break;
		case 2:
			for(int i=0; i<100; i++){
				nx = r.nextInt(width);
				ny = r.nextInt(height);
				vx = r.nextDouble()*2 - 1;
				vy = r.nextDouble()*2 - 1;
				this.addBall(new Ball(nx, ny, vx, vy, 50, 10, false, Color.blue, this));
			}
			break;
		case 3:
			for(int i=0; i<100; i++){
				nx = r.nextInt(width);
				ny = r.nextInt(height);
				vx = r.nextDouble()*2 - 1;
				vy = r.nextDouble()*2 - 1;
				this.addBall(new Ball(nx, ny, vx, vy, 50, 10, false, Color.blue, this));
			}
			grav = 0.0;
			break;
		}
		
		//setup the window for controls
		control = new Controls(this);
	}
	
	//paint all balls on the canvas
	public void paint(Graphics g) {
		for (int i=0; i<numBalls; i++) 
			((Ball)ball.get(i)).draw(g);
	}

	//call this method to add a ball to the canvas
	public void addBall(Ball b) {
		if (numBalls < max) ball.add(numBalls++, b);
	}
	
	public void removeBall(int i){
		curBall = i;
		remBall = true;
	}
	
	private void readyToRemoveBall(){
		ball.remove(curBall);
		numBalls--;
		remBall = false;
	}
	
	public void run() {
		//store the current thread, so that it can be stopped safely
		Thread currentThread = Thread.currentThread();
		
		//add listeners for mouse actions
		MouseActionAdapter adapter = b.getMouseActionAdapter();
		adapter.addMousePressedAction(mousePressedAction);
        adapter.addMouseDraggedAction(mouseDraggedAction);
        adapter.addMouseReleasedAction(mouseReleasedAction);
		
		while (me == currentThread) {
			//bound each ball to the limits of this canvas
			for (int i=0; i<numBalls; i++)
				((Ball)ball.get(i)).update();
			
			//make all balls interact with each other
			for (int i=0; i<numBalls-1; i++)
				for (int j=i+1; j<numBalls; j++)
					((Ball)ball.get(i)).interact(((Ball)ball.get(j)));
			
			//if mouse is dragging, adjust the current ball's
			//   velocity in direction of mouse
			if (dragging) {
				((Ball)ball.get(curBall)).vx = (mx - ((Ball)ball.get(curBall)).x) / 5;
				((Ball)ball.get(curBall)).vy = (my - ((Ball)ball.get(curBall)).y) / 5;
			}

			//repaint the canvas and pause before looping again
			b.clearPanel();
			paint(b.getBufferGraphics());
			b.repaint();
			JPTUtilities.pauseThread(10);  // pause
			while(pauseAnim);
			if(remBall) readyToRemoveBall();
		}
	}
	
	//start new thread and store it in variable 'me' 
	public void start(){
		this.me = new Thread(this);
		this.me.start();
	}
	
	//set thread 'me' to null so this thread will halt safely
	public void stop(){
		this.me = null;
	}
	
	//  gives the index of the ball closest to mouse position (x, y)
	int nearestBall(int x, int y, int ex) {
		double d=999999999999.0, t; 
		int j=0;
		for (int i=0; i<numBalls; i++) {
			t = Ball.hypot(x - ((Ball)ball.get(i)).x, y - ((Ball)ball.get(i)).y);
			if (t < d) {
				d = t;
				j = i;
			}
		}
		return j;
	}

	//called when canvas needs to be resized to given w and h
	public void resized(Dimension d){
		// set size
		this.width = d.width;
		this.height = d.height;
	}
	
	// perform mouse actions
	public void mouseReleased(MouseEvent e) {
		this.dragging = false;
	}

	public void mousePressed(MouseEvent e){
        mx = e.getX();
		my = e.getY();
		
		curBall = nearestBall(mx, my, numBalls);
		
		if(e.getButton() == MouseEvent.BUTTON3)
			remBall = true;
		else
			if(eMouse) dragging = true;
	}

	public void mouseDragged(MouseEvent e){
		mx = e.getX();
		my = e.getY();
	}
}
