/***********************
Program:  Assingment2.java
Description:  Creates a polygon that is
	controlled by the mouse, and shoots
	bullets at squares on the other side
	of the applet
Creator:  Matthew Sharritt
Date:  February 14, 2000
************************/

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

class Gun
{
	public int y, firstP;
	private Color color;
	public int xa[] = new int[4];
	public int ya[] = new int[4];

	Gun (int y, Color color)
	{
		this.y = y;
		firstP = y - 13;
		this.color = color;
		xa[0] = 1;
		xa[1] = 1;
		xa[2] = 15;
		xa[3] = 15;
		ya[0] = firstP - 25;
		ya[1] = firstP + 25;
		ya[2] = firstP + 5;
		ya[3] = firstP - 5;
	}

	public void move (Rectangle bd, double trackY)
	{
		if (y < (bd.height - 25))
		{
			this.y = y;

			firstP = (int)trackY;
			ya[0] = firstP - 25;
			ya[1] = firstP + 25;
			ya[2] = firstP + 5;
			ya[3] = firstP - 5;
		}
	}

	public void draw (Graphics g)
	{
		g.setColor(color);
		g.fillPolygon(xa, ya, 4);
	}
}

class Bullet
{
	public double    x, y, dx;
	public int      size, radius;
	private Color   color;

	Bullet (double x, double y, double dx, int size, Color color)
	{
		this.x = x;
		this.y = y;
		this.dx = dx;
		this.color = color;
		this.size = size;
		radius = size / 2;
	}

	public void move (Rectangle bd)
	{
		// Add velocity to position to get new position
		x += dx;
	}

	public boolean checkHit (Target target)
	{
		boolean hit;
		if ((((x + size) > (target.x - (target.width / 2))) && ((x) < (target.x + (target.width / 2))))
		&& (((y + size) > (target.y - (target.height / 2))) && ((y) < (target.y + (target.height / 2)))))
		{
			hit = true;
		}
		else
		{
			hit = false;
		}

		return hit;
	}

	public void draw (Graphics g)
	{
		g.setColor(Color.black);
		g.fillOval(((int)this.x - radius), ((int)this.y - radius), size, size);
	}
}

class Target
{
	public int   	x, y, dy;
	public int      width, height;
	private Color   color;

	Target ()
	{
		x = 315;
		y = (int)(Math.random()*340 + 5);
		dy = (int)(Math.random()*7 + 2);
		color = Color.red;
		width = (int)(Math.random()*55 + 20);
		height = (int)(Math.random()*35 + 15);
	}

	public void move (Rectangle bd)
	{
		// Check for collision with edge
		if ((y < (bd.y+25)  &&  dy < 0) || ((y + height) > (bd.height)  &&  dy > 0))
			dy = -dy;

		// Add velocity to position to get new position
		y += dy;
	}

	public void draw (Graphics g)
	{
		g.setColor(color);
		g.fillRect((x -(width / 2)), (y -(height / 2)), width, height);
	}

	public void drawExplosion (Graphics g)
	{
		g.setColor(Color.magenta);
		g.fillRect((x -(width / 2)), (y -(height / 2)), width, height);
		width = width - 4;
		height = height - 4;
	}
}

public class Assignment2 extends Applet implements Runnable
{
	private Image               offscreenImage;
	private int                 width, height;
	private Graphics            offscr;
	private Thread              ticker;
	private Font                font;
	private FontMetrics         fontMet;
	private int                 fontHeight;
	private boolean             running = false;
	private boolean             mouse_in = false;
	private boolean 			isHit = false;
	private boolean 			isMiss = false;
	private Gun              	gun;
	private Bullet              bullet;
	private	Dimension           msePad;
	private Target 				target;
	private Rectangle           table;
	private	Point               player;
	private Point				game;
	private Point 				winner;
	private	double              trackX, trackY;
	private int                 pScore = 0, gScore = 0;
	private static final int    WAIT = 1;
	private static final int    SHOOT = 2;
	private static final int    SHOT = 4;
	private static final int    HIT = 8;
	private static final int    MISS = 16;
	private static final int    PSCORE = 32;
	private static final int    GSCORE = 64;
	private static final int    GWON = 128;
	private static final int    PWON = 256;
	private int                 gstate = WAIT;
	private static final int    MAX_SCORE = 10;
	private boolean				isVisible = false;


	public void init()
	{
		width = getSize().width;
		height = getSize().height;

		// Set up table and mouse control area dimensions
		table = new Rectangle(width, height);
		msePad = new Dimension(width, height);
		player = new Point(width - width / 4, 5);
		game = new Point(width / 4, 5);
		winner = new Point(width / 2, 15);

		// Create offscreen Image
		offscreenImage = createImage(width, height);
		offscr = offscreenImage.getGraphics();

		// Setup text font for displaying the score
		font = new Font("TimesRoman", Font.PLAIN, 14);
		fontMet = getFontMetrics(font);
		fontHeight = fontMet.getAscent();

		//set up mouse listener
		MseL m = new MseL();
		addMouseListener(m);
		addMouseMotionListener(m);

		gun = new Gun(table.height /2, Color.blue);
		target = new Target();
	}


	public void run ()
	{
		int	delay = 20, win_show = 1000, bulletSize = 10;

		while (running)
		{
			if ((gstate & (SHOOT | SHOT | HIT | MISS)) != 0)
			{
				gun.move(table, trackY);
				target.move(table);
			}

			switch (gstate)
			{
				case WAIT:
					if (!mouse_in)
						delay = 20;

					else if (--delay < 0)
					{
						//ready to go
						isVisible = false;
						gun.move(table, trackY);
						target.move(table);
						//winner show for length
						win_show = 100;
					}

					break;

				case SHOOT:
					//shoot the ball

					if (!isVisible)
					{
						bullet = new Bullet(15, trackY, 14, bulletSize, Color.black);
						isVisible = true;
					}
					gstate = SHOT;
					break;

				case SHOT:
					//bullet flying thru air
					bullet.move(table);
					isHit = bullet.checkHit(target);

					if(isHit == true)
					{
						isVisible = false;
						gstate = HIT;
						break;
					}

					if(bullet.x > (table.width - bullet.radius))
					{
						isVisible = false;
						gstate = MISS;
						break;
					}

					else
						gstate = SHOT;

					break;

				case HIT:
					// create explosion
					gstate = PSCORE;
					break;

				case MISS:
					// Wait for bullet to move off table
					gstate = GSCORE;
					break;

				case PSCORE:
					// Increment player's score and check if it has won
					pScore++;
					if (pScore >= 10)
					{
						gstate = PWON;
						gScore = 0;
						pScore = 0;
					}
					else
					{
						target = new Target();
						gstate = WAIT;
					}
					break;

				case GSCORE:
					// Increment computer's score and check if it has won
					gScore++;
					if (gScore >= 10)
					{
						gstate = GWON;
						gScore = 0;
						pScore = 0;
					}
					else
					{
						target = new Target();
						gstate = WAIT;
					}
					break;

				case PWON:
				// Delay while we show who won
				gScore = 0;
				pScore = 0;
				if (--win_show > 0)
				{
					gstate = PWON;
					isVisible = true;
				}
				else
				{
					target = new Target();
					gstate = WAIT;
					isVisible = false;
				}
					break;

				case GWON:
					// Delay while we show who won
					gScore = 0;
					pScore = 0;
					if (--win_show > 0)
					{
						gstate = GWON;
						isVisible = true;
					}
					else
					{
						target = new Target();
						gstate = WAIT;
						isVisible = false;
					}
					break;
			}

			repaint();
			try
			{
				ticker.sleep(1000 / 30);
			} catch (InterruptedException e) { ; }
		}
	}

	public void paint (Graphics g)
	{
		if (offscr == null)
		{
			offscreenImage = createImage(width, height);
			offscr = offscreenImage.getGraphics();
		}

		// Fill offscreen buffer
		offscr.setColor(Color.green);
		offscr.fillRect(0, 0, table.width, table.height);

		// Draw border
		offscr.setColor(Color.black);
		offscr.drawRect(0, 0, 399, 399);

		// Draw Scores
			offscr.setFont(font);
			centerText(offscr, game, Color.gray, "MISSES:  " + gScore);
			centerText(offscr, player, Color.gray, "HITS:  " + pScore);

			if (gstate == GWON)
				centerText(offscr, winner, Color.black, "You Lose");

			if (gstate == PWON)
				centerText(offscr, winner, Color.black, "You Win");

		else
		{
			// Draw gun
			if (gstate == SHOOT || gstate == SHOT || gstate == MISS || gstate == HIT
				|| gstate == WAIT || gstate == GSCORE || gstate == PSCORE)
			{
				offscr.setColor(Color.blue);
				gun.draw(offscr);
			}

	 		// Draw bullet
			if (gstate == SHOT || gstate == MISS)
		 	{
			offscr.setColor(Color.black);
			bullet.draw(offscr);
			}

			// Draw target
			if (gstate == SHOOT || gstate == SHOT || gstate == MISS || gstate == WAIT)
			{
				offscr.setColor(Color.red);
				target.draw(offscr);
			}

			// Draw explosion
			if (gstate == HIT)
			{
				int i = target.width;
				while(i > 0)
				{
					target.drawExplosion(offscr);
					i--;
				}
			}
		}

		g.drawImage(offscreenImage, 0, 0,this);
	}

	public void update (Graphics g)
	{
		paint(g);
	}

	public double rnd (double range)
	{
		return (double) Math.random() * range;
	}

	public int rndInt (int range)
	{
	// Returns an int between 0 and  (exclusive)
		return (int) (Math.random() * range);
	}

	private void centerText (Graphics g, Point loc, Color clr, String str)
	{
		g.setColor(clr);
		g.drawString(str, (loc.x - (fontMet.stringWidth(str) / 2)),
			loc.y + fontHeight);
	}

	private class MseL implements MouseListener, MouseMotionListener
	{
		public void mouseMoved(MouseEvent e)
		{
			trackY = e.getY();
			trackX = e.getX();
		}

		public void mouseDragged(MouseEvent e) {}

		public void mousePressed(MouseEvent e)
		{
			if((gstate != SHOT) && (gstate != MISS) && (gstate != HIT) &&
				(gstate != PSCORE) && (gstate != GWON) && (gstate != PWON))
			{
			gstate = SHOOT;
			trackY = e.getY();
			}
		}

		public void mouseReleased(MouseEvent e) {}

		public void mouseClicked(MouseEvent e) {}
		public void mouseEntered(MouseEvent e)
		{
			mouse_in = true;
			running = true;
		}

		public void mouseExited(MouseEvent e)
		{
			mouse_in = false;
		}
	}

	// Start and stop the animation when the Applet is not visible in browser.
	public void start ()
	{
		if (ticker == null  ||  !ticker.isAlive())
		{
			running = true;
			ticker = new Thread(this);
			ticker.setPriority(Thread.MIN_PRIORITY + 1);
			ticker.start();
		}
	}

	public void stop ()
	{
		running = false;
	}
}