import java.awt.*;
import java.awt.event.*;

public class Wilan extends AnimatedSprite
{
  /*
   * CONSTANTS
   */

  //
  // Wilan's states
  //
  private static final int READY_WITH_BOULDER   = 0;
  private static final int WALKING_WITH_BOULDER = 1;
  private static final int JUMPING_WITH_BOULDER = 2;
  private static final int DYING_WITH_BOULDER   = 3;
  private static final int READY_NO_BOULDER     = 4;
  private static final int WALKING_NO_BOULDER   = 5;
  private static final int JUMPING_NO_BOULDER   = 6;
  private static final int DYING_NO_BOULDER     = 7;
  private static final int DEAD                 = 8;

  //
  // Indices of Wilan's tracks
  //
  private static final int RRB = 0;
  private static final int RLB = 1;
  private static final int WRB = 2;
  private static final int WLB = 3;
  private static final int JRB = 4;
  private static final int JLB = 5;
  private static final int DRB = 6;
  private static final int DLB = 7;
  private static final int RRN = 8;
  private static final int RLN = 9;
  private static final int WRN = 10;
  private static final int WLN = 11;
  private static final int JRN = 12;
  private static final int JLN = 13;
  private static final int DRN = 14;
  private static final int DLN = 15;
 
  //
  // Wilan's tracks
  //
  private static final int[] RRB_TRACK = {0};           
  private static final int[] RLB_TRACK = {4};
  private static final int[] WRB_TRACK = {0, 1, 2, 3};
  private static final int[] WLB_TRACK = {4, 5, 6, 7};
  private static final int[] JRB_TRACK = {0};
  private static final int[] JLB_TRACK = {4};
  private static final int[] DRB_TRACK = {0, 4};
  private static final int[] DLB_TRACK = {4, 0};
  private static final int[] RRN_TRACK = {8};
  private static final int[] RLN_TRACK = {12};
  private static final int[] WRN_TRACK = {8, 9, 10, 11};
  private static final int[] WLN_TRACK = {12, 13, 14, 15};
  private static final int[] JRN_TRACK = {8};
  private static final int[] JLN_TRACK = {12};
  private static final int[] DRN_TRACK = {8, 12};
  private static final int[] DLN_TRACK = {12, 8};

  //
  // Wilan's defaults
  //
  private static final int DEFAULT_DIRECTION = Ps1Defs.RIGHT;
  private static final int DEFAULT_STATE     = READY_WITH_BOULDER;
  private static final int DEFAULT_TRACK     = RRB;
  private static final int DEFAULT_VELOCITY  = 0;

  /*
   * Class constructor.  Creates Wilan from a 16-image strip
   */
  public Wilan(Image wilanImage)
  {
    super(wilanImage, 16);
    addTrack(RRB_TRACK); // TRACK 0
    addTrack(RLB_TRACK); // TRACK 1
    addTrack(WRB_TRACK); // TRACK 2
    addTrack(WLB_TRACK); // TRACK 3
    addTrack(JRB_TRACK); // TRACK 4
    addTrack(JLB_TRACK); // TRACK 5
    addTrack(DRB_TRACK); // TRACK 6
    addTrack(DLB_TRACK); // TRACK 7
    addTrack(RRN_TRACK); // TRACK 8
    addTrack(RLN_TRACK); // TRACK 9
    addTrack(WRN_TRACK); // TRACK 10
    addTrack(WLN_TRACK); // TRACK 11
    addTrack(JRN_TRACK); // TRACK 12    
    addTrack(JLN_TRACK); // TRACK 13
    addTrack(DRN_TRACK); // TRACK 14
    addTrack(DLN_TRACK); // TRACK 15

    //
    // Initialize Wilan with default information
    //
    setState(DEFAULT_STATE);
    setTrack(DEFAULT_TRACK); 
    setDirection(DEFAULT_DIRECTION);
    setVelocity(DEFAULT_VELOCITY);
  }

  /*
   * Draws Wilan to the specified Raster object
   */
  public void draw(Raster raster)
  {
    updateState(raster);
    super.draw(raster);
  }

  /*
   * Updates Wilan's member variables based on 
   * which state he is in
   */
  public void updateState(Raster raster)
  {
    switch (getState())
      {
      case READY_WITH_BOULDER: 
      case READY_NO_BOULDER:
	break;

      case WALKING_WITH_BOULDER:
	testDumpedBoulder(raster);
      case WALKING_NO_BOULDER:
	updateWalk(raster);
	break;
	
      case JUMPING_WITH_BOULDER:
      case JUMPING_NO_BOULDER:
	updateJump(raster);
	break;

      case DYING_WITH_BOULDER:
      case DYING_NO_BOULDER:
	updateDeath(raster);
	break;

      case DEAD:
	updateDead(raster);
	break;       
      }
  }
  
  /*
   * Updates Wilan while he is dead.  Not used.
   */
  public void updateDead(Raster r) {}

  /*
   * Updates the variables associated with Wilan's
   * death sequence.
   */
  public void updateDeath(Raster r) 
  {
    switch (getDirection())
      {
      case Ps1Defs.DOWN: setY(getY() + getVelocity());
	if (getY() > r.getHeight())
	  dead();
	break;
      case Ps1Defs.UP: setY(getY() - getVelocity());
	if (getY() + getFrameHeight() < 0)
	  dead();
	break;
      }
  }

  /*
   * Updates the variables associated with Wilan's
   * jumping sequence.
   */
  public void updateJump(Raster r) 
  {
    setY(getY() - m_stateData1);
    if (m_stateData1 > 0)
      {
	m_stateData1 >>= 1;
      }
    else if (m_stateData1 == 0)
      {
	m_stateData1 = -1;
      }
    else
      {
	m_stateData1 *= 2;
      }
    updateWalk(r);
    if (m_stateData1 < -64)
      {
	setState(READY_NO_BOULDER);
	walk(getDirection());
      }
  }

  /*
   * Updates the variables associated with Wilan's
   * walking sequence.
   */
  public void updateWalk(Raster raster)
  {
    int rasterWidth = raster.getWidth();
    int pf_width;
    int pf_x;
    int scrn_ctr;
    
    pf_width = getPlayfield().getWidth();
    pf_x = getPlayfield().getX();
    scrn_ctr = rasterWidth >> 1;


    if (pf_x <= 0) 
      {
	switch (getDirection())
	  {
	  case Ps1Defs.RIGHT: setX(getX() + getVelocity()); break;
	  case Ps1Defs.LEFT: setX(getX() - getVelocity()); break;
	  }
	if (getX() > scrn_ctr)
	  {
	    getPlayfield().scroll(getX() - scrn_ctr, Ps1Defs.RIGHT);
	  }
      }
    else if (pf_x + rasterWidth >= pf_width)
      {
	switch (getDirection())
	  {
	  case Ps1Defs.RIGHT: setX(getX() + getVelocity()); break;
	  case Ps1Defs.LEFT: setX(getX() - getVelocity()); break;
	  }
	if (getX() < scrn_ctr)
	  {
	    getPlayfield().scroll(scrn_ctr - getX(), Ps1Defs.LEFT);
	  }
      }
    else
      {
	getPlayfield().scroll(getVelocity(), getDirection());
      }
  }
  

  /*
   * Returns true if Wilan has deposited his boulder on the
   * right-hand side of the screen.  Removes the boulder
   * from his graphic, and sets the state to reflect this
   */
  public void testDumpedBoulder(Raster r)
  {
    int rasterWidth = r.getWidth();
    if (getX() + getFrameWidth() > rasterWidth)
      {
	setState(WALKING_NO_BOULDER);
	setTrack(WRN);
      }
  }



  /********************************************************
   *                                                      *
   *                 ACTION METHODS                       *
   *                                                      *
   * These methods are used to initiate some action       *
   * that Wilan takes.  Wilan can perfrom the following   *
   * actions:                                             *
   *                                                      *
   * dead()              - ends the game                  *
   * die()               - starts Wilan's dying sequence  *
   * jump()              - starts Wilan's jumping         *
   *                       sequence                       *
   * rest()              - puts Wilan in the READY        *
   *                       position                       *
   * walk(int direction) - start Wilan walking in the     *
   *                       specified direction            *
   *                                                      *
   *                                                      *
   *******************************************************/
  
  /*
   * Wilan is dead, ends the game
   */
  public void dead()
  {
    getPlayfield().gameOver();
  }

  /*
   * Starts Wilan's dying animation, and prevents
   * Wilan from performing other activities.
   */
  public void die()
  {
    switch (getState())
      {
      case READY_NO_BOULDER:
      case WALKING_NO_BOULDER:
      case JUMPING_NO_BOULDER:
	setState(DYING_NO_BOULDER);
	setDirection(Ps1Defs.DOWN);
	setVelocity(8);
	switch(getDirection())
	  {
	  case Ps1Defs.RIGHT: setTrack(DRN); break;
	  case Ps1Defs.LEFT: setTrack(DLN); break;
	  }
	break;
      case READY_WITH_BOULDER:
      case WALKING_WITH_BOULDER:
      case JUMPING_WITH_BOULDER:
	setState(DYING_WITH_BOULDER);
	setDirection(Ps1Defs.DOWN);
	setVelocity(8);
	switch(getDirection())
	  {
	  case Ps1Defs.RIGHT: setTrack(DRB); break;
	  case Ps1Defs.LEFT: setTrack(DLB); break;
	  }
	break;
      }
  }

  /*
   * Makes Wilan jump.  Afterwards, he will start walking
   * in the direction he was facing
   */
  public void jump()
  {
    switch (getState())
      {
      case WALKING_NO_BOULDER:
      case READY_NO_BOULDER:
	setState(JUMPING_NO_BOULDER);
	m_stateData1 = 64;
	switch (getDirection())
	  {
	  case Ps1Defs.RIGHT: setTrack(JRN); break;
	  case Ps1Defs.LEFT: setTrack(JLN); break;
	  }
      }
  }

  /*
   * Puts Wilan in the READY position
   */
  public void rest()
  {
    switch (getState())
      {
      case WALKING_NO_BOULDER:
	setState(READY_NO_BOULDER);
	switch (getDirection())
	  {
	  case Ps1Defs.RIGHT: setTrack(RRN); break;
	  case Ps1Defs.LEFT: setTrack(RLN); break;
	  }
	break;
      case WALKING_WITH_BOULDER:
	setState(READY_WITH_BOULDER);
	switch (getDirection())
	  {
	  case Ps1Defs.RIGHT: setTrack(RRB); break;
	  case Ps1Defs.LEFT: setTrack(RLB); break;
	  }
	break;
      }
  }

  /*
   * Starts Wilan walking in the specified direction.
   * This direction must be either Ps1Defs.RIGHT, or
   * Ps1Defs.LEFT.
   */
  public void walk(int direction)
  {
    switch (getState())
      {
      case JUMPING_WITH_BOULDER:
      case JUMPING_NO_BOULDER:
      case DYING_WITH_BOULDER:
      case DYING_NO_BOULDER:
      case DEAD: 
	break;
      case WALKING_WITH_BOULDER:
	if (direction == getDirection()) break;
      case READY_WITH_BOULDER:
	setState(WALKING_WITH_BOULDER);
	setDirection(direction);
	setVelocity(20);
	switch(direction)
	  {
	  case Ps1Defs.LEFT: setTrack(WLB); break;
	  case Ps1Defs.RIGHT: setTrack(WRB); break;
	  }
	break;
      case WALKING_NO_BOULDER:
	if (direction == getDirection()) break;
      case READY_NO_BOULDER:
	setState(WALKING_NO_BOULDER);
	setDirection(direction);
	setVelocity(20);
	switch(direction)
	  {
	  case Ps1Defs.LEFT: setTrack(WLN); break;
	  case Ps1Defs.RIGHT: setTrack(WRN); break;
	  }
      }
  
  }



  /*********************************************************
   *                                                       *
   *                   EVENT HANDLERS                      *
   *                                                       *
   ********************************************************/

  //
  // Key Events
  //
  public void keyPressed(KeyEvent e)
  {
    if (isControllable())
      {
	switch(e.getKeyCode())
	  {
	  case KeyEvent.VK_LEFT: walk(Ps1Defs.LEFT); break;
	  case KeyEvent.VK_RIGHT: walk(Ps1Defs.RIGHT); break;
	  case KeyEvent.VK_DOWN: rest(); break;
	  case KeyEvent.VK_SPACE: jump(); break;
	  }
      }
  }
  public void keyReleased(KeyEvent e) {}
  public void keyTyped(KeyEvent e)    {}


  //
  // Mouse Events
  //
  public void mouseClicked(MouseEvent e) 
  {
    int x, y;
    x = e.getX();
    y = e.getY();
    
    if (hitTest(x, y))
      {
	die();
      }
  }
  public void mousePressed(MouseEvent e)  {}
  public void mouseReleased(MouseEvent e) {}
  public void mouseEntered(MouseEvent e)  {}
  public void mouseExited(MouseEvent e)   {}

}  



