//---------------------------------------------------------------------
// CS100B   HOMEWORK#5
//
// Created : W.A.Yoon   1999/11/05
//
// ADD STUDENT INFO HERE:
//
//
//---------------------------------------------------------------------

import java.awt.*;
import java.awt.event.*;
import java.io.*;

public class hw5 extends Frame implements KeyListener
{
    //labels on screen for score and Projectile
    Label score;
    Label scoreLabel;
    Label numProjectile;
    Label numProjectileLabel;
    Graphics g;
    
    //objects in the program
    Wall wall;
    Ufo ufo;
    Tank tank;
    Projectile projectile;
    
    //variables used for the flow of the control
    boolean isFired;
    boolean GameOver;
    String action;  
    
    //constant: size of screen 
    static final int ScreenSizeX = 800; 
    static final int ScreenSizeY = 600; 
    boolean redrawn;
    
    //screen layout frame constructor
    public hw5()
    {    
	//setting up environment
	setLayout(null);
	setTitle("CS100B Homework#5");  
	addWindowListener(new CloseWindowAndExit());
	addKeyListener(this);
	requestFocus();
	
	//layout parameter setting
	setSize(800,600);
	setBackground(Color.white);
	score = new Label("0");
	scoreLabel = new Label("SCORE");
	score.setBounds(150,50,100,20);
	scoreLabel.setBounds(50,50,100,20);
	numProjectile = new Label("10");
	numProjectileLabel = new Label("PROJECTILE");
	numProjectile.setBounds(150,70,100,20);
	numProjectileLabel.setBounds(50,70,100,20);
	setVisible(true);
	add(scoreLabel);
	add(score);
	add(numProjectileLabel);
	add(numProjectile);
	
	//initial values 
	redrawn = false;
	action = "BEGIN";
	isFired = false;	
	GameOver= false;
	g = getGraphics();	
	
	//define objects and start UFO
	tank = new Tank(55, ScreenSizeX/2 - 55, ScreenSizeY - 10,g);
	wall = new Wall(ScreenSizeX/2, ScreenSizeY - 10,g);
	ufo = new Ufo(ScreenSizeX, ScreenSizeY,g);
	ufo.start();
	
      	//drawing backgroud
	paint();
	
	setVisible(false);
	setVisible(true);
	
    }
    
    
    //key event handling : define controlling keys
    public void keyReleased(KeyEvent e){ }
    public void keyTyped(KeyEvent e){ }
    public void keyPressed(KeyEvent e)
    {
	//get the key pressed
	String a = e.getKeyText(e.getKeyCode());
	action = " ";	
	
	//set the action accordingly
	if (a.equalsIgnoreCase("Right")) action = "MOVE_RIGHT";
	if (a.equalsIgnoreCase("Left")) action = "MOVE_LEFT";
	if (a.equalsIgnoreCase("Up")) action = "CANNON_UP";
	if (a.equalsIgnoreCase("Down")) action ="CANNON_DOWN";
	if (a.equalsIgnoreCase("Insert")) action ="POWER_UP";
	if (a.equalsIgnoreCase("Delete")) action = "POWER_DOWN";
	if (a.equalsIgnoreCase("Space")) action = "FIRE";	
	if (a.equalsIgnoreCase("Enter")) action = "RESTART";
	//call action handler
	
    	paint();
    }
    
    
    //Action handler: update screen and invoke action
    public void paint() 
    {
	g = getGraphics();
	g.setPaintMode();
	
	//call action methods
	System.out.println(action);
	if (action.equalsIgnoreCase("RESTART")) RestartGame();
	if (action.equalsIgnoreCase("MOVE_RIGHT")) tank.MoveRight(g);
	if (action.equalsIgnoreCase("MOVE_LEFT")) tank.MoveLeft(g);
	if (action.equalsIgnoreCase ("CANNON_UP")) tank.IncreaseAngle(g);
	if (action.equalsIgnoreCase("CANNON_DOWN")) tank.DecreaseAngle(g);
	if (action.equalsIgnoreCase("POWER_UP")) tank.IncreaseInitialVelocity(g);
	if (action.equalsIgnoreCase("POWER_DOWN")) tank.DecreaseInitialVelocity(g);
	if ( (action.equalsIgnoreCase("FIRE")) && 
	     (isFired == false) && (GameOver == false)) {
	    isFired = true;
	    ProjectileFired(g);
	}
	wall.DrawWall(g);
	
    	//reset the action
    	action =" ";
	redrawn = true;
    }
    
    public void paint(Graphics g) 
    {
	if (redrawn){
	    wall.DrawWall(g);
	    tank.DrawTank(g);
        }
    }
    
    
    
    // Once the ProjectileFire button (spacebar) is pressed
    // the following code is executed.
    // This procedure traces the course of Projectile's trajectory
    // and checks if it hit wall, hit UFO, or went out of boundary
    public void ProjectileFired(Graphics g) 
    {
	//get the initial location where the Projectile is fired
	int x = tank.GetCurrentX();
	double a = tank.GetAngle();
	double v = tank.GetVelocity();
	
	//create the Projectile and initialize it
	int projectileX=x;
	int projectileY=ScreenSizeY-20;
	projectile = new Projectile(projectileX,projectileY, a, v);	
	//print the initial angle and velocity
	System.out.println("Initial velocity = "+v);
	System.out.println("Initial angle = "+a);
	
	//trace the flying projectile until it hits something	
	boolean hit = false;
	double t = 0.0;  //time
	while (hit == false) {
	    //move projectile one step and get the current
	    //postion of Projectile
	    projectile.MoveProjectile(g,t);
	    projectileX=projectile.GetCurrentX();
	    projectileY=projectile.GetCurrentY();
	    //print the coordinate
	    System.out.println("x = "+projectileX+ " y = "+projectileY);
	    //check if the projectile hit the wall
	    if (wall.DidItHit(projectileX, projectileY)) {
		projectile.BlowUp(g); 
		DecreaseNumProjectile();  //you lost one Projectile
		hit = true;
	    }
	    
	    //check if the Projectile hit the UFO
	    if (ufo.DidItHit(projectileX, projectileY)) {
		int s = ufo.GetScore(projectileX, projectileY);
		projectile.BlowUp(g);
		ufo.Repair(g);    //UFO never dies.....
		AddScore(s);
		hit = true;
	    }
	    
	    //check if the Projectile went off of the screen
	    if (OutOfBound(projectileX, projectileY)) {
		projectile.BlowUp(g);
		DecreaseNumProjectile(); //you lose one projectile
		hit = true;
	    }
	    
	    tank.DrawTank(g);
	    
	    //used as speed control
	    mySleep(10);
	    t=t+0.1;
	}
	
	//start over
	action = " ";	
	isFired = false;
	paint(g);
    }
    
    
    //boolean function that checks if the projectile is out of bounds
    private boolean OutOfBound(int x, int y) 
    {
	if ( (x<10) ||
	     (x>ScreenSizeX-10) ||
	     (y<10) ||
	     (y>ScreenSizeY-10))
	    return true;
	else
	    return false;
    }
    
    
    //method that adds the screen
    private void AddScore(int s) 
    {
	String s1 = score.getText();
	int s2 = Integer.parseInt(s1);
	s2 = s2 + s;
	score.setText(Integer.toString(s2));
    }
    
    
    //method that decreases the number of projectiles
    private void DecreaseNumProjectile() 
    {
	String s1 = numProjectile.getText();
	int s2 = Integer.parseInt(s1);
	s2 = s2 - 1;
	numProjectile.setText(Integer.toString(s2));
	
	//if number of bom is 0 then GAME OVER!!!
	if (s2 == 0) {
	    numProjectile.setText("GameOver");
	    GameOver = true;
	}
    }
    
    
    //Restarts the game
    private void RestartGame()
    {
	GameOver = false;
	numProjectile.setText("10");
	score.setText("0");
    }
    
    //just delay function used for speed control
    private void mySleep(long t)
    {
	try
	    {
		Thread.currentThread().sleep(t);
	    }
	catch (InterruptedException ie)
	    {
		ie.printStackTrace();
	    }
    }
    
    
    //main initializes the screen and lets it run
    public static void main  (String[] argc) throws IOException
    {
	hw5 game = new hw5();
	game.setVisible(true);
    }
}

//this class closes the window and exits the program when
//"window close button" was clicked by mouse
class CloseWindowAndExit extends WindowAdapter 
{
    public void windowClosing (WindowEvent e)
    {
	e.getWindow().dispose();
	System.exit(0);
    }
}

// wall class --- which does nothing but block the projectile
class Wall {
    //location of wall
    int wallX;
    int wallY;
    
    //constructor -- just setting the location
    public Wall(int x, int y, Graphics g)
    {
	wallX = x;
	wallY = y;
	DrawWall(g);	
    }
    
    //draw the wall on the screen
    public void DrawWall(Graphics g)
    {
	g.setColor(Color.lightGray);
	g.fill3DRect(wallX -10, wallY/2, 20, wallY/2-30,true);
	g.fill3DRect(wallX -20, wallY - 30, 40, 30, true);
    }
    
    //checks if the projectile hit the wall
    public boolean DidItHit(int x, int y) 
    {	
	if ((x <= wallX +10) &&
	    (x >= wallX -10) &&
	    (y <= wallY) &&
	    (y >= wallY/2-5))
	    return true;
	else
	    return false;
    }
}


//this class is UFO class
class Ufo extends Thread
{
    //current location of ufo
    private int ufoY;
    private int ufoX;
    //boundary
    private int minY;
    private int maxY;
    private int minX;
    private int maxX;
    //movement control
    private int ufoInterval;
    private int ufoMoveCount;
    private boolean AmIBroken;
    
    private Graphics g;
    
    //constructor
    Ufo(int x, int y, Graphics gr)
    {   
	//initializing
	ufoMoveCount = 0;
	minX = x/2 + 70;
	maxX = x - 70;
	minY = 100;
	maxY = y-100;
	ufoY = 100;
	ufoX = x-70;
	ufoInterval = 1;
	AmIBroken = false;
	g = gr;
    }
    
    
    //draw UFO on the screen 
    public void DrawUfo(Graphics g)
    {
	//select color 
	if (AmIBroken) g.setColor(Color.red);
	else g.setColor(Color.black);
	//draw body
	g.fillOval(ufoX-40,ufoY-15, 80, 30);
	g.drawLine(ufoX,ufoY-15,ufoX-10,ufoY-25);
	g.drawLine(ufoX,ufoY-15,ufoX,ufoY-25);
	g.drawLine(ufoX,ufoY-15,ufoX+10,ufoY-25);
	g.setColor(Color.yellow);
	g.fillOval(ufoX-23,ufoY,6,6);
	g.fillOval(ufoX-3,ufoY,6,6);
	g.fillOval(ufoX+17,ufoY,6,6);
    }
    
    //Erase UFO simply by erasing the outline only
    //XOR mode can be used as suggested in the text book, but it 
    //causes blinking and does move smoothly
    public void EraseUfo(Graphics g)
    {	
	//erase body
	g.setColor(Color.white);
	g.drawOval(ufoX-41,ufoY-16, 82, 32);
	g.drawOval(ufoX-40,ufoY-15, 80, 30);
	g.drawLine(ufoX,ufoY-15,ufoX-10,ufoY-25);
	g.drawLine(ufoX,ufoY-15,ufoX,ufoY-25);
	g.drawLine(ufoX,ufoY-15,ufoX+10,ufoY-25);
    }
    
    //check if projectile hit the UFO
    public boolean DidItHit(int x, int y){
	if ((x<= ufoX+40) &&
	    (x>= ufoX-40) &&
	    (y<= ufoY+15) &&
	    (y>= ufoY-15))
	    {	
		//in case it is hit by projectile
		//it becomes broken and does not move
		ufoInterval = 0;
		AmIBroken = true;
		return true;
	    }
	else
	    return false;
    }
    
    //calculate score: 
    //200 is base score for a hit and distance from projectile to the center
    //of UFO is substracted from the base score
    public int GetScore(int x, int y) {
	int x1 =(int)(Math.sqrt( (x-ufoX)*(x-ufoX) + (y-ufoY)*(y-ufoY)));
	return (200 - x1);
    }
    
    //when UFO is hit, it can be repaired
    //in fact, it will never be destroyed in this game!!!!!
    public void Repair(Graphics g) {
	mySleep(1000);
	g.setColor(Color.white);
	g.fillRect(ufoX-50,ufoY-30, 100,60);
	ufoInterval=1;
    	ufoX = (int)(Math.random()* (maxX-minX) + minX);
    	AmIBroken = false;
    }
    
    
    //just delay function used for speed control
    private void mySleep(long t)
    {
	try{
	    Thread.currentThread().sleep(t);
	}
	catch (InterruptedException ie){
	    ie.printStackTrace();
	}
    }
    
    //main loop that makes the UFO move
    public void run()
    {
	while (true) {
	    //in every movement of 70 count, it 
	    //changes direction or keeps the direction
	    //according to the random value
	    if  (ufoMoveCount > 70) {
		if (Math.random() > 0.5) {
		    ufoInterval= ufoInterval * -1;
		}
		ufoMoveCount = 0;
	    }
	    
	    
	    ufoMoveCount++;
	    mySleep(5);
	    
	    //erase ufo
	    EraseUfo(g);
	    
	    //new UFO position
	    ufoY = ufoY + ufoInterval;
	    
	    //check its boundary
	    if (ufoY > maxY) {
		ufoY = maxY;
		ufoInterval = ufoInterval * -1;
	    }
	    if (ufoY < minY) {
		ufoY = minY;
		ufoInterval = ufoInterval * -1;
	    }
	    
	    //draw ufo
	    DrawUfo(g);
	}
    }
}

// TANK class
class Tank
{
    //boundary: tank can only move within this boundary
    private int xMin;
    private int xMax;
    
    //current tank position 
    private int xPos;   //center of tank
    private int yPos;   //bottom of tank
    
    //this is delta X, when <- or -> is pressed the
    //xPos is changed by this interval value
    private int IntervalX = 10;
    
    //current angle of the cannon in RADIANs not degrees
    private double angle;
    private double IntervalAngle = 0.1;
    
    //current setting of initial velocity (m/s)
    private double velocity;
    private double IntervalVelocity = 5.0;
    
    //initial velocity can only be within this range
    private double minVelocity = 10.0;
    private double maxVelocity = 200.0;
    
    
    //constructor just sets the initial values and draws tank 
    //----------------------------------------------------------
    //input: xmin - minimum boundary of the tank's x position
    //       xmax - maximum boundary of the tank's x position
    //       ypos - y positon of tank, this is the bottom of the
    //              tank, not the center
    //output: none
    //----------------------------------------------------------
    public Tank(int xmin, int xmax, int ypos, Graphics g)
    {
	xMin = xmin;
	xMax = xmax;
	yPos = ypos;
	xPos = 80;
	angle = 0.5;
	velocity = (minVelocity + maxVelocity)/2;
	g.setColor(Color.blue);
	DrawTank(g);
    }
    
    //draw Tank on the screen
    //tanks dimention should not be bigger than 100 x 100 pixel
    //tank can be any shape you like
    //however xPos is the center of tank, and yPos is the bottom
    //of the tank
    public void DrawTank(Graphics g)
    {
	g.setColor(Color.blue);
	//shape of cannon
	Polygon cannon = new Polygon();
	int x1 = (int)(xPos + Math.cos(angle)*40);
	int y1 = (int) (yPos - 20 - Math.sin(angle)*40);
    	cannon.addPoint(xPos-10,yPos-20);
	cannon.addPoint(x1,y1);
	cannon.addPoint(xPos+10, yPos-20);
	cannon.addPoint(xPos-10,yPos-20);
	g.fillPolygon(cannon);
	//body
	g.fillRect(xPos-30, yPos-20, 60,20 );
	}

    //Erase Tank from the screen
    public void EraseTank(Graphics g)
    {
	g.setColor(Color.white);
	//shape of cannon
	Polygon cannon = new Polygon();
	int x1 = (int)(xPos + Math.cos(angle)*40);
	int y1 = (int) (yPos - 20 - Math.sin(angle)*40);
	cannon.addPoint(xPos-10,yPos-20);
	cannon.addPoint(x1,y1);
	cannon.addPoint(xPos+10, yPos-20);
	cannon.addPoint(xPos-10,yPos-20);
	g.fillPolygon(cannon);
	//body
	g.fillRect(xPos-30, yPos-20, 60 ,20);
    }
    
    
    //move tank to the left (one interval)
    //you need to erase the tank in the current position and
    //redraw at the new posion
    //Remember that tank can not go out of its boundary
    public void MoveLeft(Graphics g)
    {	
	EraseTank(g);
	xPos = xPos - IntervalX;
	if (xPos < xMin) xPos = xMin;
	DrawTank(g);		
    }
    
    //move tank to the right (one interval)
    //description same as above
    public void MoveRight(Graphics g)
    {
	EraseTank(g);
	xPos = xPos + IntervalX;
	if (xPos > xMax) xPos = xMax;
	DrawTank(g);
    }
    
    //increase the angle of cannon (firing angle)
    //if you are going  to draw cannon, you should
    //redraw the cannon according to the new angle
    public void IncreaseAngle(Graphics g)
    {
	EraseTank(g);
	angle = angle + IntervalAngle;
	DrawTank(g);
    }
    
    //Decrease the angle of cannon
    public void DecreaseAngle(Graphics g)
    {
	EraseTank(g);
	angle = angle - IntervalAngle;
	DrawTank(g);
    }
    
    //Increase initial velocity setting
    //Remember that you have a range
    public void IncreaseInitialVelocity(Graphics g)
    {
	g.setColor(Color.white);
	g.fillRect(10,yPos- (int)velocity,10,(int)velocity);
	velocity = velocity + IntervalVelocity;
	if (velocity > maxVelocity) velocity = maxVelocity;
	g.setColor(Color.blue);
	g.fillRect(10,yPos- (int)velocity,10,(int)velocity);
    }
    
    //Decrease initial velocity setting
    public void DecreaseInitialVelocity(Graphics g)
    {
	g.setColor(Color.white);
	g.fillRect(10,yPos- (int)velocity,10,(int)velocity);
	velocity = velocity - IntervalVelocity;
	if (velocity < minVelocity) velocity = minVelocity;
	g.setColor(Color.blue);
	g.fillRect(10,yPos- (int)velocity,10,(int)velocity);
    }
    
    
    //returns the current x location of the tank
    public int GetCurrentX()
    {
	return xPos;
    }
    
    //returns the current firing angle of the tank
    public double GetAngle()
    {
	return angle;
    }
    
    //returns the current initial velocity setting
    public double GetVelocity()
    {
	return velocity;
    }
}



class Projectile 
{
    //initial position of Projectile
    private double xInit;
    private double yInit;
    
    //current positon of Projectile
    private double xPos;
    private double yPos;
    
    //initial firing angle in RADIAN not in degree
    private double angle;
    
    //initial velocity
    private double velocity;
    
    //g constant (m/s^2)
    private final static double G = 9.81;
    
    
    
    //constructor : set initial values
    //----------------------------------------------------------
    //input : x - initial firing x position 
    //        y - initial firing y position
    //        a - initial firing angle
    //        v - initial velocity
    //----------------------------------------------------------
    public Projectile (int x, int y, double a, double v)
    {
	xInit = (double)x;
	yInit = (double)y;
	xPos = (double)x;
	yPos = (double)y;
	angle = a;
	velocity = v;
    }
    
    //draw the projectile at the current location
    //the dimension of projectile should not be bigger than 20 x 20 pixels
    public void DrawProjectile(Graphics g)
    {

	///////////////////////////////////////////////////
	/// ADD CODE HERE                               ///
	///////////////////////////////////////////////////





    }
    
    //erase the projectile at the current position
    public void EraseProjectile(Graphics g)
    {

	///////////////////////////////////////////////////
	/// ADD CODE HERE                               ///
	///////////////////////////////////////////////////




    }
    
    
    //it calculates next position (by calling CalculateYpos
    //and CalculateXpos) and moves the projectile to the next position.
    //This invloves erasing and drawing 
    //-----------------------------------------------------------
    //input : t - time
    //-----------------------------------------------------------
    public void MoveProjectile(Graphics g, double t)
    {
	EraseProjectile(g);
	xPos = calculateXPos(t);
	yPos = calculateYPos(t);
	DrawProjectile(g);
    }
    
    
    //make special effect (drawings) to represent 
    //projectile blowing up or just erase the projectile.
    //you can make the explosion as messy as you want but you have to 
    //clean up everything, after you fininsh your "presentation"
    //of projectile blowing up
    //if you don't want anything, then just erase the projectile
    //on the screen
    public void BlowUp(Graphics g)
    {
	for (int i = 1 ; i<= 50; i++)
	    {
		g.setColor(Color.red);
		g.drawOval((int)xPos-i, (int)yPos-i, i*2, i*2);
		// just delay
		mySleep(10);
	    }
	g.setColor(Color.white);
	g.fillOval((int)xPos-60, (int)yPos-60, 120,120);
    }
    
    
    //calculate new x position
    //-----------------------------------------------------
    // input : t- time
    // output: new x positon
    //
    // note: you should use "angle", and "velocity", along
    //       with the input field in order to calculate
    //       You should also remember that your initial
    //       firing posion is not 0
    //-----------------------------------------------------

    private double calculateXPos(double t)
    {


	///////////////////////////////////////////////////
	/// ADD CODE HERE                               ///
	///////////////////////////////////////////////////




    }
    
    
    //calculate new y position
    //-----------------------------------------------------
    // input : t- time
    // output: new y positon
    //
    // note: you shold use "angle", and "velocity", along
    //       with the input field in order to calculate
    //       Remember the coordinate system of computer 
    //       screen
    //-----------------------------------------------------
    private double calculateYPos(double t)
    {


	///////////////////////////////////////////////////
	/// ADD CODE HERE                               ///
	///////////////////////////////////////////////////



    }
    
    
    //return the current x position of projectile
    public int GetCurrentX()
    {
	return (int)xPos;
    }
    
    //return the current y position
    public int GetCurrentY()
    {
	return (int)yPos;
    }
    
    
    //just delay function used for speed control 
    //you don't have to use it if you don't need.
    //It just delays t millisec.
    private void mySleep(long t)
    {
	try {
	    Thread.currentThread().sleep(t);
	}
	catch (InterruptedException ie) {
	    ie.printStackTrace();
	}
    }
}


