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;
            }
        }
    }
}
