//To use Frame and Graphics
import java.io.*;
import java.awt.*;
import java.awt.image.*;
import javax.imageio.*;
//To use Geometry
import java.awt.geom.*;
//To use a Listener for closing window
import java.awt.event.*;


import java.util.LinkedList.*;
import java.util.*;
import java.awt.image.BufferedImage;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
//-------------------------------------------------------------------

//Extend the Frame Class to build our Graphics Frame
public class TestGraphics extends Frame

implements WindowListener

{

                private Image dbImage;
                private Graphics buffer;

               // Variable needed to get key states
               private static  GameKey game_key=new GameKey();
               //Variable for maximum number of box enemies 
               private static int maxBoxes=100;
               //arrays for enemy boxes
                private static int[] x=new int[maxBoxes];
                private static int[] y=new int[maxBoxes];
                private static int[] xVel=new int[maxBoxes];
                private static int[] yVel=new int[maxBoxes];
                private static int[] energy=new int [maxBoxes];
                private static boolean[] enemyExists=new boolean[maxBoxes];
                
                //Variable used for maximum number of bullets
                private static int maxBullets=100;
                //arrays for player bullets
                private static int[] bulletX=new int[maxBullets];
                private static int[] bulletY=new int[maxBullets];
                private static boolean[] bulletExists=new boolean[maxBullets];
                //variable to check is another bullet can be fired yet
                private static int bulletTimer=0;
                //variable to adjust the rotation of enemy boxes
                private static double spin=0.01;
                
                //variable used for keeping the games score, timing the appearance
                //of enemies, the rate at which enemies are generated
                private static int score=0;
                private static int gameTimer=0;
                private static int speedUp=100;
                private static int speedUpCounter=0;
                
                private static BufferedImage img = null;
                
                //variables for player coordinates
                private static int playerX=220;
                private static int playerY=550;

                //a random number generator object
                private static Random generator = new Random();
                
                //has the game started?
                private static boolean start=true;
                //variables for total time in 'Jiffies' that has elapsed
                private static int bigTimer=0;
                //number of hits a player can take before dying
                private static int playerHits=3;
                
                //constructor
                TestGraphics()
                 {
                    super("Asteroid Belter");

                        //add a listener for THIS window
                        addWindowListener(this);



                 }
                //main method for game
                public static void main(String args[]) {
                		
                		//assign default values to the box enemies
                        for(int i=0;i<maxBoxes;i++) {
                            x[i]= generator.nextInt(780) + 10;
                            y[i]=-20;//generator.nextInt(580) + 10;
                            xVel[i]=0;//generator.nextInt(5) + 1;
                            yVel[i]=generator.nextInt(2) + 1;
                            energy[i]=3;
                            enemyExists[i]=false;
                        }
                        //assign default values to the players bullets
                        for(int i=0;i<maxBullets;i++) {
                            bulletExists[i]=false;
                        }
                        


                 //create frame
                        TestGraphics frame = new TestGraphics();
                        //set the size of the frame
                                frame.setSize(480,600);
                                //draw the frame (show calls paint method)
                                frame.setFocusable(true); // Required for keyboard input
                             frame.addKeyListener(game_key); // Add it to your game

                             //display the graphics window
                         frame.show();

                }
                
                //paint method
                public void paint(Graphics g) {

                        //cast graphics g to a graphics2D object
                        Graphics2D g2 = (Graphics2D)g;




                        //Turn on anti-aliasing
                        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
                        
                        //variable to hold random numbers
                        int rand=0;
                        //colour objects 
                        Color red=new Color(255,0,0);
                        Color black=new Color (0,0,0);
                        Color blue=new Color(0,0,255);
                        Color green=new Color(255,150,0);
                        
                        //affine transform object captures the un-transformed g2
                        //object so that transformations can be reset
                        AffineTransform currentState = g2.getTransform();


                        //drawing and logic
                        
                        
                        //if game has started, increase timer values
                        if (start==true){
                        	gameTimer++;
                        	bulletTimer++;
                        	bigTimer++;
                        	
                        	//if 10000 jiffies have elapsed, end the game
                        	if (bigTimer==10000)
                        		start=false;
                        	//if the player has been hit too many time end the game
                        	if (playerHits<0)
                        		start=false;
                        	
                        	//this increases the rate at which asteroid boxes are created
                        	//making it so the game becomes more difficult over timer
                        	if(speedUpCounter==10){
                        		speedUpCounter = 0;
                        		speedUp=speedUp - 5;
                        	}
                        	
                        	//if the player presses the 'z' key, create a new bullet by 
                        	//putting it at the players location and setting the bulletExists[]
                        	//variable to true. Also reset the bulletTimer variable so
                        	// that there is a delay before the next bullet can be shot
                        	if(game_key.key_z)
                        	{
                        		if(bulletTimer>=50){
                        			do
                        				rand=generator.nextInt(maxBullets);
                        			while(bulletExists[rand]==true);
                        			bulletX[rand]=playerX;
                        			bulletY[rand]=playerY;
                        			bulletExists[rand]=true;
                        			energy[rand]=3;
                        			bulletTimer=0;

                        		}
                        	}


                        	//create a new asteroid by giving it a random x Value and a 
                        	//y Velue that is off the screen. Setting it's enemyExists[] variable to 
                        	//true means that the enemy will be updated and drawn. also reset the gameTimer variable
                        	if (gameTimer % speedUp ==0){
                        		do
                        			rand=generator.nextInt(maxBoxes);
                        		while(enemyExists[rand]==true);
                        		y[rand]=-20;
                        		x[rand]=generator.nextInt(460) + 10;
                        		enemyExists[rand]=true;
                        		yVel[rand]=generator.nextInt(1) + 1;
                        		gameTimer=0;
                        		if (speedUp > 20)
                        			speedUpCounter++;
                        	}
                        	
                        	//set the colour to black and draw the score, 
                        	//remaining hits and time on screen
                        	g2.setColor(black);
                        	g2.drawString("Score: "+score,10,50);
                        	g2.drawString("Hits: "+playerHits, 10,75);
                        	g2.drawString("time: "+(10000-bigTimer),10,100);
                        	
                        	//set the colour to green and begin the loop that 
                        	//draws bullets
                        	g2.setColor(green);

                        	for (int i=0;i<maxBullets;i++){
                        		//only draw and update bullets that are on screen
                        		if (bulletExists[i]==true){
                        			//Collision detection, rects overlap
                        			//check through the enemy array, checking to see if 
                        			//any bullets are overlapping an enemy.
                        			// if any are then turn off both the bullet and
                        			//the enemy off by setting the respective exists[] values to false
                        			//also increase the score by 100
                  
                        			for (int o=0;o<maxBoxes;o++){
                        				if (enemyExists[o]==true){
                        					if (rectsOverlap(bulletX[i]-6,bulletY[i]-6,12,12,x[o],y[o],30,30)==true){
                        						bulletExists[i]=false;
                        						enemyExists[o]=false;
                        						score=score+100;

                        					}
                        				}

                        			}
                        			//move the bullet down the screen, if it drops below the height
                        			//of the window then turn it off by setting the exists[] value to false
                        			bulletY[i]=bulletY[i]-3;
                        			if (bulletY[i]<=-20){
                        				bulletExists[i]=false;
                        			}
                        			//draw the bullet on the screen
                        			g2.fillRect(bulletX[i]-3,bulletY[i]-3,6,6);


                        		}
                        	}



                        // increase the rotation value by 1 degree
                        	spin=spin+1;
                        	//begin the enemy loop
                        	for (int i=0;i<maxBoxes;i++) {
                        		//only update enemies which 'exist' ie. are on the screen
                        		if (enemyExists[i]==true){
                        			
                        			//increascethe enemies x velocity by the given ammount
                        			//in the case of this game the xVelocity is 0
                        			x[i]=x[i]+xVel[i];
                        			if (x[i]>=480)
                        				xVel[i]=-xVel[i];
                        			if (x[i]<=0)
                        				xVel[i]=-xVel[i];
                        			
                        			//increase the y Velocity by a given ammount
                        			//if the y coordinate is greater than the height of the screen
                        			//plus the size of the enemy then set the eneimes enemyExists[]
                        			//value to false so it is no longer drawn
                        			y[i]=y[i]+yVel[i];
                        			if (y[i]>=620){
                        				y[i] = -20;
                               	     enemyExists[i]=false;
                        			}
                        			
                        			//in an enemy collides with the player ship then 
                        			//reduce the players hits by one and remove the enemoy from the screen.
                        			//if the enemyExists[] value is not set to false then the enemy Collision 
                        			//would reduce the players hits to 0 in the next two frames, making the 
                        			//game unfair.
                        			if (rectsOverlap(x[i],y[i],30,30,playerX-10,playerY-10,20,20)==true){
                        				enemyExists[i]=false;
                        				playerHits--;
                        			}
                        			//rotate the enemy square around it's centre by the spin value
                        			g2.rotate((Math.PI*2/360)*spin+i,x[i]+15,y[i]+15);
                        			//set the colour to red
                        			g2.setColor(red);
                        			
                        			//draw the enemy on screen
                        			g2.drawRect(x[i],y[i],30,30);

                        		}
                        		//return the graphics object's transformation state to it's
                        		//original un-transformed stae
                        		g2.setTransform(currentState);


                        	}
                        	
                        	//this part of the code controls the players movement, either
                        	//increasing or decreasing the players x value, and also keeping it within the 
                        	//graphics frame
                      
                        	if(game_key.key_left)
                        	{
                        		playerX--;
                        		if (playerX<20)
                        			playerX=20;
                        	}
                        	if(game_key.key_right)
                        	{
                        		playerX++;
                        		if(playerX>460)
                        			playerX=460;
                        	}


                        	//set the colour to blue and draw the player ojn the screen
                        	g2.setColor(blue);
                        	g2.drawRect(playerX-10,playerY-10,20,20);
                        	
                        	//call the repaint() method
                        	repaint();
                        }

                        //if the game ended, with either the time having
                        //run out or the players lives having been reduced to less than 0
                        if (start==false)
                        {
                        	//draw the game over text and final score on screen
                        	g2.setColor(black);
                        	g2.scale(3, 5);
                        	g2.drawString("Game Over",10,50);
                        	g2.drawString("You Scored: "+score,10,70);
                        	g2.setTransform(currentState);
                        }
                }
                
                //update method handles the double buffering that allows the smooth
                //movement of the graphics
                public void update(Graphics g) {
                        if (dbImage == null){
                                // initialize buffer to same size (component)
                                dbImage = createImage(this.getSize().width, this.getSize().height);
                                //put image into buffer
                                buffer = dbImage.getGraphics ();
                        }
                        // get background colour
                        buffer.setColor(getBackground());
                        // clear screen (fill with background colour)
                        buffer.fillRect(0, 0, this.getSize().width, 	this.getSize().height);

                        // draw elements into the buffer using paint()
                        paint(buffer);

                        // draw image on the screen
                        g.drawImage(dbImage, 0, 0, this);
                }


//the rectsOverlap method returns true id two rectangular graphical objects are occupying the same space on the screen

public static boolean rectsOverlap(int x0,int  y0,int  w0,int  h0,int  x2,int  y2,int  w2,int  h2)
{
    if ((x0 > (x2 + w2)) || ((x0 + w0) < x2))
            return false;
    if(y0 > (y2 + h2) ||(y0 + h0) < y2)
            return false;
    return true;
 }

//this class handles keyboard input in order to allow a user to move the players ship around the screen
             public static class GameKey implements KeyListener // Keyboard input
                {
                    public boolean key_right,key_left,key_up,key_down,key_z,key_x;
                    public void keyTyped(KeyEvent e){}
                    public void keyPressed(KeyEvent e)
                    {
                 if(e.getKeyCode() == e.VK_DOWN)
                     key_down = true;
                 if(e.getKeyCode() == e.VK_UP)
                     key_up = true;
                 if(e.getKeyCode() == e.VK_LEFT)
                     key_left = true;
                 if(e.getKeyCode() == e.VK_RIGHT)
                     key_right = true;
                 if(e.getKeyCode() == e.VK_Z)
                     key_z = true;
                 if(e.getKeyCode() == e.VK_X)
                     key_x = true;
                    }
                    public void keyReleased(KeyEvent e)
                    {
                 if(e.getKeyCode() == e.VK_DOWN)
                     key_down = false;
                 if(e.getKeyCode() == e.VK_UP)
                     key_up = false;
                 if(e.getKeyCode() == e.VK_LEFT)
                     key_left = false;
                 if(e.getKeyCode() == e.VK_RIGHT)
                     key_right = false;
                 if(e.getKeyCode() == e.VK_Z)
                     key_z = false;
                 if(e.getKeyCode() == e.VK_X)
                     key_x = false;
                    }
                }

//code for the various window things etc.
                public void windowActivated(WindowEvent event)
        {
        }

        public void windowDeactivated(WindowEvent event)
        {
        }

        public void windowClosed(WindowEvent event)
        {
        }

        public void windowDeiconified(WindowEvent event)
        {
        }

        public void windowIconified(WindowEvent event)
        {
        }

        public void windowOpened(WindowEvent event)
        {
        }

        public void windowClosing(WindowEvent event)
        {
               System.exit(0);
        }



}

