import java.awt.Image;
import java.awt.Color;
import java.util.Vector;

/** The Sprite class is designed to act as a moving object on the
    screen.  It caches its current image, stores a list of
    SpriteListener objects that wish to be informed when the Sprite
    moves, and adds a touch(x,y) method that tests if a given pixel in
    the Sprite is indeed a valid part of the Sprite or not.
*/
public class Sprite extends Raster {

  public static final int ALPHA_CHANNEL = 0xFF000000;
  private String name;
  private int x, y;
  private Image image;
  private Vector listeners;
  
  /** Constructor.
      Creates a sprite displaying 'img'
  */
  public Sprite(String name, Image img) {
    super(img);
    this.name = name;
    this.image = toImage();
    listeners = new Vector();
  }
  
  /** Adds l to the list of SpriteListeners.
    */
  public void addSpriteListener(SpriteListener l) {
    listeners.addElement(l);
  }

  /** Removes l from the list of SpriteListeners, if l was a member of
      the list.  Otherwise does nothing.
    */
  public void removeSpriteListener(SpriteListener l) {
    listeners.removeElement(l);
  }
  
  /** Raster fill method.
      effects: Fills sprite with a solid color.
  */
  public void fill(Color c) {
    super.fill(c);
    this.image = toImage();
  }

  /** Raster setPixel method.
      effects: Sets a pixel to a given value.
    */
  public boolean setPixel(int pix, int x, int y) {
    boolean success = super.setPixel(pix,x,y);
    this.image = toImage();
    return success;
  }
  
  /** Raster setColor method.
      effects: Sets a pixel to a given color.
    */
  public boolean setColor(Color c, int x, int y) {
    boolean success = super.setColor(c,x,y);
    this.image = toImage();
    return success;
  }

  /** Clears the area behind sprite.
      effects: Clears the area behind sprite in Raster r, using the
               Raster bg as the source for the background pixels.
  */
  public void clear(Raster r, Raster bg) {
    int offset=(y<0)?-y*getWidth():0;

    int backOffset= (y<0)?x:y*r.getWidth()+x;

    int startWidth = (x<0)?-x:0;
    int startHeight = (y<0)?-y:0;

    // check for clipping bounds
    int endWidth = 
      (r.getWidth()-x < getWidth())?
      r.getWidth()-x:
      getWidth();
    int endHeight =
      (r.getHeight()-y < getHeight())?
      r.getHeight()-y:
      getHeight();

    for (int h=startHeight; h<endHeight; h++) {
      for (int w=startWidth; w<endWidth; w++) {
	if ((pixel[offset+w] & ALPHA_CHANNEL) != 0) {
	  r.pixel[backOffset+w] = bg.pixel[backOffset+w];
	}
      }
      offset += getWidth();
      backOffset += r.getWidth();
    } 
  }
  
  /** Draws sprite onto Raster r.
    */
  public void draw(Raster r) {
    // check for clipping bounds
    int offset=(y<0)?-y*getWidth():0;
    int backOffset= (y<0)?x:y*r.getWidth()+x;    
    int startWidth = (x<0)?-x:0;
    int startHeight = (y<0)?-y:0;    
    int endWidth =(r.getWidth()-x < getWidth())?r.getWidth()-x:getWidth();
    int endHeight=(r.getHeight()-y < getHeight())?r.getHeight()-y:getHeight();

    for (int h=startHeight; h<endHeight; h++) {
      for (int w=startWidth; w<endWidth; w++) {
	if ((pixel[offset+w] & ALPHA_CHANNEL) != 0) {
	  r.pixel[backOffset+w]=pixel[offset+w];
	}
      }
      offset += getWidth();
      backOffset += r.getWidth();
    } 
  }
  
  /** Returns the name of this sprite.
    */
  public String getName() {
    return name;
  }

  /** Object toString method.
    */
  public String toString() {
    return "Sprite[ " + name + " ]";
  }

  /** Sets the location of this sprite.
    */
  public void setLocation(Point p) {
    Point oldPoint = new Point(x, y);
    x = p.getX();
    y = p.getY();
    for(int i=0; i<listeners.size(); i++) {
      SpriteListener l = (SpriteListener) listeners.elementAt(i);
      l.locationChanged(this, oldPoint, p);
    }
  }

  /** Returns the location of this sprite.
    */
  public Point getLocation() {
    return new Point(x, y);
  }

  /** Checks if this sprite covers the point (x,y), and returns true
      if it does, false otherwise.
      Subclasses of Sprite implementing side effects of user
      interaction with the Sprite should override this method to do so
      (but also ensure that their version of touch has identical
      effects.
  */
  public boolean touch(int x, int y) {
    int index = y*getWidth()+x;
    if (y > 0 && y < getHeight() &&
	x > 0 && x < getWidth()) {
      if ((pixel[index] & ALPHA_CHANNEL) != 0) {
	return true;
      }
    }
    return false;
  }
  
  /** tick method for updating Sprites.
      
      subclasses of Sprite should fill in this method to cause
      something to happen when a timestep occurs.
    */
  public void tick() {
    
  } 
}


