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

public abstract class Sprite extends Raster implements KeyListener, 
                                                       MouseListener
{
  /*
   * Member variables
   */
  private boolean m_isControllable;
  private boolean m_hasFocus;
  private int m_direction;
  private int m_velocity;
  private int m_state;
  private int m_x, m_y;
  private Playfield m_pf;

  /*
   * State variables to be used at the discretion
   * of classes that extend Sprite
   */
  protected int m_stateData1, m_stateData2, m_stateData3;


  /*
   * Class constructor.  
   */
  public Sprite(Image image)
  {
    super(image);
    m_direction      = Ps1Defs.STILL;
    m_isControllable = false;
    m_hasFocus       = false;
    m_state          = 10;
    m_x              = 10;
    m_y              = 10;
    m_pf             = null;
  }

  /* 
   * Draws this Sprite to the specified Raster object.
   * 
   * NOTE:  This draw() method is entirely un-optimized,
   *        as I use only AnimatedSprites in my program.
   *        Thus, this method is never called.  It works,
   *        but is very slow.
   */
  public void draw(Raster raster)
  {
    int clippedHeight;
    int clippedWidth;
    int clippedX;
    int clippedY;
    int height;
    int pixel;
    int rasterHeight;
    int rasterWidth;
    int width;
    int x, y;
    int pf_x, pf_y;
    
    height        = this.getHeight();
    clippedHeight = height;
    width         = this.getWidth(); 
    clippedWidth  = width;
    clippedX      = 0;
    clippedY      = 0;
    rasterHeight  = raster.getHeight();
    rasterWidth   = raster.getWidth();

    if (hasFocus())
      {
	x = getX();
	y = getY();
      }
    else
      { 
	pf_x = getPlayfield().getX();
	pf_y = getPlayfield().getY();
	x    = getX() - pf_x;
	y    = getY() - pf_y;
      }
  
    //
    // First, check if the object is entirely off the screen
    if ((x >= rasterWidth) ||
	((x + width) <= 0) ||
	(y >= rasterHeight) ||
	((y + height) <= 0))
      {
	return;
      }
    else
      {
	//
	// Horizontal clipping:
	//

	//
	// Left edge is off of the screen
	if (x < 0)
	  {
	    clippedX     = -x;
	    clippedWidth = width + x;
	  }
	//
	// Right edge is off of the screen
	else if ((x + width) >= rasterWidth)
	  {
	    clippedWidth = rasterWidth - x;
	  }
	
	//
	// Vertical clipping:
	//
	
	//
	// Top edge is off of the screen
	if (y < 0)
	  {
	    clippedY      = -y;
	    clippedHeight = height + y;
	  }
	else if ((y + height) >= rasterHeight)
	  {
	    clippedHeight = rasterHeight - y;
	  }

	for (int y_offset = 0; y_offset < clippedHeight; y_offset++)
	  {
	    for (int x_offset = 0; x_offset < clippedWidth; x_offset++)
	      {
		pixel = this.getPixel(clippedX + x_offset, 
				      clippedY + y_offset);
		if (!Ps1Defs.IsPixelTransparent(pixel))
		  {
		    raster.setPixel(pixel, 
				    x + x_offset + clippedX, 
				    y + y_offset + clippedY);
		  }
	      }
	  }
      }	
  }

  /*
   * Gets the direction of this Sprite
   */
  public final int getDirection()
  {
    return m_direction;
  }

  /*
   * Gets the Playfield in which this Sprite exists
   */
  public final Playfield getPlayfield()
  {
    return m_pf;
  }

  /*
   * Gets the state of this Sprite
   */
  public final int getState()
  {
    return m_state;
  }

  /*
   * Gets the velocity of this Sprite
   */
  public final int getVelocity()
  {
    return m_velocity;
  }

  /*
   * Gets the x-coordinate of this Sprite
   */
  public final int getX()
  {
    return m_x;
  }

  /*
   * Gets the y-coordinate of this Sprite
   */
  public final int getY()
  {
    return m_y;
  }

  /*
   * Returns true if this Sprite has focus, false otherwise
   */
  public final boolean hasFocus()
  {
    return m_hasFocus;
  }

  /*
   * Returns true if the specified coordinates fall upon
   * a non-transparent pixel in this Sprite.
   * If this Sprite does NOT have focus, the supplied 
   * x-y coordinates are considered to be relative
   * to the current position of the playfield.  If the 
   * sprite DOES have focus, the coordinates are considered
   * to be the location of the sprite on the screen.
   */
  public boolean hitTest(int x, int y)
  {
    int thisX, thisY;
    int pf_x, pf_y;
    int pixel;

    if (hasFocus())
      {
	thisX = getX();
	thisY = getY();
      }
    else
      {
	pf_x = getPlayfield().getX();
	pf_y = getPlayfield().getY();
	thisX = getX() - pf_x;
	thisY = getY() - pf_y;
      }

    if ((x >= thisX) && 
	(x < (thisX + getWidth())) &&
	(y >= thisY) &&
	(y < (thisY + getHeight())))
      {
	x -= thisX;
	y -= thisY;
	return (!Ps1Defs.IsPixelTransparent(getPixel(x, y)));
      }
    return false;

  }

  /*
   * Returns true if this Sprite is controllable, false
   * otherwise
   */
  public final boolean isControllable()
  {
    return m_isControllable;
  }

  /*
   * Sets whether the sprite is controllable
   */
  public final void setControllable(boolean isControllable)
  {
    m_isControllable = isControllable;
  }

  /*
   * Sets the direction of this sprite, using a constant
   * from Ps1Defs as a parameter
   */
  public final void setDirection(int direction)
  {
    m_direction = direction;
  }

  /*
   * Sets the event source which will propagate events
   * to this Sprite
   */
  public final void setEventSource(Component src)
  {
    src.addKeyListener(this);
    src.addMouseListener(this);
  }

  /*
   * Sets whether the Sprite has focus
   */
  public final void setFocus(boolean hasFocus)
  {
    m_hasFocus = hasFocus;
  }

  /*
   * Sets the Playfield of this Sprite to be the
   * specified parameter
   */
  public final void setPlayfield(Playfield pf)
  {
    m_pf = pf;
  }
  
  /*
   * Sets the state of this sprite to be the 
   * specified parameter
   */
  public final void setState(int state)
  {
    m_state = state;
  }

  /*
   * Sets the velocity of this Sprite to be the
   * specified parameter
   */
  public void setVelocity(int v)
  {
    m_velocity = v;
  }

  /*
   * Sets the x-coordinate of this Sprite to be the
   * specified parameter
   */
  public final void setX(int x)
  {
    m_x = x;
  }

  /*
   * Sets the y-coordinate of this Sprite to be the
   * specified parameter
   */
  public final void setY(int y)
  {
    m_y = y;
  }
  

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

  public abstract void keyPressed(KeyEvent e);
  public abstract void keyReleased(KeyEvent e);
  public abstract void keyTyped(KeyEvent e);
  public abstract void mouseClicked(MouseEvent e);
  public abstract void mousePressed(MouseEvent e);
  public abstract void mouseReleased(MouseEvent e);
  public abstract void mouseEntered(MouseEvent e);
  public abstract void mouseExited(MouseEvent e);
                           
}








