import java.awt.*;
import java.awt.event.KeyEvent;
import java.net.URL;
import java.io.*;

/** CatBurglar.  A simple retro game. **/

public class CatBurglar extends RetroApplet {
	private int state = INTRO;
	private final static int INTRO = 0;
	private final static int GAME  = 1;
	private final static int OUTRO = 2;

	private final int WIDTH  = 256;
	private final int HEIGHT = 256;

	private int score = 0;
	private int lives = 9;
	private int level = 1;

	private int counter = 0;
	private int invincible = 0;
	
	private SpriteImages images = null;

	private Level theLevel = new Level();
	private Sprite catSprite = null;

	private Image background = null;

	private String[] levelFiles = { "test.lev" };

	/** Do some initialisation.  **/

	public void init() {
		super.init();
		images = new SpriteImages( this );
		catSprite = new Sprite( images.getCatImage() );
		// create another back buffer that the static (unchanging)
		// images will be drawn onto, so that drawing them is only
		// one operation instead of several
		background = createImage( Level.GRID_WIDTH*Level.ELEMENT_SIZE,
							    Level.GRID_HEIGHT*Level.ELEMENT_SIZE );
		
		// load the list of levels
		try {
			URL url = new URL( getCodeBase(), "levels.txt" );
			InputStream in = url.openStream();
			BufferedReader r = new BufferedReader( new InputStreamReader( in ) );
			java.util.Vector v = new java.util.Vector();
			String str = null;
			while( (str = r.readLine()) != null ) {
				v.addElement( str );
			}
			levelFiles = new String[ v.size() ];
			v.copyInto( levelFiles );
		}
		catch( Exception e ) {
			System.err.println( e );
		}
	}


	/** Do a timestep. **/
	public void tick() {
		counter++;
		switch( state ) {
			case INTRO: intro(); break;
			case GAME:  game();  break;
			case OUTRO: outro(); break;
			default:
				System.err.println( "unknown state" );
		}
	}

	private Font bigFont   = new Font( "SansSerif", Font.BOLD,  20 );
	private Font smallFont = new Font( "SansSerif", Font.PLAIN, 12 );

	/** Draw an intro screen, and wait for user input before changing
	 *  to the game screen.
	 **/
	public void intro() {
		Graphics g = getBackBuffer();
		g.setColor( Color.black );
		g.fillRect( 0, 0, WIDTH, HEIGHT );
		g.setColor( Color.white );
		g.setFont( bigFont );
		FontMetrics metrics = g.getFontMetrics();
		String str = "Catburglar!";
		int w = metrics.stringWidth( str );
		g.drawString( str, (WIDTH-w)>>1, HEIGHT>>1 );
			
		g.setFont( smallFont );
		g.drawString( "click on applet, then press space", 5, HEIGHT-4 );

		if ( counter > 25 ) {
			// wait for 25 frames before drawing
			g.setColor( Color.green );
			g.fillOval( (WIDTH>>1)-40, (HEIGHT>>1)+14, 30, 22 );		
			g.fillOval( (WIDTH>>1)+10, (HEIGHT>>1)+14, 30, 22 );		
			g.setColor( Color.black );
			g.fillOval( (WIDTH>>1)-27, (HEIGHT>>1)+14, 6, 22 );		
			g.fillOval( (WIDTH>>1)+22, (HEIGHT>>1)+14, 6, 22 );		
			
			int c = counter - 25;
			c = c > 90 ? 90 : c;
			double d = (1.0 + Math.cos( c*0.1f ));
			int h = (int)(10*d);
			g.fillRect( 0, (HEIGHT>>1)+10, WIDTH, h );
			h = (int)(7*d);
			g.fillRect( 0, (HEIGHT>>1)+40-h, WIDTH, h );
			if ( counter > 150 ) counter = 55;
		}
				
		// copy to screen
		flipBuffer();
		if ( counter >= 2 5 && fire() ) { // wait 1 second before allowing
			lives = 9;				   // user to go into game
			score = 0;
			level = 0;
			state = GAME;
			counter = 0;
			invincible = 0;
			nextLevel();
		}
	}

	/** Run the game for one timestep and draw it. **/

	public void game() {
		drawGame();
		if ( lives < 0 ) {  // if all lives gone then go to outro
			state = OUTRO;
			counter = 0;
		}
		else if ( counter < 50 ) { // wait a brief while to show an
			if ( counter > 25 )	// intro screen
				if ( fire() )
					counter = 50;  // fire pressed so go into main game
				else	
					counter = 26;
		}
		else {
			theLevel.tick();			   	// update the level
			catSprite.tick( theLevel );   	// update the main sprite
			if ( theLevel.levelDone( catSprite ) ) { // check if finished
				nextLevel();
				counter = 0;
				invincible = 0; 
				return;
			}

			// see if cat is on a jewel
			int score = theLevel.checkJewelCollisions( catSprite );
			this.score += score;			
			
			// if the cat hits a dog make it invincible for a while (after
			// losing a life) so it can get away
			invincible--;
			if ( invincible < 0 && theLevel.checkDogCollisions( catSprite ) ) {
				lives--;
				invincible = 50;
			}

			// work out how to move the cat next
			// all the logic is setup to deal
			// with what happens when several keys are pressed
			boolean u = (up() && !down());
			boolean d = (!up() && down());
			boolean v = u || d;

			boolean l = (left() && !right());
			boolean r = (!left() && right());			
			boolean h =  l || r ;
			int k = lastKeyPressed();


			if ( !v || (k != UP_KEY && k != DOWN_KEY) ) {
				if ( l ) 	 catSprite.goLeft();	
				else if ( r ) catSprite.goRight();
			}
			if ( !h || (k != LEFT_KEY && k != RIGHT_KEY) ) {
				if ( u ) 	 catSprite.goUp();
				else if ( d ) catSprite.goDown();
			}
		}
	}

	/** Draw the game. **/

	private void drawGame() {
		Graphics g = getBackBuffer();
		
		g.setColor( Color.black );
		g.fillRect( 0, 0, WIDTH, HEIGHT );
		g.setColor( Color.white );
		g.setFont( smallFont );
		g.drawString( "lives: " + lives, 5, HEIGHT-4 );
		g.drawString( "level: " + level, (WIDTH/3) + 5, HEIGHT-4 );
		g.drawString( "score: " + score, ((WIDTH<<1)/3) + 5, HEIGHT-4 );

		if ( counter < 50 ) {
			// if we're still on the level intro screen
			g.setFont( bigFont );
			FontMetrics metrics = g.getFontMetrics();
			String str = "Next Level";
			int w = metrics.stringWidth( str );
			g.drawString( str, (WIDTH-w)>>1, HEIGHT>>1 );
			g.setFont( smallFont );
			g.drawString( "press space", (WIDTH-w)>>1, (HEIGHT>>1) + metrics.getHeight() );
		}
		else {
			// draw the static images (walls, floor etc)
			g.drawImage( background, 0, 0, this );
			// paint the rest of the level (dogs, jewels etc)
			theLevel.paint( g, this );
	
			// if the cat is invincible only draw it once in
			// a while, so that it flashes
			if ( invincible < 0 || invincible % 4 == 0 )
				catSprite.paint( g, this );
			//g.drawImage( images.getDogImage(), 50, 50, this );
		}

		// copy to screen
		flipBuffer();
	}

	private void loadLevel( String level ) {
		try {
			// load the level from the file specified
			URL url = new URL( getCodeBase(), level );
			InputStream in = url.openStream();
			Reader r = new BufferedReader( new InputStreamReader( in ) );
			theLevel.load( r, catSprite );
		}
		catch( Exception e ) {
			System.err.println( e );
		}
		// paint the static (unmoving) images to an image
		// so they can be drawn in one operation
		theLevel.paintStatic( background.getGraphics(), this );
	}

	private void nextLevel() {
		// load the next level from the list of the levels
		catSprite.reset();
		loadLevel( levelFiles[ level % levelFiles.length ] );
		level++;
	}
	
	/** Show the outro (or gameover) screen. **/

	public void outro() {
		Graphics g = getBackBuffer();
		g.setColor( Color.black );
		g.fillRect( 0, 0, WIDTH, HEIGHT );
		g.setColor( Color.white );
		g.setFont( bigFont );
		FontMetrics metrics = g.getFontMetrics();
		String str = "Game Over!";
		int w = metrics.stringWidth( str );
		g.drawString( str, (WIDTH-w)>>1, HEIGHT>>1 );
		g.setFont( smallFont );
		g.drawString( "level: " + level, (WIDTH/3) + 5, HEIGHT-4 );
		g.drawString( "score: " + score, ((WIDTH<<1)/3) + 5, HEIGHT-4 );
		
		flipBuffer();
		if ( (counter > 12 && fire()) ) { // wait for a bit before moving
			state = INTRO;			   // back to intro
			counter = 0;
		}
	}

}