package Cluedo.Game;
import java.util.Vector;
import Cluedo.API.Binding;

/**
 * This like a real-life model of the Cluedo board containing all the
 * players, rooms, weapons etc.
 * <p>
 *   
 * @author alex
 */
public class Board
		implements Cluedo.API.Persistant {
		
	private Position lnkSquare;
	/** 
	 * Instance of the "helper" class which holds all of the nodes
	 * in the board and all the rooms, it also deals with getting from
	 * one position to another. 
	 */
	public Map map = new Map();
	
	/**
	 * Set limit for players.
	 */
	final static public int MAX_PLAYERS = 6;
	/**
	 * Index to the players vector for finding the current player object.
	 */
	private int currentPlayer = 0;

	/**
	 * Whether the game has been played to the point where game-play should stop.
	 */
	boolean gameFinished = false;

	/**
	 * Stores players in the order they were entered at the beginning
	 * of the game.
	 */
	Vector players = new Vector();
	
	/**
	 * Type of game rules to be used for the game, 0 = regular, 1 = special rolling ( 2x6 == go to any room).
	 * 
	 */
	public int gameType = 0;

	
	/**
	 * Currently not used for any useful purpose.
	 * 
	 */
	Vector weapons = new Vector();			// Vector of weapons
	
	public int diceThrow1 = -1;
	public int diceThrow2 = -1;
	
	/**
	 * Card pack containing <b>all</b> of the cards in the game.
	 */
	private CardCollection cardPack = new CardCollection();
	
	/**
	 * Comment for <code>envelope</code><br>
	 * Holds the 3 secret cards which describe the murder, the weapon used and the murder scene.
	 */
	private CardCollection envelope = new CardCollection();

	/**
	 * Creates a new board.<p>
	 *
	 * No parameters need to be passed when creating a board.
	 *
	 */
	public Board() {

	}
	
	public int getDiceValue(int die) {
		if(die == 1)
			return 	diceThrow1;
		return 	diceThrow2;
	}
	
	public void rollDice() {
		diceThrow1 = (int) (Math.random() * 6)+1;
		diceThrow2 = (int) (Math.random() * 6)+1;
	}

	/**
	 * Only ever used to start or restore a game
	 * 
	 * @param player
	 */
	synchronized public void setCurrentPlayer(int player) {
		currentPlayer = player;
	}
	public boolean getGameFinished() {
		return gameFinished;
	}

	public void setGameFinished(boolean gameFinished) {
		this.gameFinished = gameFinished;
	}
	
	/**
	*  Set the last player's moves remaining to 0, increment to the next player and reset the rolled and moved guards.<p>
	*  Attention:<br>
	*  There is potential for an infinite loop here, but this will never occour due to checking. 
	*
	*/
	public synchronized void nextPlayer() {
		getCurrentPlayer().setMovesRemaining(0);
		
		if(++currentPlayer >= players.size()) {
			currentPlayer = 0;
		}
		
		getCurrentPlayer().setRolled( false );
		getCurrentPlayer().resetMovedStatus();
		
	}
	
	public int countInteractivePlayers() {
		int count = 0;
		for(int i = 0; i < getPlayers().size(); i++)
			if( ((Player)getPlayers().elementAt(i)).isInteractive() == true)
					count++;
					
		return count;
	}



	/**
	 * Searches the players list for a specified player's name and then returns a pointer to it.
	 * @param realName
	 * @return
	 * 		returns null if the player wasn't found or a pointer to the object
	 */
	public Player getPlayer(String realName) {
		Player pointer = null;
		
		for (int i = 0; i < players.size(); i++) {
			if(((Player)players.elementAt(i)).getRealName().equals(realName)) {
					pointer = ((Player)players.elementAt(i));
					break;
			}
		}
		return pointer;
	}
	

	/**
	 * Return a player object by it's character name
	 * @param charName
	 * @return the player object playing as the specified character
	 */
	public Player getCharacter(String charName) {
		Player pointer = null;
		
		for (int i = 0; i < players.size(); i++) {
			if(((Player)players.elementAt(i)).getCharacter().equals(charName)) {
					pointer = ((Player)players.elementAt(i));
					break;
			}
		}
		return pointer;
	}

	
	public int countPlayers() {
		return players.size();
	}
	/**
	 * Returns all players on the board.
	 * @return
	 */
	public Vector getPlayers() {
		return players;
	}

	/**
	* Find the shortest path from a position x to y.
	*
	* @return
	*          -1 if the path is not possible or n where n is the amount of moves required to move to
	*          position currentPosition
	*/
//	public int shortestPathTo(Position currentPosition, Position targetPosition, int maxDepth) {
//		//return map.lengthToPath( currentPosition, targetPosition, maxDepth );
//		return map.legalLengthToPath( currentPosition, targetPosition, maxDepth );
//	}
	
	/**
	* Initalise the board's map with some data
	*/
	public void createSquares(Vector squares) {
		map.positions = squares;
	}

	/**
	* Bind the map's nodes together to from links.
	*
	*/
	public void createBindings(Vector bindings) {
		for(int i = 0; i < bindings.size(); i++) {
			Binding binding = (Binding)bindings.elementAt(i);
			map.bind(binding.getSource(), binding.getTarget());
			//bindSquare(binding.getSource(), binding.getTarget());
		}		
	}


	/**
	* Get a pointer to the player currently in turn.
	*/	
	synchronized public Player getCurrentPlayer() {
		return (Player)players.elementAt(currentPlayer);
	}

	/**
	* Get an index of the player currently in turn in relation to the Vector of players.
	*/	
	public int getPlayerIndex(Player target) {
		for(int i = 0; i < players.size(); i++)
			if(target == players.elementAt(i))
				return i;
		return -1;
	}


	/**
	 * Inserts a player onto the board.
	 * @param newPlayer
	 * @return
	 */
	public boolean addPlayer(Player newPlayer) {
		// Guard here ( whether another player may be added)
		if(countPlayers() < MAX_PLAYERS) {
			System.out.println("[Game:Board] Adding player ["+newPlayer.getRealName()+",t="+newPlayer.getPlayerType()+"] at " + countPlayers() + " reference ["+newPlayer+"]");
			players.add(newPlayer);
			return true;
		}
		else
			return false;
	}

	public String toXML(){
		return toXML("default");
	}
	
	/**
	 * 
	 * Function exists in all persistant classes to output
	 * releveant XML data for a disk-dump.
	 * 
	 */
	public String toXML(String map){
		StringBuffer buffer = new StringBuffer();

		buffer.append(
				"<game type=\"" + gameType +  "\">\n" +
				"  <currentplayer>" +
				currentPlayer + 
		              	"</currentplayer>\n" +
    				"  <envelope>\n" +
				envelope.toXML() +
    				"\n  </envelope>\n" +
		  		"  <map>" + map + "</map>\n"
		  		);

		buffer.append("  <players>\n");
		for(int i = 0; i < players.size(); i++) {
			buffer.append( ((Player) players.elementAt(i)).toXML() );
		}
		buffer.append("\n  </players>\n");

		buffer.append("</game>\n");
		
		return buffer.toString();
	}

	/**
	 * @return Returns only a link/pointer to the cardPack.
	 */
	public CardCollection getCardPackLink() {
		return cardPack;
	}
	/**
	 * @return
	 * a new CardCollection containing new copies of all the cards in the board.
	 * 
	 */
	public CardCollection getCardPack() {
		CardCollection newCardPack = new CardCollection();
		for(int i = 0; i < cardPack.countCards(); i++) {
			newCardPack.insertCard(new Card(cardPack.cardAt(i).getName(), cardPack.cardAt(i).getType()));
		}
		return newCardPack;
	}
	
/*	public CardCollection getCardPack(String type) {
		CardCollection newCardPack = new CardCollection();
		for(int i = 0; i < cardPack.countCards(); i++) {
			if(cardPack.cardAt(i).getType().equals(type))
				newCardPack.insertCard(new Card(cardPack.cardAt(i).getName(), cardPack.cardAt(i).getType()));
		}
		return newCardPack;
	}*/


	public Vector getCardList(String type) {
		Vector cardList = new Vector();
		for(int i = 0; i < cardPack.countCards(); i++) {
			if(cardPack.cardAt(i).getType().equals(type))
				cardList.add(cardPack.cardAt(i).getName());
		}
		return cardList;
	}
	/**
	 * @param cardPack The cardPack to set.
	 */
	public void setCardPack(CardCollection cardPack) {
		this.cardPack = new CardCollection();
		for(int i = 0; i < cardPack.countCards(); i++) {
			this.cardPack.insertCard(new Card(cardPack.cardAt(i).getName(), cardPack.cardAt(i).getType()));
		}
		System.out.println("[Board:setCardPack] loaded "+this.cardPack.countCards() + " unique cards.");
	}
	/**
	 * @return Returns the envelope.
	 */
	public CardCollection getEnvelope() {
		return envelope;
	}
	/**
	 * @param envelope The envelope to set.
	 */
	public void setEnvelope(CardCollection envelope) {
		this.envelope = envelope;
	}
    /**
     * @return Returns the gameType.
     */
    public int getGameType() {
        return gameType;
    }

    /**
     * @param gameType The gameType to set.
     */
    public void setGameType(int gameType) {
        this.gameType = gameType;
    }
}
