/* Breakout game.  Copyleft Jonathan Riddell(jr@jriddell.org) 2000
**   version 1.0
**   There a a million bugs in this
**   There are a million other things that could be improved
**   But I like it
*/

import java.awt.*;
import java.applet.Applet;
import java.awt.event.*;

public class Breakout extends Applet implements MouseMotionListener, MouseListener{

    //size of applet
    private final int width = 301;
    private final int height = 400;

    private Bat bat;
    private Ball ball;

    private Brick[][] bricks;
    private boolean[][] brickAlive;
    final int horizontalBricks = 10;
    final int verticalBricks = 3;

    private Color background = Color.lightGray;

    public void init() {
	addMouseMotionListener(this);
	addMouseListener(this);
       	setBackground(background);
	//needs to create bat, ball and bricks

	Graphics g = getGraphics();
	bat = new Bat(g, width, background);
	bat.start();

	ball = new Ball(g, width, height, background, horizontalBricks, verticalBricks, 30, 12);
	ball.start();

	//make the bricks
	int brickX = 1;
	int brickY = 10;

	bricks = new Brick[horizontalBricks][verticalBricks];

	//initialise all bricks with correct x and y value
	for (int i = 0; i < verticalBricks; i++) {
	    for (int j = 0; j < horizontalBricks; j++) {
     	       bricks[j][i] = new Brick(g, brickX, brickY, background);
	       brickX = brickX + 30;
	    }
            brickX = 1;
	    brickY = brickY + 12;
	}//end of bricks fors
	
    }//end of init

    public void paint(Graphics g) {

	//draw border
	g.setColor(Color.red);
	//border (or not)
	//g.drawRect(0, 0, width-1, height);

	//needs to call drawing method of bricks
	for (int i = 0; i < verticalBricks; i++) {
	    for (int j = 0; j < horizontalBricks; j++) {
		if (ball.brickAlive[j][i]) {
		    bricks[j][i].display();
		}
	    }
	}//end of fors
    }//end of paint



    //mouseMoved needs to tell Bat and Ball it's x value
    public void mouseMoved(MouseEvent event) { 
	int mouseX = event.getX();
	bat.changeX(mouseX);
	ball.mouseMoved(mouseX);
    }

    public void mouseDragged(MouseEvent event) { }

    public void mouseClicked(MouseEvent event) {
	ball.aliveState(true);
    }

    public void mouseReleased(MouseEvent event) { }
    public void mousePressed(MouseEvent event) { }
    public void mouseEntered(MouseEvent event) { }
    public void mouseExited(MouseEvent event) { }

    public void foo() {
	System.out.println("it worked!");
    }

} //end of Game


class Ball extends Thread {
    private Graphics g;

    private int ballX=100;
    private int ballY=350;
    private int oldX=ballX;
    private int oldY=ballY;

    private int diameter = 10;

    private boolean alive = false;
    private boolean won = false;

    private int deadX;
    private int deadY;

    private int borderLeft = 0, borderRight;
    private int borderTop = 0, borderBottom;

    //this is so it knows where the bat is
    private int mouseX;

    //pixels the ball moves between frames
    private int xChange=-5, yChange=-5;

    private Color background;

    //array describing each brick's state
    public boolean[][] brickAlive;
    public int[] brickX;
    public int[] brickY;

    //number of bricks each way
    private int horizontalBricks, verticalBricks;

    //construct
    public Ball (Graphics graphics, int width, int height, Color bg, int hb, int vb, int brickDeltaX, int brickDeltaY) {
	g=graphics;
	borderRight = width-1;
	borderBottom = height-1;
	background = bg;
	horizontalBricks = hb;
	verticalBricks = vb;

	brickAlive = new boolean[horizontalBricks][verticalBricks];
     	for (int i = 0; i < verticalBricks; i++) {
	    for (int j = 0; j < horizontalBricks; j++) {
		brickAlive[j][i] = true;
	    }
        }//end of brickAlive fors
	brickX = new int[horizontalBricks + 1];
	brickY = new int[verticalBricks + 1];
	int x =1;
	int y = 10;
	brickX[0] = x;
	brickY[0] = y;
	for (int i=1; i < brickX.length; i++) {
	    x = x + brickDeltaX;
	    brickX[i] = x;
	}
	for (int i=1; i < brickY.length; i++) {
	    y = y + brickDeltaY;
	    brickY[i] = y;
	}
    }

    public void mouseMoved(int x) {
	if (!alive) {
	   ballX = x +25;
	}
	mouseX = x;
    }

    public void aliveState(boolean state) {
	alive = state;
    }

    public void run() {
	while(true){

	    if (alive) { 
		alive();
	    }
	    else {
		 dead();
	    }
	    try { Thread.sleep(50); }
	    catch (InterruptedException e) {
               System.err.println("Sleep exception in ball");
            }
	}
    }

    //places ball on top of bat
    private void dead () {
	deadY = 350-10;
        if (ballX > 300-50) { deadX = (300-25);}
        else {deadX = ballX;}
       
	g.setColor(background);
	g.fillOval(oldX, oldY, diameter, diameter);
	oldX = deadX;
	oldY = deadY;
	g.setColor(Color.blue);
	g.fillOval(deadX, deadY, diameter, diameter);
    }

    //moves ball around the place
    private void alive() {
	bounce();
	g.setColor(background);
	g.fillOval(oldX, oldY, diameter, diameter);
	g.setColor(Color.blue);
	ballX = ballX + xChange;
	ballY = ballY + yChange;
	g.fillOval(ballX, ballY, diameter, diameter);
	oldX = ballX;
	oldY = ballY;
	if (won) {
	    g.drawString("Well done!", 10, 370);
	    g.drawString("25 years ago your Dad though this was radge", 10,390);}
    }

    //checks to see if ball bounced
    private void bounce() {
	//bounce off sides
	if (ballX + xChange <= borderLeft)
	    xChange = -xChange;
	if (ballX + xChange >= borderRight)
	    xChange = -xChange;
	if (ballY + yChange <= borderTop) {
	    yChange = -yChange;
	    won = true;
	}
	//bounce off bottom
	if (ballY + yChange >= borderBottom) {
       	    yChange = -yChange;
	    aliveState(false);
	    //these refresh ballX and ballY, there should be
	    //a better way to do this
	    ballX = mouseX +25;
	    ballY = 350+10; 
	}
	//bounce off bat
	if ((ballY + yChange) >= 350 && ballX >= mouseX && ballX <= (mouseX + 60))
	  yChange = -5;

	//bounce off brick
     	for (int i = 0; i < verticalBricks; i++) {
	    for (int j = 0; j < horizontalBricks; j++) {
		if (((ballY + yChange) >= brickY[i])  
		      && ((ballY + yChange) <=brickY[i+1])
		      && ((ballX + xChange) >= brickX[j])
		      && ((ballX + xChange) <= brickX[j+1])
		      && (brickAlive[j][i]) ) {
                    //System.out.println("killed brick"+j+","+i);
		    yChange = -yChange;
		    brickAlive[j][i] = false;
		    //this looks crappy (need width and height vars)
		    g.setColor(background);
		    g.fillRect(brickX[j], brickY[i], 28, 10);
		}
	    }
        }//end of brickAlive fors
    }
}

class Bat extends Thread {
    private Graphics g;

    //position of bat
    private int batX = 100;
    private int batY = 350;

    //size of bat
    private final int width = 60;
    private final int height = 10;

    //stop it going off the edge
    private final int borderLeft;
    private final int borderRight;

    private Color background;

    //construction
    public Bat (Graphics graphics, int appletWidth, Color bg) {
	g = graphics;
	borderLeft = 0;
	borderRight = appletWidth;
	background = bg;
    }

    public void run() {

	int oldX = batX;

	while(true) {
	    g.setColor(background);  //draw over old bat in background
	    g.fillRect(oldX, batY, width, height);

	    
	    if (batX <= borderLeft) {
		batX = 1;
	    }
	    if (batX >= (borderRight - width -1)) {
		batX = (borderRight - width -1);
	    }
	    g.setColor(Color.black);  //stop bat going off edge
      g.fillRect(batX, batY, width, height);  //draw bat

	    // potential slight bug here: batX could change between drawing 
	    // bat and the next line, but this would be too complicated to fix
	    oldX = batX;

	    try { Thread.sleep(50); }  //and snooze
	    catch (InterruptedException e) {
               System.err.println("Sleep exception in bat");
	    }
	}//end of while loop
    }//end of Bat.run

    public void changeX(int x) {
	batX = x;
	//System.out.println("batX ="+batX);
    }

} //end of Bat

class Brick {

    private Graphics g;

    private int width = 28;
    private int height = 10;

    private int brickX;
    private int brickY;

    private Color background;

    //construction
    public Brick(Graphics graphics, int x, int y, Color bg) {
	g = graphics;
        brickX = x;
	brickY = y;
	background = bg;
    }

    public void display() {
   	   g.setColor(Color.green);
	   g.fillRect(brickX, brickY, width, height);
    }

}//end of Brick
