package jp.ac.nii.icpc2010.players;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import jp.ac.nii.icpc2010.playfield.FieldDirection;
import jp.ac.nii.icpc2010.playfield.IPlayField;


/**
 * Players may extend this class to receive some additional convenience functions.
 * It is not necessary to extend BasePlayer; you may opt to extend AbstractPlayer directly instead.
 *
 *@see AbstractPlayer
 */
public class BasePlayer extends AbstractPlayer
{
	public BasePlayer(int id, IPlayField playField) {
		super(id, playField);
	}

	
	/**
	 * Obtain the list of enemy ids
	 * @return ArrayList of ids
	 */
	public ArrayList<Integer> getEnemyIds()
	{
		ArrayList<Integer> result = new ArrayList<Integer>();
		for(int i = 0; i < playField.getNumOfPlayers(); i++){
			if(i != getId()){
				result.add(i);
			}
		}
		return result;
	}

	/**
	 * Is it a free cell?
	 * @param x Current position X
	 * @param y Current position Y
	 * @return
	 */
	public boolean isFree(int x, int y)
	{
		int chip = 0;
		try {
			chip = playField.getObjectAt(x, y); 
		}
		catch (ArrayIndexOutOfBoundsException e) {
			return false;
		}

		if (chip == OBJECT_FREE) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Is there a coin on the cell?
	 * @param x Current position X
	 * @param y Current position Y
	 * @return
	 */
	public boolean isCoin(int x, int y)
	{
		int chip = 0;
		try {
			chip = playField.getObjectAt(x, y); 
		}
		catch (ArrayIndexOutOfBoundsException e) {
			return false;
		}

		if (chip == OBJECT_COIN) {
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * Is it a wall?
	 * @param x Current position X
	 * @param y Current position Y
	 * @return
	 */
	public boolean isWall(int x, int y)
	{
		int chip = 0;
		try {
			chip = playField.getObjectAt(x, y); 
		}
		catch (ArrayIndexOutOfBoundsException e) {
			return false;
		}

		if (chip == OBJECT_WALL) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Is it a trail of my Tron?
	 * @param x Current position X
	 * @param y Current position Y
	 * @return
	 */
	public boolean isMyTron(int x, int y)
	{
		int chip = 0;
		try {
			chip = playField.getObjectAt(x, y); 
		}
		catch (ArrayIndexOutOfBoundsException e) {
			return false;
		}

		if (chip == getTrailId()) {
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * Is it a trail of enemies?
	 * @param x Current position X
	 * @param y Current position Y
	 * @return
	 */
	public boolean isEnemyTron(int x, int y)
	{
		int chip = 0;
		try {
			chip = playField.getObjectAt(x, y); 
		}
		catch (ArrayIndexOutOfBoundsException e) {
			return false;
		}

		for (int enemyId: getEnemyIds())
		{
			if (chip == getTrailIdOf(enemyId)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Is it a trail?
	 * @param x Current position X
	 * @param y Current position Y
	 * @return
	 */
	public boolean isTron(int x, int y)
	{
		return (isMyTron(x, y) || isEnemyTron(x, y));
	}
	
	/**
	 * Is it safe to walk on the indicated coordinates?
	 * Currently this is true if it is either FIELD_FREE or FIELD_COIN.
	 * @param x Current position X
	 * @param y Current position Y
	 * @return
	 */
	public boolean isSafe(int x, int y)
	{
		return (isFree(x, y) || isCoin(x, y));
	}

	/**
	 * Obtain the position of the player
	 * @param id Target player's id
	 * @return Array of size 2; position 0 is x, position 1 is y
	 */
	public int[] getPositionById(int id)
	{
		return new int[]{getXOf(id), getYOf(id)};
	}
	
	/**
	 * Obtain the position of the player
	 * @param id Target player's id
	 * @return Point with the player's position
	 */
	public Point getPosition(int id)
	{
		return new Point(getXOf(id), getYOf(id));
	}

	/**
	 * Obtain the array of directions
	 * @return Up, Down, Left, Right
	 */
	public FieldDirection[] getDirs()
	{
		return FieldDirection.values();

	}

	/**
	 * Obtain the list of directions in randomised order
	 * @return Shuffled array of Up, Down, Left, Right
	 */
	public List<FieldDirection> getRandomDirs()
	{
		ArrayList<FieldDirection> result = new ArrayList<FieldDirection>();
		for (FieldDirection fd: getDirs()) {
			result.add(fd);
		}

		Collections.shuffle(result);
		return result;
	}

	/**
	 * Obtain the array of safe directions
	 * @param x Current position X
	 * @param y Current position Y
	 * @return array list of directions
	 */
	public List<FieldDirection> getSafeDirs(int x, int y)
	{
		ArrayList<FieldDirection> result = new ArrayList<FieldDirection>();

		for (FieldDirection dir: getDirs())
		{
			int[] pred = getPlayField().stepOne(x, y, dir);
			if (isSafe(pred[0], pred[1]))
			{
				result.add(dir);
			}
		}
		return result;
	}

	/**
	 * Obtain the list of positions next to the current position.
	 * In the resulting list, every item is an integer array of length 2. The first value in each array is the X-position,
	 * the second is the Y-position.
	 * @param x Current position X
	 * @param y Current position Y
	 * @return List of positions. 
	 */
	public List<int[]> getAdjacentPos(int x, int y)
	{
		ArrayList<int[]> result = new ArrayList<int[]>();
		for (FieldDirection dir: getDirs())
		{
			result.add(getPlayField().stepOne(x, y, dir));
		}
		return result;
	}
	
	/**
	 * Override this method to control the agent.
	 * 
	 * 
	 * This method currently always returns FieldDirection.UP. If you extend this class, you should override this method and change its
	 * behaviour to something more intelligent.
	 * @return FieldDirection.Up
	 */
	@Override
	public FieldDirection getInput()
	{
		return FieldDirection.Up;
	}
}