/**********************************
 * Assignment 5: Pacman
 * Date: 5/5/2006
 *
 * Jenna Holloway: jh382, 563324
 * Benji Shultz: bas67, 1786511
 **********************************/
package game.actor;

import game.Constants;
import game.Game;
import game.map.PacmanMap;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.util.EnumSet;
import java.util.Set;

/**
 * Class to represent an Actor in the game
 * Since it is Abstract, it must be extended
 * 
 * @author Alec Berntson
 * 
 */
public abstract class Actor implements Constants {
   
	// these are used to keep track of movement
   static final EnumSet<MovementDirection> SOUTH_OR_EAST = EnumSet.of(MovementDirection.SOUTH, MovementDirection.EAST);
   MovementDirection currentDirection = MovementDirection.NONE;
   MovementDirection nextDirection = MovementDirection.NONE;
   
   protected Point2D lastInterection = new Point2D.Double(-1, -1);
   private Point2D initialPosition; // where the actor was created
   protected Point2D currentPosition; // the current position of the Actor
   double speed; // speed in pixels per second
   // static reference to the Game object
   static Game game;
   /*
    * The standard and current image set used for this Actor
    */
   Images standardImages;
   Images currentImages;

   /**
    * Constructs a new Actor instance
    * 
    * @param unitType
    *           what type of unit
    * @param speed
    *           how fast the unit moves (pixels/sec)
    * @param initialPosition
    *           initial location
    * @param imagePaths
    *           the file paths where this unit's images are stored
    */
   public Actor(String unitType, double speed, Point2D initialPosition, String[][] imagePaths) {
      this.speed = speed;
      currentImages = standardImages = new Images(imagePaths);
      currentPosition = this.initialPosition = initialPosition;
   }
   /**
    *puts the actor back to where it came from 
    */
   public void reset() {
      currentPosition = initialPosition;
   }
  public Point2D getInitialPosition(){
	  return initialPosition;
	  
  }
  public void addDirection(MovementDirection md){;}
   /**
    * Given that delta_time has passed, move this actor. This method relies on
    * the Map for allowable coordinates and on the current and next Movement
    * Direction fields being set. It moves the Actor along currentDirection
    * until it 1.) hits a wall and stops or 2.) reaches an intersection and
    * changes direction.
    * 
    * @param dt
    *           the delta_time - amount of time passed
    */
   public void move(double dt) {
      Point2D newLocation = calculatePotentialNewLocation(dt);
      Point2D newLeadingEdge = convertToLeadingEdge(newLocation);
      boolean oldIntersect = false;

      // Set up our new, old tiles and calculate the 'leading edge' if we are
      // going South or East
      int newTileX = (int) newLeadingEdge.getX() / CELL;
      int newTileY = (int) newLeadingEdge.getY() / CELL;

      int nlnewTileX = (int) newLocation.getX() / CELL;
      int nlnewTileY = (int) newLocation.getY() / CELL;

      int oldTileX = (int) currentPosition.getX() / CELL;
      int oldTileY = (int) currentPosition.getY() / CELL;

      int tileX = 0;
      int tileY = 0;

      if (SOUTH_OR_EAST.contains(currentDirection)) {
         tileX = nlnewTileX;
         tileY = nlnewTileY;
      } else {
         tileX = oldTileX;
         tileY = oldTileY;
      }

      // the leading edge of our square has moved to a new tile
      if (oldTileX != newTileX || oldTileY != newTileY) {
         PacmanMap map = game.map;
         // we need to check if the old space is an intersection (only when we
         // 'finish' a space can we turn)
         Set<MovementDirection> possibleDirections = map.getPossibleDirections(tileX, tileY, this instanceof Ghost);
         boolean isIntersection = map.isIntersection(tileX, tileY, this instanceof Ghost);

         // if this is an intersection, we can go our 'nextDirection' way
         oldIntersect = lastInterection.equals(new Point2D.Double(tileX, tileY));
         if (!oldIntersect && nextDirection != currentDirection && directionIsPossible(nextDirection, possibleDirections)) {
            // prevent doubling back
            if (isIntersection) {
               lastInterection = new Point2D.Double(tileX, tileY);
            }

            // relocate, redirect and recall this method so we move in the new
            // direction
            currentPosition = new Point2D.Double(tileX * CELL, tileY * CELL);
            currentDirection = nextDirection;
            move(dt);
            return;
         }

         // otherwise, the new space is just a straight line, check if we can
         // move to this space
         if (map.isTraversable(newTileX, newTileY, this instanceof Ghost)) {
            if(map.isWarpPoint(newTileX, newTileY)){
               Point2D warpPoint = map.getMatchingWarpPoint(newTileX, newTileY);
               newLocation = new Point2D.Double(warpPoint.getX() * CELL, warpPoint.getY() * CELL); 
            }
            // no problems then
         } else { // Or we have reached a dead end, so stop moving
            currentPosition = new Point2D.Double(tileX * CELL, tileY * CELL);
            newLocation = currentPosition;
            currentDirection = MovementDirection.NONE;
            nextDirection = MovementDirection.NONE;
         }
      }

      // move to whatever new position we computed above
      currentPosition = newLocation;
   }

   /**
    * Check to see if direction is a member of possibleDirections
    * 
    * @param direction
    * @param possibleDirections
    * @return true if direction is contained in possibleDirections
    */
   private boolean directionIsPossible(MovementDirection direction, Iterable<MovementDirection> possibleDirections) {
      for (MovementDirection dir : possibleDirections) {
         if (dir == direction) return true;
      }
      return false;
   }

   /**
    * Calculate where we would be if we moved for dt time along our current
    * direction
    * 
    * @param dt
    *           delta_time - how much time has passed
    * @return the new potential location
    */
   private Point2D calculatePotentialNewLocation(double dt) {
      double movePotential = getSpeed() * dt;
      switch (currentDirection) {
      case NONE: return currentPosition;
      case NORTH: return new Point2D.Double(currentPosition.getX(), currentPosition.getY() - movePotential);
      case SOUTH: return new Point2D.Double(currentPosition.getX(), currentPosition.getY() + movePotential);
      case EAST: return new Point2D.Double(currentPosition.getX() + movePotential, currentPosition.getY());
      case WEST: return new Point2D.Double(currentPosition.getX() - movePotential, currentPosition.getY());
      default: return null;
      }
   }

   /**
    * Takes the upperLeft corner of the actor and returns the 'leading edge'.
    * The Leading edge is the part of the image that is at the front, based on
    * the direction of the Actor's movment
    * 
    * @param upperLeft
    *           the coordinates of the image
    * @return the leading edge's coordinates
    */
   private Point2D convertToLeadingEdge(Point2D upperLeft) {
      if (currentDirection == MovementDirection.SOUTH) {
         return new Point2D.Double(upperLeft.getX(), upperLeft.getY() + CELL);
      }
      if (currentDirection == MovementDirection.EAST) {
         return new Point2D.Double(upperLeft.getX() + CELL, upperLeft.getY());
      }
      return upperLeft;
   }

   /**
    * draw the unit onto graphics context g
    * 
    * @param g
    *           the Graphics object that we can use to draw
    */
   public void draw(Graphics g) {
      Image image = currentImages.getNextImage(currentDirection);
      g.drawImage(image, (int)currentPosition.getX() - CELL/2, (int)currentPosition.getY() - CELL/2, null);
   }

   public boolean collidesWith(Actor a) {
      Rectangle rect = a.getBoundingBox();
      return rect.intersects(this.getBoundingBox());
   }

   public Rectangle getBoundingBox() {
      return new Rectangle((int) getCurrentPosition().getX(), (int) getCurrentPosition().getY(), CELL, CELL);
   }

   /**
    * 
    * @return the current position of this unit
    */
   public Point2D getCurrentPosition() {
      return currentPosition;
   }
   
   public Point getCurrentCellPoint() {
      Point2D p = getCurrentPosition();
      return new Point((int)p.getX()/CELL, (int)p.getY()/CELL);
   }

   public double getSpeed() {
      return speed;
   }

   public static void setGame(Game game){
	   Actor.game = game;
   }

}
