/*
 * Motion manager
 * 
 * Control the player position according to the user's actions.
 */

/**
 * Constructor.
 *
 * @param player to control
 */
function MotionManager(player)
{
	//Constants
	this.KEY_LEFT = 37;
	this.KEY_UP = 38;
	this.KEY_RIGHT = 39;
	this.KEY_DOWN = 40;
	this.KEY_SPACE = 32;
	this.DISTANCE_MOVEMENT = 8;
	this.DISTANCE_FAST_MOVEMENT = 16;

	//Attributes
	this.player = player;
	this.undergroundPathFinder = new UndergroundPathFinder(this, player.map.getPaths(), player.map.getTeleporters());
	this.outdoorPathFinder = new OutdoorPathFinder(this, player.map.getPaths(), player.map.getTeleporters());
	this.direction = 0; // Direction of the player's movement (0 = don't move, this.KEY_xxx = go to xxx)
	this.wishedDirection = 0; // Value set in this.direction when the player is ready
	this.lastTeleporterPosition = {"x":-1, "y":-1}; // Some tile normally stop the player movements; theses
	this.lastStationPosition = {"x":-1, "y":-1};    // variables temporary deactivate their effects.
	this.lastCrossroadPosition = {"x":-1, "y":-1};  //
	this.undergroundComputedPath = null;
	this.outdoorComputedPath = null;
	this.isUnderground = false;
	this.moveFast = false;
}

/**
 * Set the direction where to move the player.
 *
 * @param wishedDirection integer which represents the direction (values: this.KEY_LEFT, this.KEY_UP...etc)
 */
MotionManager.prototype.setWishedDirection = function(wishedDirection)
{
	this.wishedDirection = wishedDirection;
}

/**
 * Set the location where to move the player.
 * The path is computed by using the Dijkstra's algorithm.
 *
 * @param wishedDirection integer which represents the direction (values: player.KEY_LEFT, player.KEY_UP...etc)
 */
MotionManager.prototype.setWishedPosition = function(wishedGridPosition)
{
	this.wishedDirection = 0;
	
	//Compute the path
	if(this.outdoorPathFinder.buildPathBetweenLocations(this.player.getGridPosition(), wishedGridPosition))
	{
		this.outdoorComputedPath = this.outdoorPathFinder.getComputedPath();
		this.moveFast = true;
	}
}

/**
 * Move the player on a certain distance.
 */
MotionManager.prototype.move = function()
{
	//Get the player position on the tilemap
	var gridPosition = this.player.getGridPosition();
	
	//Compute the next direction (when the player is not between 2 tiles)
	if(gridPosition.x*this.player.map.SPRITE_SIZE == this.player.position.x &&
	   gridPosition.y*this.player.map.SPRITE_SIZE == this.player.position.y)
	{
		this.computeNextDirection(gridPosition);
	}
	
	//Move the player according to the direction
	var distanceMovement = this.moveFast ? this.DISTANCE_FAST_MOVEMENT : this.DISTANCE_MOVEMENT;
	switch(this.direction)
	{
		case this.KEY_LEFT:  this.player.position.x-=distanceMovement; break;
		case this.KEY_RIGHT: this.player.position.x+=distanceMovement; break;
		case this.KEY_UP:    this.player.position.y-=distanceMovement; break;
		case this.KEY_DOWN:  this.player.position.y+=distanceMovement; break;
	}
}

/**
 * Compute the next direction.
 * @param gridPosition player's position on the tilemap
 */
MotionManager.prototype.computeNextDirection = function(gridPosition)
{
	var teleporters = this.player.map.getTeleporters();
	var paths = this.player.map.getPaths();
	var previousDirection = this.direction;
	
	//Guide the player if a computed path has been computed
	if(this.undergroundComputedPath != null)
	{
		var computedDirection = this.getComputedPathDirection(this.undergroundComputedPath, gridPosition);
		if(computedDirection > 0)
		{
			this.direction = computedDirection;
			return;
		}
	}
	if(this.outdoorComputedPath != null)
	{
		var computedDirection = this.getComputedPathDirection(this.outdoorComputedPath, gridPosition);
		if(computedDirection > 0)
		{
			this.direction = computedDirection;
			return;
		}
	}

	//Reset some variables used for deactivating some tiles actions
	if(this.lastTeleporterPosition.x != gridPosition.x || this.lastTeleporterPosition.y != gridPosition.y)
		this.lastTeleporterPosition = {"x":-1, "y":-1};
	if(this.lastStationPosition.x != gridPosition.x || this.lastStationPosition.y != gridPosition.y)
		this.lastStationPosition = {"x":-1, "y":-1};
	if(this.lastCrossroadPosition.x != gridPosition.x || this.lastCrossroadPosition.y != gridPosition.y)
		this.lastCrossroadPosition = {"x":-1, "y":-1};
		
	//Change the player direction if a key has been pressed
	if(this.wishedDirection != 0)
	{
		this.direction = this.wishedDirection;
		this.wishedDirection = 0;
	}
	
	//When the player arrive at a teleporter, pre-Compute a path
	if(teleporters[gridPosition.y][gridPosition.x] > 1 &&
	   (this.lastTeleporterPosition.x != gridPosition.x || this.lastTeleporterPosition.y != gridPosition.y))
	{
		if(this.undergroundPathFinder.buildPathBetweenTeleporters(gridPosition))
		{
			//Compute the underground
			this.undergroundComputedPath = this.undergroundPathFinder.getComputedPath();
			this.direction = this.getComputedPathDirection(this.undergroundComputedPath, gridPosition);
			
			//Set the last teleporter position
			var teleporterExitPosition = this.undergroundPathFinder.getExitTeleporterLocation(gridPosition);
			this.lastTeleporterPosition.x = teleporterExitPosition.x;
			this.lastTeleporterPosition.y = teleporterExitPosition.y;
			
			//Set the underground flag for the Player class which must know if we display the player or not
			this.isUnderground = true;
			
			return;
		}
	}
	
	//The player must stop when he arrives at a station
	if(paths[gridPosition.y][gridPosition.x] == 2 &&
	   (this.lastStationPosition.x != gridPosition.x || this.lastStationPosition.y != gridPosition.y))
	{
		this.direction = 0;
		this.lastStationPosition.x = gridPosition.x;
		this.lastStationPosition.y = gridPosition.y;
		return;
	}

	//Compute the next possible path
	if(this.direction == 0) return;
	
	var gridPositionIfGoStraight = this.getNextGridPosition(gridPosition, this.direction);
	var pathIfGoStraight = paths[gridPositionIfGoStraight.y][gridPositionIfGoStraight.x];
	
	var turnRightDirection = this.getNextDirectionIfTurn(this.direction, true);
	var gridPositionIfTurnRight = this.getNextGridPosition(gridPosition, turnRightDirection);
	var pathIfTurnRight = paths[gridPositionIfTurnRight.y][gridPositionIfTurnRight.x];
	
	var turnLeftDirection = this.getNextDirectionIfTurn(this.direction, false);
	var gridPositionIfTurnLeft = this.getNextGridPosition(gridPosition, turnLeftDirection);
	var pathIfTurnLeft = paths[gridPositionIfTurnLeft.y][gridPositionIfTurnLeft.x];
	
	//The player must stop when he arrives at a crossroad
	if(previousDirection != 0 && 
	   (this.lastCrossroadPosition.x != gridPosition.x || this.lastCrossroadPosition.y != gridPosition.y))
	{
		if( (pathIfGoStraight != 0 && pathIfTurnRight != 0) ||
			(pathIfGoStraight != 0 && pathIfTurnLeft != 0) ||
			(pathIfTurnLeft != 0 && pathIfTurnRight != 0))
		{
			this.direction = 0;
			this.lastCrossroadPosition.x = gridPosition.x;
			this.lastCrossroadPosition.y = gridPosition.y;
			return;
		}
	}
	
	//If the player cannot go straight, maybe he can turn
	if(pathIfGoStraight == 0)
	{
		if(pathIfTurnRight != 0) this.direction = turnRightDirection;
		else if(pathIfTurnLeft != 0) this.direction = turnLeftDirection;
		else this.direction = 0;
		return;
	}
}

/**
 * Get the current direction of the computed path.
 *
 * @param computedPath computed path to follow
 * @param currentGridPosition player grid position
 * @return movement direction
 */
MotionManager.prototype.getComputedPathDirection = function(computedPath, currentGridPosition)
{
	var direction = computedPath[currentGridPosition.y][currentGridPosition.x];
	
	//Check when we finish the path
	var pathFinder = (computedPath == this.undergroundComputedPath ? this.undergroundPathFinder : this.outdoorPathFinder);
	var lastComputedPathTilePosition = pathFinder.getLastComputedPathTilePosition();
	if(lastComputedPathTilePosition.x == currentGridPosition.x && lastComputedPathTilePosition.y == currentGridPosition.y)
	{
		if(computedPath == this.undergroundComputedPath)
			this.undergroundComputedPath = null;
		else
		{
			this.outdoorComputedPath = null;
			this.player.openCurrentStation();
			this.moveFast = false;
		}
		if(this.isUnderground == true) this.isUnderground = false;
	}
	
	return direction;
}

/**
 * Get the next grid position according to the movement direction.
 *
 * @param currentGridPosition player grid position
 * @param direction movement direction
 * @return point object {x,y}
 */
MotionManager.prototype.getNextGridPosition = function(currentGridPosition, direction)
{
	switch(direction)
	{
		case this.KEY_LEFT: return {"x":(currentGridPosition.x-1), "y":currentGridPosition.y};
		case this.KEY_RIGHT: return {"x":(currentGridPosition.x+1), "y":currentGridPosition.y};
		case this.KEY_UP: return {"x":currentGridPosition.x, "y":(currentGridPosition.y-1)};
		case this.KEY_DOWN: return {"x":currentGridPosition.x, "y":(currentGridPosition.y+1)};
	}
	
	return {"x":currentGridPosition.x, "y":currentGridPosition.y};
}

/**
 * Get the next direction if we turn
 *
 * @param direction movement direction
 * @param turnRight boolean value if we turn right or not
 * @return direction
 */
MotionManager.prototype.getNextDirectionIfTurn = function(direction, turnRight)
{
	switch(direction)
	{
		case this.KEY_LEFT: return (turnRight ? this.KEY_UP : this.KEY_DOWN);
		case this.KEY_RIGHT: return (turnRight ? this.KEY_DOWN : this.KEY_UP);
		case this.KEY_UP: return (turnRight ? this.KEY_RIGHT : this.KEY_LEFT);
		case this.KEY_DOWN: return (turnRight ? this.KEY_LEFT : this.KEY_RIGHT);
	}
}




