package jp.ac.nii.icpc2010.players;

import java.awt.Point;

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

/**
 * The AbstractPlayer class contains functions for querying the position of the players, 
 * details on the playfield and the remaining time per round.
 * <P>
 * This is the most basic class that all players must extend. Players also have the option to extend
 * BasePlayer, which contains some additional convenience functions.
 * <P>
 * Playfields use a 2D coordinate system. X-values increase from left to right, and Y-values increase from top to bottom:
 *<P> 
 * 
 * <pre>
 * 0----------------->x
 * |        ^
 * |        -y 
 * |    <-x    +x>
 * |        +y
 * |        v
 * |
 * v
 * y
 * 
 * </pre>
 * 
 * <P>
 * For an explanation of tron IDs and trail IDs, see the IPlayField class.
 * 
 * @see IPlayField
 */

public abstract class AbstractPlayer implements Player {
	/**
	 * ID of this player
	 */
	protected int id;
	/**
	 * Reference to the field information class.
	 */
	protected IPlayField playField;
	
	private long finishTime;
	
	//protected IBasicOptions basicOptions;
	
	/**
	 * 
	 * @param id the id of the tron
	 * @param playField a reference of the playfield
	 */
	public AbstractPlayer(int id, IPlayField playField){
		this.id = id;
		this.playField = playField;
		//this.basicOptions = basicOptions;
		/*
		if (!om.isTournamentMode())
			System.out.println("New Player. Class: " + getClass());
			*/
	}

	/**
	 * Internal framework use only. This function is called by PlayerRunProcess. 
	 * You are NOT allowed to call this method.
	 * @param finishTime
	 */
	final public void setFinishTime(long finishTime) {
		this.finishTime = finishTime;		
	}

	/**
	 * Internal framework use only. This function is called by PlayerManager. 
	 * You are NOT allowed to call this method.
	 * @param playField
	 */
	final public void setPlayField(IPlayField playField){
		this.playField = playField;	
	}

	/**
	 * Obtain the ID of this Tron.
	 * @return the unique id of this Tron
	 */
	protected int getId(){
		return this.id;
	}

	/**
	 * Obtain the current playfield.
	 * @return A reference to the current playfield
	 */
	protected IPlayField getPlayField(){
		return this.playField;
	}

	/**
	 * Obtain the configuration of the current game.
	 * @return
	 */
	/*
	protected IBasicOptions getGameOptions() {
		return basicOptions;
	}
	*/

	
	/**
	 * Obtain the number of players in the playfield.
	 * @return number of players (usually between 2 and 4)
	 */
	/*
	protected int getNumOfPlayers(){
		return this.playField.getNumOfPlayers();
	}
	*/

	/**
	 * Given a tron id, this method returns the x-coordinate of the specified tron.
	 * @param id the id of the tron
	 * @return the x-coordinate of the tron
	 */
	protected int getXOf(int id){
		return this.playField.getTrons().get(id).getX();
	}

	/**
	 * Given a tron id, this method returns the y-coordinate of the specified tron.
	 * @param id the id of the tron
	 * @return the y-coordinate of the tron
	 */
	protected int getYOf(int id){
		return this.playField.getTrons().get(id).getY();
	}
	
	/**
	 * Given a tron id, this method returns the position of the specified tron.
	 * Combines getXOf and getYOf
	 * @param id the id of the tron
	 * @return the position of the tron
	 */
	/*
	protected Point getPositionOf(int id){
		return new Point(getXOf(id), getYOf(id));
	}
	*/
	
	/**
	 * Get the X-coordinate of this player's tron.
	 * @return the current x-coord of this tron
	 */
	protected int getX(){
		return getXOf(this.id);
	}

	/**
	 * Get the Y-coordinate of this player's tron.
	 * @return the current y-coord of this tron
	 */
	protected int getY(){
		return getYOf(this.id);
	}
	
	/**
	 * Get the position of this player's tron.
	 * @return the current position of this tron(same a getX and getY)
	 */
	/*
	protected Point getPosition(){
		return getPositionOf(this.id);
	}
	*/
	
	/**
	 * Returns the object at a given position. The return value can be one of OBJECT_FREE, OBJECT_WALL,
	 * OBJECT_COIN. (Equivalent values IplayField.FIELD_FREE, etc., exist for your convenience).
	 * The return value can also be the ID of a tron trail.
	 * If you want to test this, use IPlayField.getTrons() to get the list of all trons, and then use objectTrailOf() to get the trail IDs. 
	 *  
	 * @param x X-coordinate of the position
	 * @param y Y-coordinate of the position
	 * @return 
	 */
	/*
	protected int getObjectAt(int x, int y){
		return this.playField.getObjectAt(x, y);
	}
	*/

	/**
	 * An empty square. Trons can move through walls.
	 */
	protected static final int OBJECT_FREE = IPlayField.FIELD_FREE;
	
	/**
	 * A wall. Trons cannot move through walls.
	 */
	protected static final int OBJECT_WALL = IPlayField.FIELD_WALL;
	
	/**
	 * A coin. Trons can move through coins, and they get points for doing so.
	 */
	protected static final int OBJECT_COIN = IPlayField.FIELD_COIN;
	
	/**
	 * Return the object ID of the trail of a given tron .
	 * @param id the id of the tron
	 * @return the object id of that tron's trail
	 */
	protected int getTrailIdOf(int id){
		return RefPlayField.playerTrail(id);
	}
	
	/**
	 * Get the Object ID of the trail of this player's tron
	 * @return the object trail of this tron
	 * @see getTrailIdOf
	 */
	protected int getTrailId(){
		return getTrailIdOf(this.id);
	}

	/**
	 * Inverse of getTrailIdOf.
	 * Return Tron ID of given Object ID
	 * @param trail the trail id
	 * @return the id of the corresponding tron
	 * @see getTrailIdOf
	 */
	protected int getTronIdOf(int trail){
		return RefPlayField.playerId(trail);
	}
	
	/**
	 * Obtain the remaining time to compute the current input.
	 * 
	 * It is recommended that you use the following form with this method:
	 * 
	 * 
	 * while (getRemainingTime() > 10) {
	 * 	 compute();
	 *   ...
	 * }
	 * 
	 * For a value >= 10, since it is difficult to measure values less than 10 ms on some
	 * operating systems. You may have to use 15 or 20, but more should not be necessary.
	 * 
	 * @return the remaining time in milliseconds
	 */
	protected int getRemainingTime() {
		return (int) (finishTime - System.currentTimeMillis());
	}
	
	/**
	 * Obtain the number of remaining turns in this round. If this returns zero, the current turn is the final one.
	 * @return
	 */
	/*
	protected int getRemainingTurns() {
		return getGameOptions().getMaxTurns() - playField.getTurn() - 1;
	}
	*/
	
	/**
	 * Obtain the number of remaining rounds in this match. If this returns zero, the current round is the final one.
	 */
	/*
	protected int getRemainingRounds() {
		return getGameOptions().getGameRounds() - playField.getRound() - 1;
	}
	*/
}
