import java.applet.Applet;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;

import java.awt.image.ImageObserver;
import java.awt.image.MemoryImageSource;
import java.awt.image.PixelGrabber;

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import java.net.URL;
import java.net.MalformedURLException;

public class DemoApplet
extends      Applet
implements   MouseMotionListener,
			 MouseListener,
			 Runnable
{
	public  Playfield      playfield;
	private Image          frame_image;
	private AnimatedSprite body_animation;
	private Vulture        vultures[];
	private Raster         main_raster;
	private Sprite         cloud_sprite;
	private Thread         update_thread;
	private Point          old_mouse_position;
	private boolean        first_time;
	private long           tics;
	
    /**
     *  This constructor creates an Raster initialized
     *  with the contents of an image.
     */
    public DemoApplet()
    {
    }


    /**
     *  init
     */
    public void
	init()
    {
		Image background  = null;
		Image upper_torso = null;
		Image vulture     = null;
		Image clouds      = null;
		
		try {
			background  = 
				getImage( getCodeBase(), "../images/background.gif" );
			upper_torso = 
				getImage( getCodeBase(), "../images/lower_torso.gif" );
			vulture     = 
				getImage( getCodeBase(), "../images/vulture.gif" );
			clouds      = 
				getImage( getCodeBase(), "../images/clouds.gif" );
			System.out.println( "Got the image..." );
		} catch ( Exception e ) {
			System.out.println( "Got exception in loading images..." );
			e.printStackTrace( System.out );
		}
		
		System.out.println( "Initialized the play_frame" );
		
		main_raster = new Raster( getSize().width, getSize().height );
		playfield   = new Playfield( background, this );
		
		Rectangle     clips[];
		Point         offsets[];
		AnimationRule torso_animation_rule   = new AnimationRule();
		long          delay                = 150l;
		clips      = new Rectangle[2];
		offsets    = new Point[2];
		clips[0]   = new Rectangle ( 0, 29, 23, 27 );
		clips[1]   = new Rectangle ( 0, 0,  24, 29 );
		offsets[0] = new Point( 30, 45 );
		offsets[1] = new Point( 33, 24 );
		torso_animation_rule.appendClips( clips, delay, offsets );
		
		clips      = new Rectangle[2];
		offsets    = new Point[2];
		clips[0]   = new Rectangle ( 28, 29, 31, 25 );
		clips[1]   = new Rectangle ( 24, 0,  24, 29 );
		offsets[0] = new Point( 30, 45 );
		offsets[1] = new Point( 36, 24  );
		torso_animation_rule.appendClips( clips, delay, offsets );
		
		clips      = new Rectangle[2];
		offsets    = new Point[2];
		clips[0]   = new Rectangle ( 60, 30, 29, 27 );
		clips[1]   = new Rectangle ( 49, 0,  24, 28 );
		offsets[0] = new Point( 30, 45 );
		offsets[1] = new Point( 33, 24  );
		torso_animation_rule.appendClips( clips, delay, offsets );
		
		clips      = new Rectangle[2];
		offsets    = new Point[2];
		clips[0]   = new Rectangle ( 90, 30, 20, 27 );
		clips[1]   = new Rectangle ( 72, 0,  17, 26 );
		offsets[0] = new Point( 30, 45 );
		offsets[1] = new Point( 36, 24  );
		torso_animation_rule.appendClips( clips, delay, offsets );
		
		clips      = new Rectangle[2];
		offsets    = new Point[2];
		clips[0]   = new Rectangle ( 112, 28, 22, 28 );
		clips[1]   = new Rectangle ( 88, 0,  24, 28 );
		offsets[0] = new Point( 27, 44 );
		offsets[1] = new Point( 31, 24  );
		torso_animation_rule.appendClips( clips, delay, offsets );
		
		clips      = new Rectangle[2];
		offsets    = new Point[2];
		clips[0]   = new Rectangle ( 140, 29, 31, 27 );
		clips[1]   = new Rectangle ( 112, 0,  24, 28 );
		offsets[0] = new Point( 25, 45 );
		offsets[1] = new Point( 33, 24  );
		torso_animation_rule.appendClips( clips, delay, offsets );

		clips      = new Rectangle[2];
		offsets    = new Point[2];
		clips[0]   = new Rectangle ( 184, 0, 27, 28 );
		clips[1]   = new Rectangle ( 137, 0,  24, 28 );
		offsets[0] = new Point( 28, 44 );
		offsets[1] = new Point( 32, 24  );
		torso_animation_rule.appendClips( clips, delay, offsets );
		
		clips      = new Rectangle[2];
		offsets    = new Point[2];
		clips[0]   = new Rectangle ( 175, 29, 17, 27 );
		clips[1]   = new Rectangle ( 164, 0,  17, 26 );
		offsets[0] = new Point( 30, 45 );
		offsets[1] = new Point( 33, 24  );
		torso_animation_rule.appendClips( clips, delay, offsets );

		body_animation = 
			new AnimatedSprite( upper_torso, torso_animation_rule );
		body_animation.x = 30;
		body_animation.y = 337;
		cloud_sprite      = new Sprite( clouds );
		playfield.addRaster( body_animation );

		vultures = new Vulture[2];
		for ( int i=0; i<vultures.length; i+=2 ) {
			vultures[i] = new Vulture( vulture, delay-i );
			playfield.addRaster( vultures[i] );
			vultures[i].x = i*40+70;
			vultures[i].y = ((i>5)?(10-i):i)*40+30;
		}

		playfield.addRaster( cloud_sprite   );

		for ( int i=1; i<vultures.length; i+=2 ) {
			vultures[i] = new Vulture( vulture, delay+i );
			playfield.addRaster( vultures[i] );
			vultures[i].x = i*40+70;
			vultures[i].y = ((i>5)?(10-i):i)*40+30;
		}
		
		
		addMouseMotionListener( this );
		addMouseListener( this );

		update_thread = new Thread( this, "UpdateThread" );
		update_thread.start();
	}
	
	
    /**
     *  destroy
     */
    public void
	destroy()
    {
	}
	
	
    /**
     *  stop
     */
    public void
	stop()
    {
	}
	
	
    /**
     *  start
     */
    public void
	start()
    {
    }


    /**
     *  Draw the sprite on the input raster.
     */
    public void
	update( Graphics g )
    {
    }


    /**
     *  Draw the sprite on the input raster.
     */
    public void
	paint( Graphics g )
    {
		if ( frame_image != null ) {
			g.drawImage( frame_image, 0, 0, this );
		} else {
			System.out.println( "Preparing the playfield" );
			playfield.draw( main_raster );
			System.out.println( "Getting the image from the playfield" );
			frame_image = main_raster.toImage( this );
			g.drawImage( frame_image, 0, 0, this );
			body_animation.startAnimation();
			playfield.startAnimation();
			
			for ( int i=0; i<vultures.length; i++ ) {
				vultures[i].startAnimation();
			}
		}
    }


    /**
     *  Draw the sprite on the input raster.
     */
    public void
	paintAll( Graphics g )
    {
		if ( frame_image != null ) {
			g.drawImage( frame_image, 0, 0, this );
		}
    }
	
	
    /**
    *
    * Handle mouse events
    *
    **/
    public void
    mouseReleased( MouseEvent e )
    {
	}
	

    
    /**
    *
    * Handle mouse events
    *
    **/
    public void
    mouseClicked( MouseEvent e ) 
    {
		Point p = e.getPoint();

		if ( cloud_sprite.isInside( playfield.toPlayfieldCoordinates( p ) ) ) {
			Graphics g = getGraphics();
			g.setColor( Color.black );
			g.drawString ( "HIT", p.x, p.y );
		} else {
			Graphics g = getGraphics();
			g.setColor( Color.black );
			g.drawString ( "MISS", p.x, p.y );
		}
    }
    

    /**
    *
    * Handle mouse events
    *
    **/
    public void
    mouseEntered( MouseEvent e ) 
    {
    }
    

    /**
    *
    * Handle mouse events
    *
    **/
    public void
    mouseExited( MouseEvent e ) 
    {
    }
    

    /**
    *
    * Handle mouse events
    *
    **/
    public void
    mousePressed( MouseEvent e ) 
    {
		first_time = true;
		old_mouse_position = e.getPoint();
    }


    /**
    *
    * Handle mouse events
    *
    **/
    public void
    mouseDragged( MouseEvent e ) 
    {
		if ( first_time ) {
			first_time = false;
			return;
		} else {
			Point current_position = e.getPoint();
			
			int dx = current_position.x - old_mouse_position.x;
			int dy = current_position.y - old_mouse_position.y;
			
			playfield.scroll( dx, dy );
			
			old_mouse_position = current_position;
		}
    }


    /**
    *
    * Handle mouse events
    *
    **/
    public void
    mouseMoved( MouseEvent e ) 
    {
    }


    /**
     *  Run
     */
    public void
	run()
    {
		while ( true ) {
			try {
				update_thread.sleep( 10l );
			} catch ( Exception e ) {
				System.out.println( "Update Thread exception in sleeping" );
				e.printStackTrace( System.out );
			}
			playfield.draw( main_raster );
			frame_image = main_raster.toImage( this );
			paint( getGraphics() );
			tics++;

			body_animation.x += 4;

			for ( int i=0; i<vultures.length; i++ ) {
				vultures[i].x += ((int)((Math.random()-.5)*7));
				vultures[i].y += ((int)((Math.random()-.5)*7));
			}

			for ( int i=0; i<vultures.length; i++ ) {
				int dx = (vultures[i].x > body_animation.x) ? -2 : 2;
				int dy = (vultures[i].y > body_animation.y) ? -2 : 2;
				
				if ( ( tics % 200 ) < 100 ) {
					vultures[i].x -= dx*( ((i%2)==0)?1:-1 );
					vultures[i].y -= dy*( ((i%2)==0)?1:-1 );
				} else {
					vultures[i].x += dx*( ((i%2)==0)?1:-1 );
					vultures[i].y += dy*( ((i%2)==0)?1:-1 );
				}
			}

			if ( ( tics % 100 ) == 0 ) {
				body_animation.x -= 400;
			}
		}
	}
}
