import java.awt.Graphics;
import java.awt.Image;
import java.awt.Dimension;
import java.awt.image.MemoryImageSource;
import java.awt.event.MouseEvent;
import java.awt.event.KeyEvent;
import java.awt.AWTEvent;
import java.applet.Applet;

public class Shooter extends Applet implements Runnable {
	private Image image = null;
	private MemoryImageSource source = null;
	private volatile Thread thread = null;
	private volatile int state = 0;
        private boolean up, down, left, right, fire;
        private int score = 0;
        
        private static int[][] ship = {
            { 0x00000000, 0x00000000, 0xFF666666, 0x00000000, 0x00000000 },
            { 0x00000000, 0x00000000, 0xFF999999, 0x00000000, 0x00000000 },
            { 0x00000000, 0xFF666666, 0xFFCCCCCC, 0xFF666666, 0x00000000 },
            { 0x00000000, 0xFF999999, 0xFF6666FF, 0xFF999999, 0x00000000 },
            { 0x00000000, 0xFFCCCCCC, 0xFF9999FF, 0xFFCCCCCC, 0x00000000 },
            { 0xFFCCCCCC, 0xFFCCCCCC, 0xFFFFFFFF, 0xFFCCCCCC, 0xFFCCCCCC },
            { 0xFFCCCCCC, 0xFFCCCCCC, 0xFFCCCCCC, 0xFFCCCCCC, 0xFFCCCCCC }
        };

        private static int[][] bullet = {
            { 0xFFFFFF33 },
            { 0xFFFFFF00 },
            { 0xFFCCCC00 }
        };

        private static int[][] alien = {
            { 0x00000000, 0xFF009900, 0xFF33FF33, 0xFF009900, 0x00000000 },
            { 0xFF009900, 0xFF33FF33, 0xFF33FF33, 0xFF33FF33, 0xFF009900 },
            { 0xFF33FF33, 0x00000000, 0xFFFF3333, 0x00000000, 0xFF33FF33 },
            { 0xFF33FF33, 0x00000000, 0xFF33FF33, 0x00000000, 0xFF33FF33 },
            { 0xFF33FF33, 0xFF009900, 0x00000000, 0xFF009900, 0xFF33FF33 },
            { 0x00000000, 0xFF33FF33, 0x00000000, 0xFF33FF33, 0x00000000 }
        };
        
        private static int[][] explosion = {
            { 0x00000000, 0xFFFF3333, 0xFFFF3333, 0xFFFF3333, 0x00000000 },
            { 0xFFFF3333, 0xFFFF3333, 0xFFFF3333, 0xFFFF3333, 0xFFFF3333 },
            { 0xFFFF3333, 0x00000000, 0xFFFF3333, 0x00000000, 0xFFFF3333 },
            { 0xFFFF3333, 0x00000000, 0xFFFF3333, 0x00000000, 0xFFFF3333 },
            { 0xFFFF3333, 0xFFFF3333, 0x00000000, 0xFFFF3333, 0xFFFF3333 },
            { 0x00000000, 0xFFFF3333, 0x00000000, 0xFFFF3333, 0x00000000 }
        };
        
	public void start() {
		(thread = new Thread( this )).start();
	}

	protected void processMouseEvent( MouseEvent me ) {
		if ( me.getID() == MouseEvent.MOUSE_RELEASED ) {
			synchronized( this ) {
				requestFocus();
			}
		}
	}
        
        protected synchronized void processKeyEvent( KeyEvent ke ) {
            int code = ke.getKeyCode();
            boolean pressed = (ke.getID() == KeyEvent.KEY_PRESSED);
            if ( ke.getID() == KeyEvent.KEY_PRESSED || ke.getID() == KeyEvent.KEY_RELEASED ) {
                switch( code ) {
                    case KeyEvent.VK_UP:    up    = pressed; break;
                    case KeyEvent.VK_DOWN:  down  = pressed; break;
                    case KeyEvent.VK_LEFT:  left  = pressed; break;
                    case KeyEvent.VK_RIGHT: right = pressed; break;
                    case KeyEvent.VK_SPACE: fire  = pressed; break;
                    case KeyEvent.VK_ESCAPE:
                        state = (!pressed)? 0 : state; break;
                }
            }
        }
	
	public void run() {
		enableEvents( AWTEvent.MOUSE_EVENT_MASK );
                enableEvents( AWTEvent.KEY_EVENT_MASK );
		Dimension size = getSize();
		int width = size.width;
		int height = size.height;
		int[] pixels = new int[ width*height ];
		
                int x = 0, y = 0; // ship position
                int[][] aliens = new int[ 20 ][ 3 ];
                int[][] bullets = new int[ 8 ][ 3 ];
                
		synchronized( this ) {
			source = new MemoryImageSource( width, height, pixels, 0, width );
			source.setFullBufferUpdates( true );
			source.setAnimated( true );
			image = createImage( source );
		}
		
                //Thread.currentThread().setPriority( Thread.MIN_PRIORITY );
                
		while( thread == Thread.currentThread() ) {
		
			
                        if ( state == 0 ) {
                                state = 1;
                                x = width/2;
                                y = height - (ship.length+2);
                                
                                for ( int i = 0; i < aliens.length; i++ ) {
                                    int[] a = aliens[ i ];
                                    a[ 0 ] = (int)(width*Math.random());
                                    a[ 1 ] = (int)((height-100)*Math.random());
                                    a[ 2 ] = 1; // alive
                                }
                                
                                for ( int i = 0; i < bullets.length; i++ ) {
                                    int[] b = bullets[ i ];
                                    b[ 2 ] = 0; // inactive
                                }
                                
                                score = 0;
                                
                                up = down = left = right = fire = false;
                        }
                        else if ( state == 1 ) {
                            if ( up ) y-=2;
                            if ( down ) y+=2;
                            if ( left ) x-=2;
                            if ( right ) x+=2;
                            
                            x = x < 0? 0 : (x >width? width: x);
                            y = y < 0? 0 : (y >height? height: y);
                            
                            if ( fire ) {
                                fire = false;
                                for ( int i = 0; i < bullets.length; i++ ) {
                                    int[] b = bullets[ i ];
                                    if ( b[ 2 ] == 0 ) {
                                        b[ 2 ] = 1;
                                        b[ 0 ] = x+ship[ 0 ].length/2;
                                        b[ 1 ] = y;
                                        break;
                                    }
                                }
                            }
                        }
                        
                        for ( int i = 0; i < pixels.length; i++ ) {
                                pixels[ i ] = 0xFF000000;
                        }
                        
                        for ( int i = 0; i < aliens.length; i++ ) {
                            int[] a = aliens[ i ];
                            if ( a[ 2 ] == 1 ) {
                
                                put( width, height, pixels, alien, a[ 0 ], a[ 1 ] );

                                if ( state == 1 ) {
                                    if ( colliding( x, y, ship, a[ 0 ], a[ 1 ], alien ) ) {
                                        state = 2; // print score
                                    }
                                    else {
                                        for ( int j = 0; j < bullets.length; j++ ) {
                                            int[] b = bullets[ j ];
                                            if ( b[ 2 ] > 0 ) {
                                                if ( colliding( b[ 0 ], b[ 1 ], bullet, a[ 0 ], a[ 1 ], alien ) ) {
                                                    a[ 2 ] = 2; // killed it
                                                    b[ 2 ] = 0;
                                                    score++;
                                                }
                                            }
                                        }
                                        a[ 1 ] += 4;
                                        if ( Math.abs( a[ 1 ] - y ) > 30 )
                                            a[ 0 ] += (int)(5*(0.5-Math.random()));
                                        else {
                                            if ( a[ 0 ] < x ) a[ 0 ]+=2;
                                            if ( a[ 0 ] > x ) a[ 0 ]-=2;
                                        }
                                        if ( a[ 1 ] > height ) {
                                            a[ 2 ] = 0;
                                        }

                                    }

                                }
                            }
                            else if ( a[ 2 ] == 2 ) {
                                put( width, height, pixels, explosion, a[ 0 ], a[ 1 ] );
                                a[ 2 ] = 0;
                            }
                            else if ( state == 1 ) {
                                a[ 1 ] = -alien.length; // just above screen
                                a[ 0 ] = (int)(width*Math.random());
                                a[ 2 ] = 1;
                            }
                        }
                        
                        
                        for ( int i = 0; i < bullets.length; i++ ) {
                            int[] b = bullets[ i ];
                            if ( b[ 2 ] > 0 ) {
                                put( width, height, pixels, bullet, b[ 0 ], b[ 1 ] );
                                if ( state == 1 ) {
                                    b[ 1 ] -= 4;
                                    if ( b[ 1 ] < 0 )
                                        b[ 2 ] = 0;
                                }
                            }
                        }
                        
                        put( width, height, pixels, ship, x, y );
                        
                        synchronized( this ) {
				source.newPixels();
				repaint();
			}
			
			try {
				Thread.sleep( 40 );
			}
			catch( Exception e ) {
			}
		}
		
	}
        
        private void put( int width, int height, int[] pixels, int[][] sprite, int x, int y ) {
            for ( int j = 0; j < sprite.length; j++ ) {
                int[] r = sprite[ j ];
                for ( int i = 0; i < r.length; i++ ) {
                    int pixel = r[ i ];
                    if ( pixel != 0 ) {
                        int px = x+i, py = y+j;
                        if ( px >= 0 && px < width && py >= 0 && py < height ) {
                            pixels[ py*width + px ] = pixel;
                        }
                    }
                }
            } 
        }
        
        private boolean colliding( int x1, int y1, int[][] sprite1, int x2, int y2, int[][] sprite2 ) {
            int width1 = sprite1[ 0 ].length, height1 = sprite1.length;
            int width2 = sprite2[ 0 ].length, height2 = sprite2.length;
            
            if ( (x1 <= (x2+width2) && x2 <= x1) || (x2 <= (x1+width1) && x1 <= x2) ) {
                if ( (y1 <= (y2+height2) && y2 <= y1) || (y2 <= (y1+height1) && y1 <= y2) ) {
                    // TODO pixel perfect collisions
                    return true;
                }
            }
            
            return false;
        }
	
	public void stop() {
		thread = null;
	}
	
	public void update( Graphics g ) {
		paint( g );
	}
	
	public synchronized void paint( Graphics g ) {
		if ( image != null ) {
                    Dimension size = getSize();
                    //g.drawImage( image, 0, 0, size.width, size.height, this );
                    g.drawImage( image, 0, 0, this );
                    if ( state == 2 ) {
                        g.setColor( java.awt.Color.white );
                        g.drawString( "Press Escape", 10, 20 );
                        g.drawString( "You Scored: " + score, 10, size.height/2 );
                    }
		}
	}
}