// package GravitySimulation;
import java.awt.*;
import java.awt.geom.*;
import edu.neu.ccs.gui.*;

public class Ball{
	double x, y, vx, vy;   //(x, y) position of this ball, and velocity components
	double radius, mass;   //radius and mass of this ball
	Color myColor;         //the color of this ball
	boolean hit;           //has this ball been involved in a collision in the current frame 
	boolean fill;		   //draw the ball filled if true, otherwise draw outline
	Mover m;               //the mover responsible for moving this ball
	
	public Ball(double x, double y, double vx, double vy, double mass, double radius, boolean fill, Color col, Mover m){
		//initialize variables
		this.x = x;
		this.y = y;
		this.vx = vx;
		this.vy = vy;
		this.mass = mass;
		this.radius = radius;
		this.hit = false;
		this.m = m;
		this.fill = fill;
		this.myColor = col;
	}
	
	public void draw(Graphics g){
		if(fill){ //-draw filled circle
			Ellipse2D.Double circle = new Ellipse2D.Double((int)(x-radius), (int)(y-radius), (int)(2*radius), (int)(2*radius));
			ShapePaintable c = new ShapePaintable(circle, PaintMode.FILL, myColor);
			c.paint(g);
		}else{    //-draw outlined circle
			g.setColor(myColor);
			g.drawOval((int)(x-radius),(int)(y-radius),(int)(2*radius),(int)(2*radius));
		}
	}

	public void interact(Ball b) {
		double dx = b.x - x;                 //delta x
		double dy = b.y - y;                 //delta y
		double h = Math.sqrt(dx*dx + dy*dy); //distance between center of balls

		// if the balls are colliding, compute their new velocities
		if (h < radius+b.radius){ 
			hit = b.hit = true;   //these balls have collided... do not exert gravity on them now
			
		    //  Compute the (elastic) collision of this ball and given ball
			//  m1 * v1 + m2 * v2 = k*((m1 + m2) * v')
			double v1, v2, l1, l2, vPrime, v, tm;
		    dx /= h;
		    dy /= h;						// normalize
		    v1 = vx*dx + vy*dy;
		    v2 = b.vx*dx + b.vy*dy;			// collision velocity
		    l1 = vx*dy - vy*dx;
		    l2 = b.vx*dy - b.vy*dx;
		    tm = mass + b.mass;				// total mass
		    if ((tm==0) || (v1 < v2)) return;
		    
		    vPrime = (v1*mass + v2*b.mass)/tm;
		    
		    //new velocity for this ball
		    v = vPrime + 0.85 * (v2 - v1) * b.mass / tm;
		    vx = v*dx + l1*dy;
		    vy = v*dy - l1*dx;
		    
		    //new velocity for ball b
		    v = vPrime + 0.85 * (v1 - v2) * mass / tm;
		    b.vx = v*dx + l2*dy;
		    b.vy = v*dy - l2*dx;
		}
	}
	
	public void update(){
		// add velocities and keep ball within boundaries
		x += vx;
		fenceX();
		y += vy;
		fenceY();
		
		if(!hit) vy += m.grav;   // exert gravity if this ball wasnt involved in a collision
		hit = false;             // reset the 'hit' flag
	}
	
	public void fenceX(){
		if (x+radius > m.width){
			if (vx > 0) vx *= 0.85;    // restitution
			vx = -Math.abs(vx);        // reverse velocity
			hit = true;
			//  Check if location is completely off screen
			if (x-radius > m.width) x = m.width + radius;
		}
		
		if (x-radius < 0){
			if (vx < 0) vx *= 0.85;    //restitution
			vx = Math.abs(vx);         // reverse velocity
			hit = true;
			//  Check if location is completely off screen
			if (x+radius < 0) x = -radius;
		}
	}
	
	public void fenceY(){
		if (y+radius > m.height){
			if (vy > 0) vy *= 0.85;     //restitution
			vy = -Math.abs(vy);         //reverse velocity
			hit = true;
			//  Check if location is completely off screen
			if (y-radius > m.height) y = m.height + radius;
		}
	
		if (y-radius < 0){
			if (vy < 0) vy *= 0.85;     //restitution
			vy = Math.abs(vy);          //reverse velocity
			hit = true;
			//  Check if location is completely off screen
			if (y+radius < 0) y = -radius;
		}
	}
	
	//return the length of the hypotenuse
	public static double hypot(double x, double y){
		return Math.sqrt(x*x + y*y);
	}
}
