import java.applet.*;
import java.awt.*;
import java.util.Date;

public class Shooter extends Applet implements Runnable
{

    boolean alive = true;
    
    Image all;
    final static int height = 400;
    final static int width = 500;
    final static int sleepTime = 50;

    
    Graphics buffer; 
    final static int totalLasers = 14;
    final static int totalPhotons = 10;

    Thread runner = null;
    boolean fire = false;
    long timer;
    long realSleep;
    
    
    /*get rid of this later */
    //long timer2;
    //long difference;
    /*get rid of this later*/

    
    int laserCounter;
    int rechargeTime;

    int score;
    int sendEnemy;
    int canFire;

    final static int totalEnemies = 10; 
    Laser laser[] = new Laser[totalLasers];
    Enemy enemy[] = new Enemy[totalEnemies];//only six ememies so far
    Photon photon[] = new Photon[totalPhotons];
    int photonIndex;//current Photon


    final static int maxDelay = 10;
    int newStar;
    int starIndex;
    final static int maxStars = 50;
    Point stars[] = new Point[maxStars];
    
    Ship ship;

    Image defender;
    Image alien;
    Image interceptor;
    Image background;
    int bgIndex = 0;
    int bgHeight = 0;

    MediaTracker tracker = new MediaTracker(this);
    boolean pixLoaded = false;
    
    public static AudioClip scream;
    AudioClip zap;
    AudioClip gotThem;
    
    public void init(){
        
       	scream = getAudioClip(getDocumentBase(),"scream.au");
	    zap = getAudioClip(getDocumentBase(),"zap.au");
	    gotThem = getAudioClip(getDocumentBase(),"enemy.au");

        
        defender = getImage(getDocumentBase(),"Ship3.gif");
        tracker.addImage(defender,1);
        //alien = getImage(getDocumentBase(), "Enemy.gif");
        //tracker.addImage(alien,2);
        alien = getImage(getDocumentBase(), "Interceptor.gif");
        tracker.addImage(alien,1);
        background = getImage(getDocumentBase(),"moon.jpg");
        tracker.addImage(background,1);

        try {
            tracker.waitForAll();
            if (tracker.isErrorAny() ) { 
                System.out.println("error loading");
                return; 
            }
        }
        catch (Exception e) {
            System.out.println("Exception");
            return;
        }
        pixLoaded = true;

        all = createImage(size().width,size().height);
        buffer = all.getGraphics();

        buffer.setColor(Color.black);
        buffer.fillRect(0,0,size().width,size().height);

        //alien = interceptor;               
        bgHeight = background.getHeight(this);
        bgIndex = 1-bgHeight;
        intialize();
    }

    /** Called by init or when the user starts again */
    void intialize()
    {
        newStar = maxDelay;
        starIndex = 0;
        photonIndex = 0; //current Photon
        laserCounter = 0;
        rechargeTime = 0;
        sendEnemy = 50;
        canFire = 0;

        //initialize laser and enemy
        for (int i=0; i<stars.length; i++)
        {
            stars[i] = new Point((int)(Math.random()*width),width-(i*maxDelay));
        }
        
        for(int i=0; i<totalEnemies;i++){
            enemy[i] = new Enemy(alien, alien.getHeight(this),alien.getWidth(this));
        }
        
        for(int i=0; i<totalLasers;i++){
            laser[i] = new Laser();
        }

        for(int i=0; i<totalPhotons;i++){
            photon[i] = new Photon();
        }

        ship = new Ship(defender, defender.getHeight(this), defender.getWidth(this));
        score=0;
        this.requestFocus();
    }


    public void start()
    {
        if (runner == null) {
            runner = new Thread(this);
            runner.start();
        }
    }

    public void stop()
    {
        runner = null;
    }
    
    public void run()
    {

        if (!pixLoaded) {
            System.out.println("pictures didn't load");
            return;
        } //pictures didn't load
        
        runner.setPriority(runner.MIN_PRIORITY);
        while(runner!=null){
                

            timer = System.currentTimeMillis() + sleepTime;
            //long timer2 = System.currentTimeMillis();
         
            if (alive)
        	{
	            
	            /*if (newStar == maxDelay)
	            {
	                newStar = 0;
	                if (starIndex == maxStars) starIndex=0;
	                stars[starIndex].x = (int)(Math.random()*(width));
	                stars[starIndex].y = 0;
	                starIndex++;
	            }
	            else { newStar++;}*/
	            
	            if (sendEnemy == 50) {
	                sendEnemy = 0;
	                for(int i=0; i<totalEnemies;i++){
	                    if (!enemy[i].active()){
	                        enemy[i].setPosition((int)(Math.random()*(width-30)),0);
	                        break;
	                    }
	                }
	            }
	            else {
	                sendEnemy++;
	            }
	                    
	            
	    
	
	            /*if (fire){
	                if (rechargeTime == 0) {
	                    rechargeTime = 3;
	                    laserCounter++;
	                    if (laserCounter >= totalLasers){
	                        laserCounter = 0;
	                    }
	                    laser[laserCounter].setPosition(ship.x+(ship.width/2), ship.y);
	                    zap.play();
	                }
	                else {rechargeTime--;}
	            }*/
	            
	            if (ship.fired)
        		{
        			laser[laserCounter].setPosition(ship.x+(ship.width/2), ship.y);
        			zap.play();
        			laserCounter = (laserCounter+1)%totalLasers;
        			ship.canFire = false;
        			ship.fired = false;
        		}
	
	            for(int k=0; k<totalEnemies;k++){
	                if (enemy[k].active()){
	                    if (hit(enemy[k])){
	                        score++;
	                        enemy[k].explode();
	                        gotThem.play();
	                    }
	                }
	            }
	            
	            paint2(buffer);
	            alive = !dead();
        	}
	
	        else paintEnd(buffer);	
	        repaint();

            try {
            	runner.yield();
            	//long thing = Math.max(0,timer-(System.currentTimeMillis()));
            	//System.out.println(timer2+","+System.currentTimeMillis());

            	//System.out.println(thing);
                runner.sleep(Math.max(0,timer-(System.currentTimeMillis())));
                //runner.sleep(sleepTime);
            }
            catch (InterruptedException e) {stop();}
        }
    }
    public void update(Graphics g){
        if (all!= null&&pixLoaded){
            g.drawImage(all,0,0,null);
        }
        //paint(g);
    }

    public void paint(Graphics g)
    {
        if (all!=null&&pixLoaded) {
            
            g.drawImage(all,0,0,null);
        }
    }

    public void paintEnd(Graphics g)
    {
        Font previousFont = g.getFont();
        g.setColor(Color.black);
        g.fillRect(0,0, width, height);
        g.setColor(Color.magenta);
        g.setFont(new Font("Times Roman",Font.BOLD,40));
        g.drawString("GAME OVER!",110,140);
        g.drawString("Hit F1 to play again",60,250);
        g.setFont(previousFont);
    }

	final static int NUM_FRAMES = 12;
    // ANIMATION
	// Take the initial (unwaved) image from the left-hand-side of the graphics
	// and make NUM_FRAMES copies of it - the pixels rows of each one dithered
	// up or down depending upon the dispy sine function.
	//---------------------------------------------------------------------------
	public void makeWaves (Graphics g, int phase) 
	{
		double p1;
		int     dispx, dispy;
		// Convert the phase into radians (by splitting 2*PI into 
		// NUM_FRAMES segments).
		//--------------------------------------------------------
		p1 = 2 * Math.PI * (double)phase / (double)NUM_FRAMES;
		// dispx defines how far across the image has to be copied 
		// from the original LHS frame.
		//--------------------------------------------------------
		dispx = width;
		// Now process each horizontal line of pixels. Copy across
		// from original frame on the left-had-side and displacing 
		// up or down WRT the dispy sine function.
		//--------------------------------------------------------
		for (int i = 0; i < height; i++) 
		{
			// dispy defines the vertical sine displacement. It 
			// attenuates higher up the image, for perspective.
			//--------------------------------------------------------
			dispy = (int)((height/14) * ((double) i + 28.0)
				* Math.sin ((double)((height/14)*(height - i))
				/(double)(i + 1)
				+ p1)
				/ (double) height);
			// If no line dithers here then copy original.
			//--------------------------------------------------------
			if (i < -dispy)
				g.copyArea (0, i, width, 1,
					dispx, 0);
			else
			// Else copy dithered line.
			//--------------------------------------------------------
				g.copyArea (0, i + dispy,
					width, 1, dispx, -dispy);
		}
		g.copyArea(dispx,0,width,height,-dispx,0);

	}
    
    
    int myPhase = 0;
    
    public void paint2(Graphics g) {
        //g.setColor(Color.black);
        //g.fillRect(0,0, width, height);

        int x = 0; 
        int y = bgIndex;
        boolean half =  false;
        while (y < height)
        {
        	x=0;
        	//if (half) x = 0;
        	//else x = background.getWidth(null)/-2;
        	//half = !half;
            while (x < width)
            {
            	//System.out.println(x+","+y);
                g.drawImage(background,x,y,null);
	            x+=background.getWidth(null);    
            }
            y+=background.getHeight(null);
        }
        bgIndex+=1;
        if (bgIndex >= 0) bgIndex = 1-bgHeight;

  		//myPhase = (myPhase+1) % NUM_FRAMES;
		//makeWaves(g,myPhase);

		g.setColor(Color.magenta);
		//g.drawString("Sleep time ", 5,20);
        //g.drawString(String.valueOf(sleepTime),70,20);
        //g.drawString("drift Value ", 120,20);
        //g.drawString(String.valueOf(ship.drift),200,20);

        g.drawString(String.valueOf(score),400,20);



        for (int i=0;i<totalLasers;i++){
            if (!laser[i].active()){continue;}
            else{laser[i].paint(g);}
        }

        for (int j=0;j<totalEnemies;j++){
            if (!enemy[j].active()){continue;}
            else{
                if (enemy[j].charged())
                {
                    photon[photonIndex].launch(enemy[j].x+enemy[j].width/2,
                        enemy[j].y+enemy[j].height/2,ship.x+ship.width/2,
                        ship.y+ship.height/2);
                    if (photonIndex==totalPhotons-1) photonIndex=0;
                    else photonIndex++;
                }    
                enemy[j].paint(g);
            }
        }

        for (int i=0;i<totalPhotons;i++){
            if (!photon[i].active()){continue;}
            else{photon[i].paint(g);}
        }

        ship.paint(g);
        


    }

    public boolean keyDown(Event e, int key)
    {
        //if (key == 32){
         	//rechargeTime = 0;       	
         //   fire = true;
        //}
        //if (key == 32) {
        //	if (ship.canFire) {
        //		ship.fired = true;
        //		ship.canFire = false;
        //	}
        //}

        if (key == Event.UP) {
            ship.drift('u',true);
        }
        else if (key == Event.DOWN) {
            ship.drift('d',true);
        }
        else if (key == Event.LEFT) {
            ship.drift('l',true);
        }
        else if (key == Event.RIGHT) {
            ship.drift('r',true);
        }
        return true;
    }

    public boolean keyUp(Event e, int key)
    {
        //if (key == 32) {
        //	rechargeTime = 0;
        //  fire = false;
        //}
        if (key == 32) {
        	ship.fired = true;
        }
        if (key == Event.UP) {
            ship.drift('u',false);
        }
        else if (key == Event.DOWN) {
            ship.drift('d',false);
        }
        else if (key == Event.LEFT) {
            ship.drift('l',false);
        }
        else if (key == Event.RIGHT) {
            ship.drift('r',false);
        }

        else if (key==Event.F1 && !alive)
        {
            intialize();
            alive = true;
        }
        /*else if (key == 'q')
        {
            ship.drift--;
        }
        else if (key == 'w')
        {
            ship.drift++;
        }
        else if (key == 'a')
        {
            sleepTime--;
        }
        else if (key == 's')
        {
            sleepTime++;
        }*/
        else {return false;}
        return true;
    }
    
    //determines if hit fix this later
    boolean hit(Enemy enemy){
        
        for (int i = 0;i<totalLasers;i++){
            if (!laser[i].active()){continue;}
            if (enemy.x < laser[i].x && enemy.x+enemy.width > laser[i].x) {
                if (laser[i].y < enemy.y+enemy.height && laser[i].y+laser[i].length>enemy.y) {
                    laser[i].hit();
                    return true;        
                }    
            }
        
        }
        return false;
    }

    /** Determines if you are dead. You are not dead until you are
      * finished exploding */
    boolean dead()
    {
        if (!ship.destroyed ) {
            for (int i = 0;i<totalPhotons;i++){
                if (!photon[i].active()){continue;}
                ship.hit(photon[i]);
            }
            return false;
        }    
        return true;
    }

}


class Photon{
    double x,y;    //x and y are startpoints
    final static int drift=7; //faster than ship
    int height=6;
    int width=6;
    double xMov; //x movement
    double yMov; //y movement
    public Rectangle mask;

    public Photon(){
        x = 0;
        y = -5;
        mask = new Rectangle((int)x,(int)y,width,height);
    }

    public void paint(Graphics g){
        x+=xMov;
        y+=yMov;
        mask.move((int)x,(int)y);
        g.setColor(Color.orange);
        g.fillOval((int)x,(int)y,width,height);
    }

    /** Launched from (x,y) to (x2,y2) */
    public void launch(int x, int y, int x2, int y2)
    {
        this.x = x;
        this.y = y;
        double xVector = x2-x;
        double yVector = y-y2;

        double angle;

        //System.out.println(xVector+","+yVector);
        //System.out.println((yVector/xVector));
        if (xVector>0) angle = Math.atan(yVector/xVector);
        else if (xVector<0) angle=Math.atan(yVector/xVector)+Math.PI;
        else {
            if (yVector>0) angle = Math.PI/2;
            else angle = (Math.PI*3)/2;
        }

        //System.out.println(angle);
        xMov = drift*Math.cos(angle);
        yMov = -drift*Math.sin(angle); //y coordinate in java is upside down
    }

    //if laser still on screen
    boolean active(){
        if (y<0 || y>Shooter.height || x<0 || x>Shooter.width){return false;}
        else {return true;}
    }

    void hit() {
        x = 0;
        y = -4;
    }

}    
//instance of a laser
//lasers only go forward
class Laser{
    int x,y;
    static final int length = 40;
    static final int increment = 40;
    //x and y are startpoints
    
    public Laser(){
        x = 0;
        y = -length;
    }

    public void paint(Graphics g){
        g.setColor(Color.red);
        g.drawLine(x,y,x,y+length);
        y-=increment;
    }

    public void setPosition(int x, int y)
    {
        this.x = x;
        this.y = y-length;
    }

    //if laser still on screen
    boolean active(){
        if (y+length<0){return false;}
        else {return true;}
    }

    void hit() {
        x = 0;
        y = -length;
    }

}

class Enemy {
    int x,y; //enemy start position upper left corner
    int height; //height of enemy
    int width; //width of enemy


    //The explosion stage: 0 means none
    //1 means stage 1
    //2 means stage 2 etc..
    int explosionStage;
    
    Image image;
    
    //amount of frames before the Enemy can fire a photon
    final static int chargeTime = 25;
    int chargeCount = chargeTime;

    public Rectangle mask;
    public Enemy(Image image, int height,int width){

        x = 0;
        y = 600;
        this.image = image;
        this.height = height;
        this.width = width;
        mask = new Rectangle(x,y,width,height);
        //System.out.println(width);
    }

    public void setPosition(int x, int y)
    {
        this.x = x;
        this.y = y;
        explosionStage = 0;
    }

    public void paint(Graphics g){
        if (explosionStage==0)
        {
            y+=2;
            mask.move(x,y);

        }
        g.drawImage(image,x,y,null);
        if (explosionStage>0)
        {
            if (explosionStage==1)
            {
                g.setColor(Color.yellow);
                g.fillOval(x+width/2-width/8,y+height/2-height/8,width/4,height/4);
            }
            else if (explosionStage==2)
            {
                g.setColor(Color.orange);
                g.fillOval(x+width/2-width/4,y+height/2-height/4,width/2,height/2);
            }
            else if (explosionStage==3)
            {
                g.setColor(Color.red);
                g.fillOval(x,y,width,height);
                y=900;
            }
            explosionStage++;

        }

        /*for(int i=0; i<length; i++)
        {
            if (laser[i] == null) {continue;}

            if (laser[i].y < y) {continue;}

            if(laser[i].x <= x)
            {
                x+=1;
                break;
            }
            else if (laser[i].x > x)
            {
                x-=1;
                break;
            }
        } */
    }

    public boolean charged()
    {
        if (explosionStage >0) return false;
        chargeCount--;
        if (chargeCount == 0)
        {
            chargeCount = chargeTime;
            return true;
        }
        return false;
    }

    public boolean active(){
        if (y>400){return false;}
        else {return true;}
    }

    void explode() {
        if (explosionStage<=0) explosionStage=1;
    }
}

class Ship
{
    int x,y; //enemy start position upper left corner
    int height; //height of Ship
    int width; //width of Ship
    Image image; //ships image
    public Rectangle mask; //nose and body of the ship
    public Rectangle maskL; //left wing
    public Rectangle maskR; //right wing
    
    public static int drift = 4;
    
    boolean fired = false;
    boolean canFire = true;
    
    boolean up = false;
    boolean down = false;
    boolean right = false;
    boolean left = false;
    int explosionStage = 0;
    boolean destroyed = false;
    
    public Ship(Image image, int height,int width){
        this.image = image;
        this.height = height;
        this.width = width;
        x = 250;
        y = 350;
        mask = new Rectangle(x+9,y+6,14,30);
        maskL = new Rectangle(x+1,y+20,7,14);
        maskR = new Rectangle(x+24,y+20,7,14);
    }

    public void paint(Graphics g){
        if (up){ 
            y-=drift;
            if (y<0) y=0;
        }

        if (down) {
            y+=drift;
            if (y > 400-height) y = 400-height;
        }

        if (left) {
            x-=drift;
            if (x<0) x = 0;
        }
        if (right) {
            x+=drift;
            if (x>500-width) x = 500-width;
        }
        
        mask.move(x+9,y+6);
        maskL.move(x+1,y+20);
        maskR.move(x+24,y+20);

        g.drawImage(image,x,y,null);
        /*g.setColor(Color.red);
        g.drawRect(mask.x,mask.y,mask.width,mask.height);
        g.drawRect(maskL.x,maskL.y,maskL.width,maskL.height);
        g.drawRect(maskR.x,maskR.y,maskR.width,maskR.height);
        */
        
        if (explosionStage>0)
        {
            if (explosionStage==1)
            {
            	Shooter.scream.play();
                g.setColor(Color.yellow);
                g.fillOval(x+width/2-width/8,y+height/2-height/8,width/4,height/4);
            }
            else if (explosionStage==2)
            {
                g.setColor(Color.orange);
                g.fillOval(x+width/2-width/4,y+height/2-height/4,width/2,height/2);
            }
            else if (explosionStage==3)
            {
                g.setColor(Color.red);
                g.fillOval(x,y,width,height);
                destroyed = true;
            }
            explosionStage++;

        }

    }
    
    public void hit (Photon photon)
    {
        if (photon.mask.intersects(mask) || photon.mask.intersects(maskL)
            || photon.mask.intersects(maskR))
        {
            photon.hit();
            explode();
        }
    }

    public void drift(char direction, boolean move)
    {
        switch(direction)
        {
            case 'u': 
                up = move;
                break;

            case 'd': 
                down = move;
                break;

            case 'r': 
                right = move;
                break;

            case 'l': 
                left= move;
                break;
        }
    }
    
    private void explode() {
        if (explosionStage<=0) explosionStage=1;
    }
            
}