/*
This work is licensed under the Creative Commons Attribution 2.5 License. 
To view a copy of this license, 
visit http://creativecommons.org/licenses/by/2.5/ 
or send a letter to Creative Commons, 543 Howard Street, 5th Floor, 
San Francisco, California, 94105, USA.
*/

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;

class BCanvas extends Canvas implements Runnable, MouseListener, MouseMotionListener{
	   
	   
	   
	static final int MAX_BALLS = 3;
	static final int MAX_TRAIL_LENGTH = 4;
	static final int POINTS_TO_MULTIBALL = 5000;
	static final int POINTS_TO_EXTRA_BALL = 100000;
	static final int TAIL_SPEED = 2;
	
	int i, j 	= 0;
	int bn 		= 0;
	int bx,by 	= 0;
	int score 	= 0; 
	int	cubes 	= 3;
	int scoreUp = 0;
	int level 	= 1;
	int displayLevel = 0;

	private Thread engine;
	public Font bigFont = new Font("Dialog", Font.PLAIN, 50);
		
	Rectangle paddle 	= new Rectangle(0, 0, 50, 15);

	Rectangle ball[][] 	= new Rectangle[MAX_BALLS][MAX_TRAIL_LENGTH];
	boolean hitTop[] 	= new boolean[MAX_BALLS];
	boolean ballInPlay[] = new boolean[MAX_BALLS];
	int ballSpeed[] 	= new int[MAX_BALLS];
	int ballMovementY[] = new int[MAX_BALLS];
	int ballMovementX[] = new int[MAX_BALLS];
	int ballTailSpeed 	= 0;
	int multiBallCount 	= 0;
	int extraBallCount 	= 0;
	int liveBalls 		= 1;
	
	Rectangle brick[][] = new Rectangle[16][7];
	boolean showBrick[][] = new boolean[16][7];
	boolean brickFound 	= false;
	int brickColor[][] 	= new int[21][7];
		
	BufferedImage bi;
	Graphics2D big;

  // Holds the coordinates of the user's last mousePressed event.
  	int last_x, last_y;
	boolean firstTime = true;
  	Rectangle area;
  
  // True if the user pressed, dragged or released the mouse outside of the rectangle; false otherwise.
	boolean pressOut = false;	
	boolean mousePressed = false;

	public BCanvas(){
    engine = new Thread(this);
    engine.setPriority(Thread.MIN_PRIORITY);
    engine.start();
		
  	setBackground(Color.black);
    addMouseMotionListener(this);
    addMouseListener(this);
	}
	
	public void run() {
		Thread me = Thread.currentThread();
    	while (engine == me) {
    		repaint();
      		try {
        		Thread.sleep(15);
      		} catch (InterruptedException e) { break; }
    	}
    	engine = null;
  	}

	public void mousePressed(MouseEvent e){				
		last_x = paddle.x - e.getX();
		if(paddle.contains(e.getX(), e.getY())){
			setCursor(new Cursor(Cursor.HAND_CURSOR));
			mousePressed = true;
			updateLocation(e);
		} else {
			pressOut = true;
			if (cubes == -1) {
				cubes = 3;
				score = 0;
				firstTime = true;
				CubeOut.label.setText("Score: " + score + "    Cubes : " + cubes + "     Round : " + level);
			}
		}
	}

	public void mouseDragged(MouseEvent e){
		if(!pressOut){
			 updateLocation(e);
		}
	}

	// Handles the event of a user releasing the mouse button.
	public void mouseReleased(MouseEvent e){
		mousePressed = false;
		setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
		if(paddle.contains(e.getX(), e.getY())){			
			updateLocation(e);
		} else {
			pressOut = false;
		}
	}

	// This method required by MouseListener.
	public void mouseMoved(MouseEvent e){}

	// These methods are required by MouseMotionListener.
	public void mouseClicked(MouseEvent e){}
	public void mouseExited(MouseEvent e){}
	public void mouseEntered(MouseEvent e){}
	public void updateLocation(MouseEvent e){
		paddle.setLocation(last_x + e.getX(), 230);
		checkPaddle();
	}

	public void paint(Graphics g){
		update(g);
	}

	public void update(Graphics g){
		Graphics2D g2 = (Graphics2D)g; 
		if (cubes != -1) {
			if(firstTime){
	  		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
	  		g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
	  		g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);

    		int w = getWidth();
	  		int h = getHeight();
      		area = new Rectangle(w, h);

		  	bi = (BufferedImage)createImage(w, h);
		  	big = bi.createGraphics();
		  	paddle.setLocation(125, 230);
		  	
		  	for(i=0;i<MAX_BALLS;i++) {
				for(j=0;j<MAX_TRAIL_LENGTH;j++) {
		  			ball[i][j] = new Rectangle(150, 150, 10, 10);
		  		}
		  		ballMovementX[i] = 3;
		  		ballMovementY[i] = 3;
		   		hitTop[i] 		= false;
		  		ballInPlay[i] 	= false;
			}
			ballInPlay[0] = true;
		  		 	
		  	for(i=1;i<16;i++){
			  	for(j=1;j<7;j++){
		  			showBrick[i][j] = true;			  	
			  		bx = (i - 1) * 20;
			  		by = (j - 1) * 15 + 20;
		   			brick[i][j] = new Rectangle(bx, by, 18, 13);
		  		}
		  	}
		  		  
		  	big.setStroke(new BasicStroke(2.0f));
		  	firstTime = false;
			} 
	
			// Clears the rectangle that was previously drawn.
			big.setColor(Color.black);
			big.clearRect(0, 0, area.width, area.height);
	
			big.setPaint(Color.green);
			big.setStroke(new BasicStroke(2.0f));
			big.draw(paddle);
			
			if (displayLevel > 0) {
				big.setColor(new Color(100,255,100));
		  	big.setFont(bigFont);
				big.drawString("ROUND  " + level,35,160);
				displayLevel--;
			}		
			
			ballTailSpeed++;
			if (ballTailSpeed == TAIL_SPEED) {
				ballTailSpeed = 0;
				
				for(i=0;i<MAX_BALLS;i++) {
					for(j=3;j>0;j--) {
						ball[i][j].setBounds(ball[i][j-1]);
					}
				}
			}
								
			for(i=0;i<MAX_BALLS;i++){	
				if(ballInPlay[i] == true){	
					for(j=3;j>-1;j--){
						big.setPaint(new Color(50,(255-(j*51)),50));
						big.draw(ball[i][j]);					
					}
				}
			}
						
			brickFound = false;
			
			big.setStroke(new BasicStroke(1.0f));
			for(i=1;i<16;i++){
				for(j=1;j<7;j++){
					if(showBrick[i][j] == true){
						brickFound = true;
						if(j==1 || j==2){
							big.setPaint(Color.red);
						} else {
							if(j==3 || j==4){
								big.setPaint(Color.yellow);				
							} else {
								big.setPaint(Color.green);
							}							
						}
						big.draw(brick[i][j]);
					}
				}
			}
						
			if (brickFound == false) {
				for(i=1;i<16;i++){
					for(j=1;j<7;j++){
						showBrick[i][j] = true;						
					}
				}
				displayLevel = 25;
				level++;
			}
								
			if(mousePressed == true){
				moveBall();
			}
			
			// Draws the buffered image to the screen.
			g2.drawImage(bi, 0, 0, this);
			
		}	else {

			big.setColor(Color.black);
			big.clearRect(0, 0, area.width, area.height);
			
			big.setColor(Color.green);
		  	big.setFont(bigFont);
			big.drawString("Game Over",20,130);
			g2.drawImage(bi, 0, 0, this);
			
		}			
	}
	
	boolean checkPaddle(){
    	if (area == null) {
    		  return false;
   		}
		if(area.contains(paddle.x, paddle.y, 100, 50)){
			return true;
		}
		int new_x = paddle.x;
		int new_y = paddle.y;

		if((paddle.x+51)>area.width){
			new_x = area.width-51;
		}
		if(paddle.x < 1){  
			new_x = 1;
		}
		paddle.setLocation(new_x, new_y);
		return false;
	}

	void moveBall(){
		for(bn=0;bn<MAX_BALLS;bn++){
			if(ballInPlay[bn] == true){	

				if(ball[bn][0].x < 1) {
					ball[bn][0].x = 1;
					ballMovementX[bn] = ballMovementX[bn] * -1;
				}
				
				if(ball[bn][0].x > 290) {
					ball[bn][0].x = 290;
					ballMovementX[bn] = ballMovementX[bn] * -1;
				}
						
				if(ball[bn][0].y > 250){
					liveBalls = liveBalls - 1;
					ballInPlay[bn] = false;
					ball[bn][0].y = 130;
					hitTop[bn] = false;
					ballMovementY[bn] = 3;
					ballSpeed[bn] = 0;
					if(ballMovementX[bn] > 0){
						ballMovementX[bn] = 3;
					} else {
						ballMovementX[bn] = -3;
					}
					if (liveBalls == 0) {
						ballInPlay[0] = true;
						cubes--;
						liveBalls = 1;
						if (cubes < 0) {
							CubeOut.label.setText("Score: " + score + "    Cubes : 0     Round : " + level);
						} else {
							CubeOut.label.setText("Score: " + score + "    Cubes : " + cubes + "     Round : " + level);
						}
					}
				}
				
				if(ball[bn][0].y < 1){
					if(hitTop[bn] == false){
						hitTop[bn] = true;
						ballSpeed[bn] += 2;
					}
					ballMovementY[bn] = ballMovementY[bn] * -1 + ballSpeed[bn];
				}	
				
				if(ball[bn][0].y > 215) { 
						if((ball[bn][0].x + 10 >= paddle.x + 15 && ball[bn][0].x + 10 <= paddle.x + 35) ||
						(ball[bn][0].x >= paddle.x + 15 && ball[bn][0].x <= paddle.x + 35)) {
						ballMovementY[bn] = -3 - ballSpeed[bn];
					}	else {
						if((ball[bn][0].x + 10 >= paddle.x && ball[bn][0].x + 10 <= paddle.x + 4) ||
							(ball[bn][0].x >= paddle.x && ball[bn][0].x <= paddle.x + 4)) {					
							ballMovementY[bn] = -3 - ballSpeed[bn];
							ballMovementX[bn] = -3 - ballSpeed[bn];
						}	else {
							if((ball[bn][0].x + 10 >= paddle.x + 5 && ball[bn][0].x + 10 <= paddle.x + 14) ||
								(ball[bn][0].x >= paddle.x + 5 && ball[bn][0].x <= paddle.x + 14)) {					
								ballMovementY[bn] = -3 - ballSpeed[bn];
								ballMovementX[bn] = -2 - ballSpeed[bn];
							}	else {
								if((ball[bn][0].x + 10 >= paddle.x + 36 && ball[bn][0].x + 10 <= paddle.x + 44) ||
									(ball[bn][0].x >= paddle.x + 36 && ball[bn][0].x <= paddle.x + 44)) {					
									ballMovementY[bn] = -3 - ballSpeed[bn];	
									ballMovementX[bn] = 2 + ballSpeed[bn];
								}	else {
									if((ball[bn][0].x + 10 >= paddle.x + 45 && ball[bn][0].x + 10 <= paddle.x + 50) ||
										(ball[bn][0].x >= paddle.x + 45 && ball[bn][0].x <= paddle.x + 50)) {					
										ballMovementY[bn] = -3 - ballSpeed[bn];
										ballMovementX[bn] = 3 + ballSpeed[bn];
									}
								}
							}
						}
					}								
				}	
				
				boolean brickHit = false;
				if(ball[bn][0].y < 111) {
					for(j=1;j<7;j++){				  
				  		for(i=1;i<16;i++){
				  			if(showBrick[i][j] == true){	
								bx = (i - 1) * 20;
					  			by = ((j - 1) * 15) + 18;	
								if(((ball[bn][0].x + 10 >= bx && ball[bn][0].x + 10 <= bx + 18) ||
									(ball[bn][0].x >= bx && ball[bn][0].x <= bx + 18)) &&
									((ball[bn][0].y + 10 >= by && ball[bn][0].y + 10 <= by + 13) ||
									(ball[bn][0].y >= by && ball[bn][0].y <= by + 13)) &&
										brickHit == false) {
									brickHit = true;

									if((ball[bn][0].x + 10 <= bx) ||	
									 	(ball[bn][0].x >= bx + 1)) {
											ballMovementX[bn] = ballMovementX[bn] * -1;
									}	else {
										ballMovementY[bn] = ballMovementY[bn] * -1;
									}
									showBrick[i][j] = false;
									scoreUp = (5 * level) * (7 - j) * liveBalls;
									score += scoreUp;
									multiBallCount += scoreUp;
									extraBallCount += scoreUp;									
									CubeOut.label.setText("Score: " + score + "    Cubes : " + cubes + "     Round : " + level);						
									
									if (multiBallCount > POINTS_TO_MULTIBALL) {
										multiBallCount = multiBallCount - POINTS_TO_MULTIBALL;
										if (ballInPlay[0] == false) {
											ballInPlay[0] = true;
											liveBalls++;
										} else if (ballInPlay[1] == false) {
											ballInPlay[1] = true;
											liveBalls++;
										} else if (ballInPlay[2] == false) {
											ballInPlay[2] = true;
											liveBalls++;
										}
									}
									
									if (extraBallCount > POINTS_TO_EXTRA_BALL) {
										extraBallCount = extraBallCount - POINTS_TO_EXTRA_BALL;
										cubes += 1;	
									}									
								}
				  			}
				  		}				  
					}				
				}
				brickHit = false;
				ball[bn][0].x = ball[bn][0].x + ballMovementX[bn];
				ball[bn][0].y = ball[bn][0].y + ballMovementY[bn];
			}
		}
	}
}
	

