
import java.awt.*;
import java.applet.Applet;
import java.lang.Math;

public class GameFrame extends Frame{
	
	Panel panel, menu_panel;
	Button new_game_button, exit_button;
	GameManager game_manager;
	
	public GameFrame (Applet applet,  int width, int height) {
		super("Breakout");
		setSize(width +13, height+100);
		setResizable(false);
		setCursor(Frame.CROSSHAIR_CURSOR);
	
		panel = new Panel();
		menu_panel = new Panel();
		game_manager = (GameManager)applet;
	
		panel.setLayout(new FlowLayout(FlowLayout.CENTER));
		panel.add(applet);
		setLayout(new BorderLayout());
	
		add("Center",panel);
		CreateMenuPanel(menu_panel);
		add("South",menu_panel);
	}
	
	public void CreateMenuPanel (Panel panel)	{
		panel.setLayout(new FlowLayout(FlowLayout.CENTER));
		new_game_button = new Button("New Game");
		exit_button = new Button("Exit");
		panel.add(new_game_button);
		panel.add(exit_button);
		
		Label directions = new Label("Click mouse to start
ball.");
		panel.add(directions);
	}
	
	public boolean action (Event e, Object o)	{
		if (e.target instanceof Button)	{
			if (e.target == new_game_button)
				game_manager.newGame();
			if (e.target == exit_button)	
			{	
				try {
					setVisible(false);
					game_manager.stop();
					game_manager.destroy();
					dispose();
				}
				catch(Exception exception){;}
			}
		}
		return (true);
	}
}

public class GameManager extends Applet implements Runnable {
	
	Thread animation;
	
	Graphics offscreen;
	Image image;
	
	static final int REFRESH_RATE = 5;
	
	BallManager ball_manager;
	RacquetManager racquet_manager;
	BrickManager brick_manager;
	GameFrame game_frame;
	Frame frame;	
	
	int applet_width, applet_height;
	int number_of_lives, number_of_lives_to_start,
number_of_bricks_left;

	
	public void init()	{
			
		setBackground(Color.black);
		applet_width = bounds().width;
		applet_height = bounds().height;
		
		racquet_manager = new RacquetManager(20,5,applet_width,
applet_height, Color.red);
		ball_manager = new BallManager(5,5,2,2,applet_width,
applet_height, 5, Color.blue);
		brick_manager = new BrickManager(5,4,20,10,applet_width,
Color.yellow);
	
		image = createImage(applet_width, applet_height);
		offscreen = image.getGraphics();
		
		game_frame = new GameFrame (this, applet_width,
applet_height);
		game_frame.setVisible(true);
		
		number_of_lives_to_start= 5;
		number_of_lives = number_of_lives_to_start;
		number_of_bricks_left = brick_manager.number_of_bricks;
		
	}
	
	public boolean mouseMove (Event e , int x, int y)	{
		racquet_manager.moveRacquet(x);
		return true;
	}
	
	public boolean mouseDrag(Event e , int x, int y)	{
		racquet_manager.moveRacquet(x);
		return true;
	}
	
	public boolean mouseDown(Event e, int x, int y)	{
		ball_manager.unpauseBall();
		return true;	
	}
	
	public void start()	{
		animation = new Thread(this);
		
		if (animation != null){		
			animation.start();
		}
	}
	
	public void updateManagers()	{
		int brick_height, brick_width;
		int count;
		brick_height = brick_manager.brick_height;
		brick_width = brick_manager.brick_width;
		


ball_manager.check_racquet_intersect(racquet_manager.getXPosition(),
racquet_manager.getYPosition(), 
				racquet_manager.getXPosition() +
racquet_manager.racquet_width, 
				racquet_manager.getYPosition() +
racquet_manager.racquet_height);

				
		for (count = 0; count <
brick_manager.number_of_bricks; count++)	{
			if (!brick_manager.check_if_broken(count)) {
				if
(ball_manager.check_brick_intersect(brick_manager.getXPosition(count),
brick_manager.getYPosition(count), 
				brick_manager.getXPosition(count) +
brick_width, 
				brick_manager.getYPosition(count) +
brick_height))
				{
					brick_manager.remove_brick(count);
					number_of_bricks_left --;
				}
			}
		}			
		ball_manager.updatePosition();
		checkGameStatus();
	}

	public void checkGameStatus()	{
		if (ball_manager.checkNumberOfBallsLeft() == 0 ||
number_of_bricks_left ==0)
			gameOver();
	}

	public void gameOver()	{
		ball_manager.freezeBall();
	}
	
	public void newGame()	{
		int count;
		for (count = 0; count <
brick_manager.number_of_bricks; count++)	{
			brick_manager.unbreak_brick(count);
		}
		
		number_of_lives = number_of_lives_to_start;
		number_of_bricks_left = brick_manager.number_of_bricks;
			
		ball_manager.resetBall();
		ball_manager.resetNumberOfBallsLeft();
		ball_manager.resetVelocity();
			
		start();
	}
	
	public void update (Graphics g)	{
		paint (g);
	}
	
	public void paint (Graphics g)	{
		offscreen.setColor(Color.black);
		offscreen.fillRect(0,0,applet_width, applet_height);
		racquet_manager.paint(offscreen);
		ball_manager.paint(offscreen);
		brick_manager.paint(offscreen);
	
		g.drawImage(image,0,0,this);
	}
	
	public void run()	{
		
		while (true)	{
			repaint();
			updateManagers();
			Thread.currentThread().yield();
			try {
				Thread.sleep (REFRESH_RATE);
			} catch (Exception exc) {;}
			
		}
	}
	
	public void stop()	{
		showStatus("Game over");
		if (animation != null)	{
			animation.stop();
			animation = null;
		}
	}
}

public class BallManager{
	private BallSprite ball;
	private int ball_width;
	private int ball_height;
	
	static int applet_width, applet_height;
	private int ball_min_x, ball_max_x;
	private int ball_min_y, ball_max_y;
	private int ball_velocity_x, ball_velocity_y;
	private boolean paused_ball;
	private int number_of_balls, number_of_balls_left;
	
	public BallManager (int width, int height, int ball_velocity_x,
int ball_velocity_y, int applet_width, int applet_height, int
number_of_balls, Color c) {
		this.applet_width = applet_width;
		this.applet_height = applet_height;
		ball = new BallSprite(0,0,width, height, c);
		ball_height = height;
		ball_width = width;
		ball_min_y = 0;
		ball_max_y = applet_height - ball_height;
		ball_min_x = 0;
		ball_max_x = applet_width - ball_width;
		this.ball_velocity_x = ball_velocity_x;
		this.ball_velocity_y = ball_velocity_y;
		
		ball.setPosition(applet_width/2,applet_height/2);
		ball.setVelocity(ball_velocity_x, ball_velocity_y);
		paused_ball = false;
		
		this.number_of_balls =number_of_balls;
		number_of_balls_left = number_of_balls;
	}
	
	public void resetBall()	{
		ball.setPosition(applet_width/2,applet_height/2);
		paused_ball = true;
	}
	
	public void freezeBall()	{
		ball.setVelocity(0,0);
	}
	
	public void unpauseBall()	{
		paused_ball = false;
	}
	
	public void pauseBall()	{
		paused_ball = true;	
	}
	
	public int checkNumberOfBallsLeft ()	{
		
		return(number_of_balls_left);
	}
	
	public void resetVelocity()	{
		ball.setVelocity(ball_velocity_x, ball_velocity_y);
	}
	
	public void resetNumberOfBallsLeft()	{
		number_of_balls_left = number_of_balls;	
	}
	
	public void updatePosition ()	{
		if(!(paused_ball))	{		
			if ((ball.locx + ball.vx < ball_min_x) ||
(ball.locx + ball.vx >= ball_max_x)){
				ball.vx = -(ball.vx);
				ball.locx += ball.vx; ball.locy +=
ball.vy; 
			
			}
			if  ((ball.locy+ ball.vy < ball_min_y) ) {
				ball.vy = -(ball.vy);
				ball.locx += ball.vx; ball.locy +=
ball.vy; 
			}
			if (ball.locy+ ball.vy >= ball_max_y) {
				number_of_balls_left--;
				resetBall();
			}
			ball.locx += ball.vx; ball.locy += ball.vy; 
		}
	}
	
	public boolean check_racquet_intersect(int x1, int y1, int x2, int
y2) {
		if ((x2 >= ball.locx) && (ball.locx + ball.width >= x1)
			&& (y2 >= ball.locy) && (ball.locy + ball.height
>= y1))	
		{
			ball.vy = - ball_velocity_y;
			if (ball.locy > y1+ 2*ball.vy)
				ball.vx = -ball.vx;
			return(true);
		}
		else
			return (false);
	}
		
	public boolean check_brick_intersect(int x1, int y1, int x2, int
y2) {
		if ((x2 >= ball.locx) && (ball.locx + ball.width >= x1)
			&& (y2 >= ball.locy) && (ball.locy + ball.height
>= y1))	
		{
			if (y1 < ball.locy && y2 > (ball.locy +
ball.height))
				ball.vx = -ball.vx;
			else
				ball.vy = -ball.vy;
			
			
			return(true);
		}
		else
			return (false);
	}

	public void paint (Graphics g)	{
		ball.paint(g);
	}
}

public class BrickManager{
	int brick_spacing, number_of_bricks;
	int brick_width, brick_height;
	int number_of_bricks_per_row;
	
	BrickSprite bricks[];
	
	public BrickManager (int brick_spacing, int number_of_rows, int
brick_width, int brick_height, int applet_width, Color c)	{
		int countOne; int countTwo;
		
		this.brick_spacing = brick_spacing;
		
		this.brick_width = brick_width;
		this.brick_height = brick_height;
		
		// determine how many bricks per row
	
		number_of_bricks_per_row =
Math.round(applet_width/(brick_width + brick_spacing));
		number_of_bricks = number_of_bricks_per_row *
number_of_rows;
		
		bricks= new BrickSprite[number_of_bricks];
		
		for (countOne = 0; countOne < number_of_rows; countOne
++)	{
			for (countTwo = 0; countTwo <
number_of_bricks_per_row; countTwo++)	{
				bricks[countOne*number_of_bricks_per_row +
countTwo] 
				= new BrickSprite(countTwo*(brick_width +
brick_spacing), countOne*(brick_height + brick_spacing) ,brick_width,
brick_height, c);	
			}
		}
	}
	
	public int getXPosition(int brick_number)	{
		return(bricks[brick_number].x);
	}
	
	public int getYPosition(int brick_number)	{
		return(bricks[brick_number].y);	
	}
	
	public boolean check_if_broken(int brick_number)	{
		return(bricks[brick_number].check_if_broken());
	}
	
	public void remove_brick(int brick_number)	{
		bricks[brick_number].break_brick();
	}
	
	public void unbreak_brick(int brick_number)	{
		bricks[brick_number].fix_brick();
		
	}
	
	public void paint (Graphics g)	{
		int count;	
		for (count = 0; count < number_of_bricks; count ++)	{
			(bricks[count]).paint(g);
		}
	}
}

public class RacquetManager {
	
	private RacquetSprite racquet;
 	int racquet_width;
	int racquet_height;
	
	static int applet_width, applet_height;
	private int racquet_min_x, racquet_max_x;
	private int racquet_y;
	
	public RacquetManager (int width, int height, int applet_width,
int applet_height, Color c) {
		this.applet_width = applet_width;
		this.applet_height = applet_height;
		racquet_y = applet_height - height;
		racquet_width = width;
		racquet_height = height;
		
		racquet = new RacquetSprite(0,racquet_y,width, height, c);
		
		
		racquet_min_x = 0;
		racquet_max_x = applet_width - width;
	}
	
	public void moveRacquet (int x)	{
		if ( x <= racquet_min_x)	{
			racquet.setPosition(racquet_min_x,racquet_y);
		}
		else if (x >= racquet_max_x) {
			racquet.setPosition(racquet_max_x,racquet_y);
		}
		else {
			racquet.setPosition(x,racquet_y);
		}
	}
		
	public int getXPosition()	{
		return(racquet.locx);
	}
		
	public int getYPosition()	{
		return(racquet.locy);
	}
	
	
	public void paint (Graphics g)	{
		racquet.paint(g);
	}
}

public class BallSprite extends RectangleSprite{
	int vx, vy;
	
	public BallSprite (int x, int y, int width, int height, Color c)	{
		super (x,y,width,height,c);
	}
	public void setPosition (int x, int y) {
		locx = x; locy = y;	
	}
	
	public void setVelocity (int vx, int vy) {
		this.vx = vx; this.vy = vy;
	}
	
	public void updatePosition() {
		
	}
}

public class BrickSprite extends RectangleSprite {
	int x; int y;
	boolean broken;
	public BrickSprite(int x, int y, int width, int height, Color c)	{
		super(x,y,width,height, c);
		this.x=x;
		this.y=y;
		broken = false;	
	}
	
	public void break_brick()	{
		broken = true;
	}
	
	public void fix_brick()	{
		broken = false;
	}
	
	public boolean check_if_broken()	{
		return(broken);
	}
	
	public void paint (Graphics g)	{
		if (!broken)	{
			g.setColor(myColor);
			g.fillRect(locx, locy, width, height);
		}
	}
}

public class RacquetSprite extends RectangleSprite implements Moveable {
	int vx, vy;
	
	
	public RacquetSprite (int x, int y, int width, int height, Color
c)	{
		super (x, y, width, height, c);
	}
	
	public void setPosition (int x, int y) {
		locx = x; locy = y;	
	}
	
	public void setVelocity (int vx, int vy) {
		this.vx = vx; 
		this.vy = vy;
	}
	
	public void updatePosition() {
		
	}
}

public class RectangleSprite
{
	public int locx, locy;
	public int width, height;
	Color myColor;
		
	public RectangleSprite (int x, int y, int width, int height, Color
c) {
		locx = x;
		locy = y;
		this.width = width;
		this.height = height;
		myColor = c;
	}
	
	public void update() {
		
	}
	
	public void paint (Graphics g)	{
		g.setColor(myColor);
		g.fillRect(locx, locy, width, height);
	}
}

interface Moveable {
	public abstract void setPosition (int x, int y);
	public abstract void setVelocity (int vx, int vy);
	public abstract void updatePosition();
}


