package ps1;

import java.awt.*;
import java.awt.image.*;
import java.util.*;
import java.util.Random;

public class Sprite
{
  /* Declarations */
  private Vector images;
  private Vector transitions;
  private int sequence;
  private boolean initialized;
  public boolean selected;
  int z_order;
  int x, y;
  
  // velocity
  private int velx;
  private int vely;
  String name;

  /**
   * This constructor takes an argument that specifies 
   * the number of images this sprite will use, and the sprite's name.
   */
  public Sprite(int n, String s)
  {
    Random rand = new Random();
    images = new Vector(n);
    
    // The transitions vecotr is initialized to start out as if there will
    // be a transition from every image to every other image. It will 
    // be trimmed after the first call to toImage.
    transitions = new Vector(n*n,5);
    sequence = 0;
    initialized = false;
    selected = false;
    x = 0;
    y = 0;
    name = s;

    velx = (int) (rand.nextFloat() * 6 - 3);
    vely = (int) (rand.nextFloat() * 6 - 3);
  }
	
  /////////////////////// Methods ////////////////////
	
  /**
   * This method adds an image to the sprite. After the 
   * sprite is initialized, all the images must be added
   * in the order in which they will be entered into the
   * vector. This is subnominal, but quick and clean.
   * The user of the class will have to maintain the ordering
   * when he specifies transitions.
   */
  public void AddImage(Image img)
  {
    // Declarations
    Raster r;
    int i, j;

    if(sequence < images.capacity())
      {
	r = new Raster(img);
	images.addElement(r);
	sequence++;
	
	// initialize the alpha values to 255
	for(i=0;i<r.width;i++)
	  for(j=0;j<r.height;j++)
	    {
	      r.pixel[j*r.width+i] = r.pixel[j*r.width+i] | 0xFF000000;
	    }
      }
  }

  /** 
   * This method adds a transition to the transition vector.
   * The transition is specified in terms of the change in x, the change
   * in y, the original image, and the final image.
   */
  public void AddTransition(boolean a, int dx, int dy, int img1, int img2)
  {
    // Declarations
    Transition t;
    
    // Initializations
    t = new Transition(a, dx, dy, img1, img2);
    
    transitions.addElement(t);
  }

  /**
   * This method initializes the sprite by setting the starting image,
   * the initial z order, and the initial position
   */
  public void initSprite(int start_x, int start_y, 
			 int start_img, int z) throws UninitializedException
  {
    if(sequence != images.size())
      {
	//System.out.println("incomplete");
	throw new UninitializedException("Image addition incomplete.");
      }
    else if(start_img < 0 || start_img >= sequence)
      {
	//System.out.println("invalid start: sequence="+sequence);
	throw new UninitializedException("Invalid start image.");
      }
    else if(z < 0)
      {
	//System.out.println("Invalid z");
	throw new UninitializedException("Invalid z order.");
      }
    else
      {
	sequence = start_img;
	x = start_x;
	y = start_y;
	z_order = z;
	initialized = true;
      }
  }

  /** 
   * This method returns the raster that is the current sprite image.
   */
  public Raster getRaster() throws UninitializedException
  {
    if(initialized)
      return((Raster)images.elementAt(sequence));
    else
      throw new UninitializedException("Sprite not initialized.");
  }

  /**
   * This method implements the movement of the sprite. The variables
   * new_x and new_y specify the new position. All sprites can only move 
   * one step in any direction at a time.
   */
  public void move(int new_x, int new_y) throws UninitializedException
  {
    // Declarations
    int dx, dy;
    int i, j;

    // Can't move it if the sprite hasn't been fully initialized.
    if(!initialized)
      throw new UninitializedException("Sprite not initialized.");

    // Figure out the change in position
    dx = new_x - x;
    dy = new_y - y;
    
    // Update the position variables.
    x = new_x;
    y = new_y;
    
    // Update the current raster
    // Check if there is a transition specified for the change in position
    // If there is none, then default to using the same image.
    for(i = 0;i < transitions.size();i++)
      if(((Transition)transitions.elementAt(i)).checkTransition(dx, dy, sequence))
	{
	  sequence = ((Transition)transitions.elementAt(i)).getTransitionTarget();
	  break;
	}
  }
  
  // modifies: this
  // effects: changes the coordinates of <this> by adding the velocity 
  //          to the current coordinates
  public void shunt () 
  {
    int i;

    // To move the object, simply move the upper left corner of 
    // its bounding rectangle
    x = x + velx;
    y = y + vely;

    // Update the current raster
    // Check if there is a transition specified for the change in position
    // If there is none, then default to using the same image.
    for(i = 0;i < transitions.size();i++)
      if(((Transition)transitions.elementAt(i)).checkTransition(velx, vely, sequence))
	{
	  sequence = ((Transition)transitions.elementAt(i)).getTransitionTarget();
	  break;
	}
  }
  
  public void flip (int maxx, int maxy) {
    // modifies: this
    // effects: checks whether the bounding rectangle of <this> is within 
    //          the maximum rectangle, where the maximum rectangle is
    //          defined as the area bounded by [0 <= x <= maxx] and 
    //          [0 <= y <= maxy]. If it's not, reverses either x or y 
    //          component of velocity in order to return <this> to the 
    //          maximum rectangle.
    {
      // Declarations (get the current size of the sprite)
      int height = ((Raster)images.elementAt(sequence)).height;
      int width =  ((Raster)images.elementAt(sequence)).width;
      
      // Perform the checks
      if(x + velx < 0)
	velx = Math.abs(velx);
      if(x + width + velx >= maxx)
	velx = - Math.abs(velx);
      
      if(y + vely < 0)
	vely = Math.abs(vely);
      if(y + height + vely >= maxy)
	vely = - Math.abs(vely);
    }
  }

  /**
   * This method returns the sprite's x position.
   */
  public int get_x_position()
  {
    return x;
  }

  /**
   * This method returns the sprite's y position.
   */
  public int get_y_position()
  {
    return y;
  }

  /**
   * This method returns the sprite's z-order.
   */
  public int get_z_order()
  {
    return z_order;
  }
  
  /**
   * This function returns the sprite's name.
   */
  public String getName()
  {
    return(new String(name));
  }

  // effects: returns true if the point lies inside the bounding 
  //          rectangle of <this>. Returns false otherwise.
  //          <pt> is App given in parent coordinates.
  public boolean hitTest(int ax, int ay)
  {
    // Declarations (get the current size of the sprite
    int height = ((Raster)images.elementAt(sequence)).height;
    int width =  ((Raster)images.elementAt(sequence)).width;
    
    return((ax > x & ax < x + width) &&
	   (ay > y & ay < y + height));
  }
}


