/////////////////////
// Sidney Chang    //
// 6.837 Project 1 //
// TA: Jacob F11   //
// 9/28/98         //
/////////////////////

import java.awt.*;
import java.awt.image.*;

class AnimatedSprite extends Sprite {

  public static final int MAX_TRACKS = 10;  // maximum no. of tracks
  public static final int MAX_FRAMES = 20;  // maximum no. of frames per track

  // Variables
  int tracks[][];        // tracks of frame sequences

  int nFrames;           // number of frames in strip of images
  int frameWidth;        // width of each frame
  int frameHeight;       // height of each frame
  int frameX;            // position of current frame in strip of images
  int frameY;            //   i.e. frame 0 has frameX 0 and frameY 0

  int currentTrack;      // current track in tracks
  int currentFrame;      // current frame in current track of tracks

  int coveredPixels[];   // pixels covered by current frame

  // Constructors
  public AnimatedSprite(Image images, int frames) {
    // Constructs an animated sprite with the sequence of images in
    // images divided into frames frames. AnimatedSprite assumes that
    // images is a horizontal series of equal size frames.

    super(images);

    tracks = new int[MAX_TRACKS][MAX_FRAMES];
    for (int i=0; i<MAX_TRACKS; i++) {    // Maximum of 10 tracks with maximum
      for (int j=0; j<MAX_FRAMES; j++) {  // of 20 frames per track. Unused 
	tracks[i][j] = -1;                // frames in tracks have value -1.
      }
    }
    
    nFrames = frames;
    frameWidth = super.width / nFrames;
    frameHeight = super.height;
    frameX = 0; 
    frameY = 0;

    currentTrack = 0;
    currentFrame = 0;

    coveredPixels = new int[frameWidth*frameHeight];
  }

  public AnimatedSprite(Image images, int frames, int xPos, int yPos) {
    // Constructs an animated sprite with the sequence of images in
    // images divided into frames frames positioned at (xPos,yPos) in
    // the playfield. AnimatedSptite assumes that images is a horizontal
    // series of equal size frames.

    super(images,xPos,yPos);

    tracks = new int[MAX_TRACKS][MAX_FRAMES];
    for (int i=0; i<MAX_TRACKS; i++) {    // Maximum of 10 tracks with maximum
      for (int j=0; j<MAX_FRAMES; j++) {  // of 20 frames per track. Unused
	tracks[i][j] = -1;                // frames in tracks have value -1.
      }
    }

    nFrames = frames;
    frameWidth = super.width / nFrames;
    frameHeight = super.height;
    frameX = 0; 
    frameY = 0;

    currentTrack = 0;
    currentFrame = 0;

    coveredPixels = new int[frameWidth*frameHeight];
  }

  // Methods
  public void Draw(Raster bgnd) {
    // AnimatedSprite's Draw method overrides Sprite's draw method.
    // Draw will draw the current frame of the current track to the
    // given Raster. It will take into account clipping and 
    // transparent pixels. It also save whatever pixels the sprite
    // may cover on the background.

    int drawWidth = frameWidth;     // width of portion to be drawn
    int drawHeight = frameHeight;   // height of portion to be drawn

    // erase sprite from previous position on field
    if (onField)
      erase(bgnd);

    // set the coordinates of the currentFrame
    frameX = frameWidth * tracks[currentTrack][currentFrame];
    frameY = 0;

    int startX = frameX;            // starting position of portion to 
    int startY = frameY;            // be drawn in sprite

    // *** CLIPPING ***
    
    // sprite is completely off playfield - don't draw anything
    if (x >= bgnd.width || y >= bgnd.height ||
	x+frameWidth < 0 || y+frameHeight < 0) {
      onField = false;
      return;
    }

    onField = true;
    
    // sprite is partially off left edge of playfield
    if (x < 0) {
      startX = startX-x;
    }
    // sprite is partially off right edge of playfield
    else 
      if (x+frameWidth >= bgnd.width) {
	drawWidth = bgnd.width - x;
      }
    // sprite is partially off top edge of playfield
    if (y < 0) {
      startY = startY-y;
    }
    // sprite is partially off bottom edge of playfield
    else 
      if (y+frameHeight >= bgnd.height) {
	drawHeight = bgnd.height - y;
      }

    oldX = x;
    oldY = y;

    for (int i=startY; i<frameY+drawHeight; i++) {
      for (int j=startX; j<frameX+drawWidth; j++) {
	// save all pixels under sprite
    	coveredPixels[(i-frameY)*frameWidth+(j-frameX)] = 
	  bgnd.getPixel(x+j-frameX,y+i-frameY);

      }
    }

    for (int i=startY; i<frameY+drawHeight; i++) {
      for (int j=startX; j<frameX+drawWidth; j++) {

	// *** TRANSPARENT PIXELS ***

	int current_pix = super.getPixel(j,i);
	// find transparency = alpha byte of 32-bit pixel
	int alpha = (current_pix >> 24) & 0xff;	
	// draw pixel only if it is not transparent
	if (alpha != 0) {
	  bgnd.setPixel(current_pix, x+j-frameX, y+i-frameY);
	}
      }
    }

    // advance to next frame
    nextFrame();
  }

  public void erase(Raster bgnd) {
    // AnimatedSprite's erase overrides Sprite's erase. erase will replace
    // the covered pixels to the background. This also much take into 
    // account clipping.

    int backWidth = frameWidth;    // width of portion to be replaced
    int backHeight = frameHeight;  // height of portion to be replaced

    int backX = frameX;            // starting position of portion to
    int backY = frameY;            // be replaced

    // *** CLIPPING ***

    // find clipping values for background to replace

    // sprite is completely off playfield - don't draw anything
    if (oldX >= bgnd.width || oldY >= bgnd.height ||
	oldX+frameWidth < 0 || oldY+frameHeight < 0)
      return;

    // sprite is partially off left edge of playfield
    if (oldX < 0) {
      backX = backX-oldX;
    }
    // sprite is partially off right edge of playfield
    else 
      if (oldX+frameWidth >= bgnd.width) {
	backWidth = bgnd.width - oldX;
      }    
    // sprite is partially off top edge of playfield
    if (oldY < 0) {
      backY = backY-oldY;
    }
    // sprite is partially off bottom edge of playfield
    else 
      if (oldY+frameHeight >= bgnd.height) {
	backHeight = bgnd.height - oldY;
      }

    // replace covered pixels to bgnd
    for (int i=backY; i<frameY+backHeight; i++) {
      for (int j=backX; j<frameX+backWidth; j++) {
	bgnd.setPixel(coveredPixels[(i-frameY)*frameWidth+(j-frameX)],
		      oldX+j-frameX,oldY+i-frameY);
      }
    }
  }

  public void addFrame(int track, int frame) {
    // addFrame add frame to the next empty position in track

    if (track < 0 || track >= MAX_TRACKS) {
      System.out.println("Error on AnimatedSprite.addFrame():\n" +
			 " No such track, maximum " + MAX_TRACKS + " tracks");
      return;
    }
    else if (frame < 0 || frame >= nFrames) {
      System.out.println("Error on AnimatedSprite.addFrame():\n" +
			 " No such frame, sprite has " + nFrames + " frames");
      return;
    }
    else {
      for (int i=0; i<MAX_FRAMES; i++) {
	// Find first empty frame in track (-1)
	// and enter new frame into track.
	if (tracks[track][i] == -1) {
	  tracks[track][i] = frame;
	  return;
	}
      }
      // No more room in specified track.
      System.out.println("Error on AnimatedSprite.addFrame():\n" +
			 " No more room in track");
    }
  }

  public void setTrack(int track) {
    // setTrack sets the currentTrack to track

    if (track < 0 || track >= MAX_TRACKS) {
      System.out.println("Error on AnimatedSprite.setTrack():\n" +
			 " No such track");
      return;
    }
    else
      currentTrack = track;
  }

  public void setFrame(int frame) {
    // setFrame sets the currentFrame in the currentTrack to frame

    if (frame < 0 || frame >= MAX_FRAMES) {
      System.out.println("Error on AnimatedSprite.setFrame():\n" +
			 " No such frame");
      return;
    }
    else
      currentFrame = frame;
  }

  public void nextFrame() {
    // nextFrame advances the currentFrame to the next frame in the
    // currentTrack, if the currentFrame is the last frame in the
    // track, nextFrame will set the currentFrame to the first frame
    // in the currentTrack
    
    // On last possible frame of track, set track to beginning
    if (currentFrame >= MAX_FRAMES)
      currentFrame = 0;
    // On last non-empty frame of track, set track to beginning
    else if (tracks[currentTrack][currentFrame+1] == -1)
      currentFrame = 0;
    else
      currentFrame++;
  }

  public boolean collidesWith(AnimatedSprite s) {
    // collidesWith returns true if the bounding boxes of sprite s and this
    // sprite collide otherwise returns false.
    
    if (((x >= s.x && x < s.x+s.frameWidth) &&
	 (y >= s.y && y < s.y+s.frameHeight)) ||
	((x+frameWidth >= s.x && x+frameWidth < s.x+s.frameWidth) &&
	 (y >= s.y && y < s.y+s.frameHeight)) ||
	((x >= s.x && x < s.x+s.frameWidth) &&
	 (y+frameHeight >= s.y && y+frameHeight < s.y+s.frameHeight)) ||
	((x+frameWidth >= s.x && x+frameWidth < s.x+s.frameWidth) &&
	 (y+frameHeight >= s.y && y+frameHeight < s.y+s.frameHeight))) 
      return true;
    else
      return false;
  }

  public boolean isSprite(int xPos, int yPos) {
    // isSprite returns true if the given coordinates are at
    // a non-transparent pixel of this sprite

    if (xPos >= x && xPos <= x+frameWidth &&
	yPos >= y && yPos <= y+frameHeight) {
      int pix = super.getPixel(xPos-x+frameX,yPos-y+frameY);
      int alpha = (pix >> 24) & 0xff;
      if (alpha == 0) // transparent
	return false;
      else            // non-transparent
	return true;
    }
    else
      return false;
  }

  public void printTracks() {
    // prints the contents of tracks

    for (int i=0; i<MAX_TRACKS; i++) {
      System.out.println("Track " + i + ": ");
      for (int j= 0; j<MAX_FRAMES; j++)
	System.out.println("Frame " + j + ": " + tracks[i][j]);
    }
  }
}
         

