package Cluedo.AI;

import Cluedo.API.*;
import Cluedo.Game.*;
import Cluedo.Controller.*;
import java.util.Vector;
import java.util.StringTokenizer;
import java.lang.StringBuffer;

/**
 * AI interacts with the game by listening to messages from the GameController
 * and then sending messages back to make changes.<p>
 * 
 * First Created on Dec 12, 2004
 * 
 * @author Alex Ellis
 * 
 */
public class ComplexAI implements 
	Runnable, Cluedo.API.AIContract {

	static int sleepPeriod = 200;
	
	/**
	 * This identifies the AI, so it can decide when it's his turn to respond to input.
	 */
	String myName = "";
	
	/**
	 * If set to true the AI's run() function will end.
	 */
	boolean dead = false;
	
	/**
	 * Stores all incoming messages in a queue. This AI is a thread, therefore it receives messages into
	 * a queue and parses them from its run() function.
	 */
	Vector inbox = new Vector();
	
	/**
	 * Current behaviour of the AI
	 */
	String motive = "";
	/**
	 * The room the AI wants to get to, intermediate Squares aren't stored here.
	 */
	Position destination = null;

	/**
	 * Indicates a disproof to the AI's suggestion was received.
	 */
	boolean disproofReceived = true;
	
	/**
	 * Used to store a disproof to the AI's suggestion.
	 */
	Message incomingDisproof = null;
	
	/**
	 * List of motives that an AI may take.
	 *
	 */
	String [] motives = { "findRoom", "walkToRoom",};
	
	/**
	 * CluedoListener acts as an inbox to receive messages from the GameController and will decide
	 * whether it needs to update any information.
	 *
	 */
	CluedoListener cluedoListener = new CluedoListener() {
		/**
		 * Messages here are received from the TextHandler and result in some action
		 * being taken locally in this View, commands called from here are located in
		 * an inner-class called LocalEvents.<br>
		 * The return value from the local event is outputted to the user.
		 *
		 */
		public void localMessage(Cluedo.API.Message message) {}
	
		/** 
		* Receives messages from the GameController and handles them in GameEvents
		*
		*/
		public void notifyAlert(Message message) {
			String output = "";
			if(debugLevel >= 2)
				outputMessage("[ComplexAI:notifyAlert] [" + message.type + "] '" + message.data + "'\n");

			
		
			// One case where we need a little help..
			if(disproofReceived == false) {
				if(message.type.equals("disproofAlert")) {
					disproofReceived = true;
					incomingDisproof = message;
					inbox.add(message);
					
				}

			}
			else
				inbox.add(message);
			
		}
        	
        	
		/* End code that handles the interaction of the GUI
		 * 
		 */
		public CluedoListener getCluedoListener() {
			return this;
		}
	
	};

	public CluedoListener getCluedoListener () {
		return cluedoListener;
	}


	private void sleepFor(int interval) {
		try {
			Thread.sleep(interval);
			outputMessage("\nPausing execution.. ("+interval+") ms\n");
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Construct a new simple AI, and bind itself to the Controller to receive game messages.
	 *
	 * @param args Does nothing
	 * @param gameController A pointer/link to the Controller.
	 * @param AIName Name that uniquely identifes this AI player
	 */
	public ComplexAI (String args, GameController gameController, String AIName) {
		//super(args);
		myName = AIName;
		this.gameController = gameController;
		System.out.println("[ComplexAI:] GC="+this.gameController.toString() + ", NAME=" + myName);
		bindAI();
		motive = "findRoom";
	}

	/**
	 * AI will bind its CluedoListener to the gameController such that it will receive game messages.
	 */
	public void bindAI() {
		System.out.println("[ComplexAI2:BindAI] binding to listener " + getCluedoListener());
		try {
			gameController.bindListener(getCluedoListener());
		}
		catch(Exception Error) {
			System.out.println("[ComplexAI2:BindAI] can't bind");
		}
	}
	
	/**
	 * If called thread will exit run() method. Called when game ends.
	 */
	synchronized public void die() {
		dead = true;
	}
	
	/**
	 * Main AI loop, will stop if die() is called.
	 *
	 * Removes message from inbox and processes it in handleMessage
	 *
	 */
	public void run () {
		while( dead == false ) {
			try {

				try {
					Thread.sleep(100);
				}
				catch (InterruptedException e) {
					e.printStackTrace();
				}

				if(inbox.size() > 0) {
					while(inbox.size() > 0) {
						Thread.sleep(10);
						handleMessage(((Message)inbox.firstElement()));	
						inbox.remove(inbox.firstElement());
					}
				}
			}
			catch (InterruptedException e) {
				
			}
		}
	}
	
	/**
	 * A message is taken from the queue and given to the AI, he decides what to do about that.
	 * @param message Message from the Q.
	 *
	 */
	 public void handleMessage(Message message) {
		String output = "";

		if(debugLevel > 5)
			outputMessage("handleMessage " + message.type);
		if(gameController.getBoard().getGameFinished() == false) {
			//if(message.type.equals("commenceGame"))
				//message.type = "nextPlayer";
				
			if(message.type.equals("newRoll")) {
                	
			}
			if(message.type.equals("commenceGame")) {
				message.type = "nextPlayer";
				sleepFor(4000);
			}
			else if(message.type.equals("newEnvelope")) {
                	
			}
			else if(message.type.equals("suggestionAlert")) {
				if(message.data.equals(myName)) {
					
					handleSuggestionAlert(message);
				}
				
			}
			else if(message.type.equals("disproofAlert")) {
					outputMessage("\nHeard a disproofAlert ...\n");
					if(disproofReceived == false) {
						DisproofAlert d = ((DisproofAlert)message.parameters.get(0));
						deductFromDisproof(d);
					}
					disproofReceived = true;
			}
			else if(message.type.equals("nextPlayer")) {
				if(gameController.getBoard().getCurrentPlayer().getRealName().equals(myName)) {
//					outputMessage(buildCards());
					outputMessage(" ********************************************");
					outputMessage(" **** "+gameController.getBoard().getCurrentPlayer().getRealName()+" ****");
					outputMessage(" ********************************************");
					takeTurn();
//					outputMessage(buildCards());
                	
				}
			}
			else if(message.type.equals("moveAlert")) {
                	
			}		
		}
		else {
			if(dead == false) {
				outputMessage("Game is over and i'm quitting..");
				die();
			}
		}
		if(!output.equals(""))
			outputMessage(output);
	}

	/**
	 * @param d
	 */
	private void deductFromDisproof(DisproofAlert d) {
		if(!d.getDisprovedBy().equals("") && !d.getCardShown().equals("")) {
			Card possible = (Card)gameController.getBoard().getCurrentPlayer().getDetectivePad().getCardList().cardByName(d.getCardShown());
			
			System.out.println("\t\tPossible card: " +possible.getName() + " already eliminated? " + possible.isEliminated());
			
			if(possible.isEliminated() == false) {
				boolean s = isEliminated(d.originalSuggestionAlert.who.getName());
				boolean w = isEliminated(d.originalSuggestionAlert.what.getName());
				boolean r = isEliminated(d.originalSuggestionAlert.where.getName());
				
				System.out.println("\t\tBooleans: "+s+" " + w + " " + r);

				if(s && w)
					registerSuggestion(possible);
				else if(s && r)
					registerSuggestion(possible);
				else if(w && r)
					registerSuggestion(possible);

			}
		}
	
	}


	/**
	 * @param name
	 * @return
	 */
	private boolean isEliminated(String name) {
		Player me = gameController.getBoard().getCurrentPlayer();
		
		return me.getDetectivePad().getCardList().cardByName(name).isEliminated();
	}


	/**
	 * Manually set the AI name, see constructor
	 */
	public void setMyName(String myName) {
		this.myName = myName;
	}

	/**
	 * Only prints a debug
	 */
	public void initaliseAI() {
		System.out.println("[ComplexAI2:initaliseAI] Initalised "+myName+".");
	}

	
	/**
	 *  Start code that handles the interaction of the AI
	 * 
	 */
	
	/** The game controller for this View */
	GameController gameController = null;
	/** The parser for text input from this View */
	Cluedo.GUI.TextParser textParser;
	/**  The handler for input from the parser on this View */
	Cluedo.GUI.TextEventHandler textHandler = new Cluedo.GUI.TextEventHandler(this);
	/** Create a game config class to read in and sort out what the new game will be like */
	CluedoConfig gameConfig = null;
	int debugLevel = 0;
	

	/**
	 * Debug function makes clear which AI is "talking"
	 */
	synchronized void outputMessage(String output ) {
		StringTokenizer lines = new StringTokenizer(output, "\n");
		while ( lines.hasMoreElements() == true ) {
			System.out.println("[AI:"+myName+"] " + lines.nextToken() );
		}
		System.out.println();
	}


	/**
	 * Figures out what to do with a message of type ='suggestionAlert'.<p>
         *
	 * Searches cards in suggestionAlert until it finds one it has in its
	 * set of cards, then composes disproofAlert alert as a reply to the
	 * message and relays this through the controller.
	 *
	 */
	private void handleSuggestionAlert(Message message) {

		outputMessage("\nHeard a suggestionAlert directed at me...\n");
		Message disproofAlert = new Message();
		String chosenCard = "";
		
		disproofAlert.type = "disproofAlert";
		
		SuggestionAlert alert = (SuggestionAlert)message.parameters.elementAt(0);
		Player me = gameController.getBoard().getPlayer(myName);
		
		
		if(me.getCards().containsCard(alert.getWhat()))
			chosenCard = alert.what.getName();
		else if(me.getCards().containsCard(alert.getWhere()))
			chosenCard = alert.where.getName();
		else if(me.getCards().containsCard(alert.getWho()))
			chosenCard = alert.who.getName();
			
		// If this happens, there is a bug in the system.
		if(chosenCard.equals(""))
			outputMessage("I didn't have a card to disprove with and yet I was asked to disprove!");

		disproofAlert.parameters.add(new DisproofAlert( chosenCard, alert, myName ));

		gameController.relayMessage(disproofAlert);
		
		outputMessage("\nRelay of disproofAlert... Ok\n");

	}

	/**
	 * Clean up anything and notify gameController to allow next player to play.
	 *
	 */
	synchronized public String endGo() {
		String output = "";

		gameController.directCommand("nextPlayer", null);
			
		return output;
	}

	/**
	 * Function for debug, returns string status of all cards.
	 *
	 * @return String containing status of all cards.
	 */
	synchronized public String buildCards() {
		StringBuffer buffer = new StringBuffer();
		
		Player me = gameController.getBoard().getPlayer(myName);
		CardCollection myCards = me.getCards();
		CardCollection myDCards = me.getDetectivePad().getCardList();
		
		for(int i = 0; i < myDCards.countCards(); i++) {
			buffer.append("- " + myDCards.cardAt(i).getName() +"\t" + myDCards.cardAt(i).isEliminated()+"\n");
		}
		
		
		return buffer.toString();
	}

	/**
	 * Called if after a nextPlayer alert if getBoard().getCurrentPlayer().getRealName() == me<p>
	 *
	 * AI player will decide what he needs to do on this turn.
	 *
	 */
	synchronized public void takeTurn() {
		String output = "";
		outputMessage("takeTurn..");

		/**
		 * Firstly make a small thinking pause
		 */
		sleepFor(sleepPeriod);

			
		/**
		 * Get the bits we need for the turn.
		 */
		Player me = gameController.getBoard().getPlayer(myName);
		CardCollection myCards = me.getCards();
		CardCollection myDCards = me.getDetectivePad().getCardList();
			
			
		/**
		 * Check if an accusation should be made.
		 *
		 */
		if(shouldAccuse() == true) {
			boolean result = makeAccusation();
			System.out.println("My accusation was " + result);
			if(result == false) {
				outputMessage("I LOST the game");
			}
			else {
				outputMessage("I won the game");
				gameController.directCommand("winGame", null);

			}
		}
		
		int unEliminatedCards = 0;
		for(int i = 0; i < myDCards.countCards(); i++)
			if(myDCards.cardAt(i).isEliminated() == false)
				unEliminatedCards++;
		output += "I have to eliminate " + unEliminatedCards + " cards.\n";

		outputMessage(output);

//		sleepFor(sleepPeriod);
		
		/**
		 * If not in the process of moving to a room, find a room that we want to visit.
		 */
		if(motive.equals("findRoom")) {

			destination = findRoom();

			motive = "walkToRoom";
		}
		
		sleepFor(sleepPeriod);
			
		/**
		 * If I'm supposed to find my way to the room then I'll start doing that.
		 * 
		 */
		if(motive.equals("walkToRoom")) {
			if(destination.equals(me.getPosition())) {
				destination = findNeighbour(me.getPosition());
			}			
			if(destination != null) {
				walkToRoom(destination);
			}
			else
				motive = "findRoom";

		}
		sleepFor(sleepPeriod);
		

		/**
		 * If it's possible to make a suggestion after the turn, then do so.
		 *
		 */		
		if(me.canSuggestHere() == true && me.canSuggest() == true) {
    			output += suggest();	
		}
	

		/**
		 * Now end the go and let the next player have his go.
		 */
		endGo();
			
		}
		
		
	/**
	 * Check for a free position in one of the neighbouring nodes.
	 *
	 * @return null if node doesn't have free neighbouring node, otherwise return first found node
	 */
	Position findNeighbour(Position node) {
		Position found = null;
		
		for(int i = 0; i < node.getNeighbours().size(); i++)
			if(
				(node.getNeighbours().elementAt(i)) instanceof Room == true
				||
				((Position)node.getNeighbours().elementAt(i)).isOccupied() == false) {

				found = (Position)node.getNeighbours().elementAt(i);
			}

		return found;
		
	}
	
	/**
	 * Walk to destination.<p>
	 * 
	 * Try to get to the destination by whatever means neccessary, make suggestions along the
	 * way wherever permitted.
	 * 
	 * 
	 */
	synchronized public void walkToRoom (Position destination) {
		String walkInfo = "";
		String output = "";
		
		Player me = gameController.getBoard().getPlayer(myName);
		CardCollection myCards = me.getDetectivePad().getCardList();
		
		
		/**
		 * Call the roll-dice function to find out how many squares we can move
		 */
		int allowed_moves = rollDice();
		
		if(gameController.getBoard().getPlayer(myName) != gameController.getBoard().getCurrentPlayer()) {
			System.out.println("+ I'm not feeling myself!! ");
			
			System.out.println("+ getCurrentPlayer() " + gameController.getBoard().getCurrentPlayer() + " "  + gameController.getBoard().getCurrentPlayer().getRealName());
			System.out.println("+ getPlayer("+myName+") " + gameController.getBoard().getPlayer(myName) + " "  + gameController.getBoard().getPlayer(myName).getRealName());
		}

		System.out.println("+ Dice: " + allowed_moves);
		
		walkInfo += "Let's see, what the best path to the "+destination.getID()+" is..\n";
		
		long start = (new java.util.Date()).getTime();
		/**
		 * Check if there is a legal path to the route 
		 */
		//Vector v = gameController.getBoard().map.illegalRouteToPath(me.getPosition(), destination, 20);
		
		Vector v = gameController.getBoard().map.routeTo(me.getPosition(), destination);
		
		long stop = (new java.util.Date()).getTime();
		
		outputMessage("Finding shortest path: "+me.getPosition().getID()+" to "+destination.getID()+" ("+(stop-start)+"ms)");
		
		if(v.size()-1 == -1) {
			destination = null;
			Vector rooms = gameController.getBoard().map.getNodes("rooms");

			for(int i = 0; i < rooms.size(); i++) {
				v = gameController.getBoard().map.routeTo(me.getPosition(), (Position) rooms.elementAt(i));
				System.out.println("Trying to get another room..");
				if(v.size()-1 == -1) {
					continue;
				}
				else {
					destination = (Position) rooms.elementAt(i);
					break;
				}


			}
			if(destination == null)
				return;

		}

		walkInfo += "Hmm.. about "+(v.size()-1)+" moves, i'd say..\n";
		walkInfo += "Starting at: ";
		outputMessage(walkInfo);
		
		for(int i = 0; i < v.size(); i++)
			walkInfo += " -> " + ((Position)v.elementAt(i)).getID();
		
		walkInfo += "\nWell, I'm allowed to make " + allowed_moves+" moves, let's see about that\n";

		/**
		 * 
		 * Not able to move in one go!
		 * 
		 * If the size of the route is longer than our goes, we just go as far as possible
		 * 
		 */
		if (v.size() > allowed_moves+1 ) {
			//output += "\nSlowly slowly - catchy monkey\n";
			outputMessage("\nSlowly slowly - catchy monkey\n");
			Room stopOver = null;
			int i = 0;
			for(i = 1; i < allowed_moves+1; i++)
				if( v.elementAt(i) instanceof Room) {
					stopOver = ((Room)v.elementAt(i));
					break;
				}
				
			if(stopOver == null) {
//				output += " - Moving to "+((Position)v.elementAt(allowed_moves)).getID() +"";
				
				// TODO
				System.out.println("May move " + allowed_moves);
				System.out.println("routeTo.size() " + v.size());

				if(v.size() > 0) {
					System.out.println("routeTo src=" + ((Position)v.firstElement()).getID());
					System.out.println("routeTo dest=" + ((Position)v.lastElement()).getID());
				}
				
				movePlayer(((Position)v.elementAt(allowed_moves)), v, v.size());
				//movePlayer(((Position)v.elementAt(allowed_moves)), v.size());

			}
			else {
				output += "- Room in my path, must stop here: " +stopOver.getID() + "\n";
				movePlayer(stopOver, v, i+1);

			}
		}
		/**
		 * 
		 * ABLE to move in one go!
		 * 
		 * If the size of the route is less than or equal to our goes, we just go to the destination
		 * 
		 */
		else {
			output += "\nEnough moves remaining to get to target.\n";
			//end go
			Room stopOver = null;
			int i = 0;
			for(i = 1; i < v.size(); i++)
				if( v.elementAt(i) instanceof Room) {
					stopOver = ((Room)v.elementAt(i));
					break;
				}
				
			if(stopOver == null) {
//				output += " - Moving to "+destination.getID() +"";
				movePlayer(destination, v, v.size());

			}
			else {
				output += "- Room in my path, must stop here: " +stopOver.getID() + "\n";
				movePlayer(stopOver, v, i+1);

			}
			motive = "findRoom";
		}


		/** 
		 * If we're in the destination, then we can make the suggestion we wanted to make.
		 * 
		 */
		if(me.getPosition() == destination ) {
			motive = "findRoom";
			output += "I got to my destination!!\n";
		}
        	
		sleepFor(sleepPeriod);
        	
		/** 
		 * If we're not yet in the destination, but in a room, why not make a suggestion here?
		 * 
		 */
		if(me.getPosition() instanceof Room) {
			output += "Erm.. I could make a quick suggestion here probably.\n";
        	
			outputMessage(output);
		}
	
	}

	synchronized public Position findRoom() {
		return findRoom(false);
	}
	/**
	 * Finds a room for the AI to go to.<p>
	 * <li> - Firstly he goes to all uneliminated rooms,
	 * <li> - if all rooms are eliminated he picks a room at random.
	 * 
	 * @return a chosen destination
	 */
	synchronized public Position findRoom(boolean randomFirst) {
		Position destination = null;
		
		Player me = gameController.getBoard().getPlayer(myName);
		CardCollection myDCards = me.getDetectivePad().getCardList();
		
		Vector rooms = gameController.getBoard().map.getNodes("rooms");
		
		/**
	 	* Find the first room which isn't eliminated.
	 	*/
		if(randomFirst == false) {
			for(int i = 0; i < rooms.size(); i++)
				if(myDCards.cardByName(((Room)rooms.elementAt(i)).getID()).isEliminated() == false) {
					destination = ((Room)rooms.elementAt(i));
				}
		}


		/**
		 * If all the rooms were eliminated, then just pick one at random
		 */
		if(destination == null || randomFirst == true) {
			int index = (int) (Math.random() * (rooms.size()));
			outputMessage("index "+index);
			destination = ( (Room)rooms.elementAt( index ) );
		}
		
		outputMessage("My aim is: " + destination.getID() + ".\n");
		
		return destination; 
	}

		
	/**
	 * Finds if the AI should make an accusation.
	 * 
	 * Makes an accusation if there is 1 weapon, suspect, and room left uneliminated.
	 * 
	 * This method can be bypassed if a suggestion is not disproved and the player has
	 * none of the cards, this means inherently that the player has found the secret
	 * cards. 
	 * 
	 * @return accusation decision
	 */
	synchronized public boolean shouldAccuse() {
		boolean result = false;
		
		Card weapon = null;
		Card suspect = null;
		Card room = null;
		
		Player me = gameController.getBoard().getPlayer(myName);
		CardCollection myCards = me.getCards();
		CardCollection myDCards = me.getDetectivePad().getCardList();
		
		int numberOfWeapons = 0;
		int numberOfRooms = 0;
		int numberOfSuspects = 0;
		
		for(int i = 0; i < myDCards.countCards(); i++) {
			if(myDCards.cardAt(i).isEliminated() == false) {
				if(myDCards.cardAt(i).getType().equals("Weapon"))
					numberOfWeapons++;
				else if(myDCards.cardAt(i).getType().equals("Suspect"))
					numberOfSuspects++;
				else if(myDCards.cardAt(i).getType().equals("Room"))
					numberOfRooms++;
			}
		}
		
		result = (numberOfWeapons == 1 && numberOfSuspects == 1 && numberOfRooms == 1);
	
		return result;
	}

	/**
	 * Find 3 cards to use in an accusation then call the accuse() function.
	 * 
	 */
	synchronized public boolean makeAccusation() {
		Card weapon = null;
		Card suspect = null;
		Card room = null; 
		
		Player me = gameController.getBoard().getPlayer(myName);
		CardCollection myCards = me.getCards();
		CardCollection myDCards = me.getDetectivePad().getCardList();
		
		for(int i = 0; i < myDCards.countCards(); i++) {
			if(myDCards.cardAt(i).isEliminated() == false) {
				if(weapon == null && myDCards.cardAt(i).getType().equals("Weapon"))
					weapon = myDCards.cardAt(i);
				else if(weapon == null && myDCards.cardAt(i).getType().equals("Suspect"))
					suspect = myDCards.cardAt(i);
				else if(room == null && myDCards.cardAt(i).getType().equals("Room"))
					room = myDCards.cardAt(i);
			}
		}
		return accuseCorrect(suspect, weapon, room);
	}
	
	/**
	 * Check if an accusation is correct
	 */
	synchronized public boolean accuseCorrect(Card suspectCard, Card weaponCard, Card roomCard) {
			boolean result = false;
			
			if(gameController.getBoard().getEnvelope().containsCard(weaponCard)  &&
					gameController.getBoard().getEnvelope().containsCard(roomCard) &&
					gameController.getBoard().getEnvelope().containsCard(suspectCard)
					)
			{
				result = true;
			}
			else {
				result = false;
			}
			
			return result;
	}
		
		
		
	public String suggest() {
		Card suspect = null;
		Card weapon = null;
		String output = "";
		Player me = gameController.getBoard().getPlayer(myName);
		CardCollection myDCards = me.getDetectivePad().getCardList();
		
		
		/** List of all suspects who haven't been eliminated yet **/ 
		Vector possSuspects = new Vector();
		/** List of all weapons which haven't been eliminated yet **/
		Vector possWeapons = new Vector();

		/**
		 * Search through detective pad to find cards which aren't eliminated.
		 */
		for(int i = 0; i < myDCards.countCards(); i++)
			if(myDCards.cardAt(i).getType().equals("Suspect") && myDCards.cardAt(i).isEliminated() == false)
				possSuspects.add(myDCards.cardAt(i));
			/**
			 * Search through detective pad to find cards which aren't eliminated.
			 */
		for(int i = 0; i < myDCards.countCards(); i++)
			if(myDCards.cardAt(i).getType().equals("Weapon") && myDCards.cardAt(i).isEliminated() == false)
				possWeapons.add(myDCards.cardAt(i));

		/**
		 * Generate a random number then use that index to access the list of suspects.
		 */
		suspect = (Card)possSuspects.elementAt(  (int) (Math.random() * possSuspects.size())) ;
		/**
		 * Generate a random number then use that index to access the list of weapons.
		 */
		weapon = (Card)possWeapons.elementAt( (int) (Math.random() * possWeapons.size())) ;
		
		/**
		 * Get the room for the suggestion by the room the player is currently standing in.
		 */
		Card room = gameController.getBoard().getCardPack().cardByName(me.getPosition().getID());
		
		
		Vector disproofInfo = findDisproveCandidate(suspect, weapon, room);
		
		Player playerCandidate = (Player)disproofInfo.firstElement();


		output += "- I suspect " + suspect.getName() + " of using the " + weapon.getName() + " in the " + me.getPosition().getID()+".\n";
		outputMessage(output);
		moveDefendant(suspect);
		
		
		// Send out an alert for the suggestion
		
		if(playerCandidate != null) {
			disproofReceived = false;
			Message suggestionAlertMessage = new Message();
			Vector params = new Vector();
			params.add(new SuggestionAlert(suspect, weapon, room, me.getRealName()) );
			suggestionAlertMessage.parameters = params;
			suggestionAlertMessage.type = "suggestionAlert";
			suggestionAlertMessage.data = playerCandidate.getRealName();
			
			gameController.relayMessage(suggestionAlertMessage);
		}
		else {
			outputMessage("Nobody could disprove");
			outputMessage(" i have weap? " + myDCards.containsCard(weapon) + "\n i have room? " +  myDCards.containsCard(room) + "\n i have suspect? " + myDCards.containsCard(suspect));
			
		}
		
		// Wait for a reply from the suggestion
		
		outputMessage("# In waiting loop");
		while ( disproofReceived == false ) {
			try {
				Thread.sleep(100);
			}
			catch(Exception e) {
				
			}
		}
		outputMessage("# Exit waiting loop");
		outputMessage("Start disproof decomposition");

		DisproofAlert disproofAlert = (DisproofAlert)incomingDisproof.parameters.firstElement();
		SuggestionAlert originalSuggestionAlert = disproofAlert.getOriginalSuggestion();
		
		String cardShown = disproofAlert.getCardShown();
		
		System.out.println(" - Card shown: "+cardShown );


		// Figure out what to do after the suggestion

		if(cardShown.equals("")) {
			if(me.getCards().containsCard(suspect) == false && 
					me.getCards().containsCard(weapon) == false &&
						me.getCards().containsCard(room) == false) {

				output+= "\nI secretly won because I know nobody else has the cards.\n";

		// At this stage we should check if we have the cards.. if not.. then we must know that we've found the correct cards.
				if( accuseCorrect(suspect, weapon, room) == true) {
					outputMessage("My accuse was true");
					Vector parameters = new Vector();
					parameters.add(me);
					gameController.directCommand("winGame", parameters );
				}
				else {
					outputMessage("My accuse was WRONG..!");
				}
			}
			else
				output += "Unable to disprove suggestion because I had the cards myself.";
		}
		else {
			outputMessage("Yeah, I made a good suggestion.");

		// We got a good response from the suggestion, one card has been eliminated.
			registerSuggestion(myDCards.cardByName(cardShown));
		}
		
		updateSuggestStatus();

		// Reset the waiting flag
		disproofReceived = false;
		return output;
	}
	
	public void updateSuggestStatus() {
		Vector param = new Vector();
		param = new Vector();
		param.add(gameController.getBoard().getPlayer(myName));
		gameController.directCommand("markSuggestMade", param);
	}

	public void registerSuggestion(String shown) {
		Vector param = new Vector();
		param.add(shown);
		gameController.directCommand("eliminateCard", param);
	}

	private void registerSuggestion(Card shown) {
		registerSuggestion(shown.getName());
	}
				
				
	/**
	 * Performs the move of the player from current position to "target".
	 * 
	 */
	synchronized public void movePlayer(Position target, Vector path, int amount) {
		String output = "";
		Vector pathTaken = applyConstraint(target, path);
		System.out.println("Feed forward : " + pathTaken);
		
		Player me = gameController.getBoard().getPlayer(myName);
		output = "#movePlayer Moving to " + target.getID();
		me.setMovesRemaining(0);
		Vector param = new Vector();
		param.add(target);
		param.add(me);
		param.add(pathTaken);
		gameController.directCommand("movePlayer", param);
		
		outputMessage ( output );

	}
	

	/**
	 * 
	 * Applies constraints to the path
	 * 
	 * @param target
	 * @param path
	 * @return
	 */
	private Vector applyConstraint(Position target, Vector path) {
		Vector altPath = new Vector();
		
		for(int i=0;i<path.size();i++) {
			if(path.elementAt(i) == target) {
				altPath.add(path.elementAt(i));
				break;
			}
			else
				altPath.add(path.elementAt(i));
		}

		return altPath;
	}


	/**
	 * Rolls the dice and then gets the result.
	 * 
	 * @return number of both dice 
	 */
	public int rollDice() {
		Player me = gameController.getBoard().getPlayer(myName);
		gameController.directCommand("roll", new Vector());
		return me.getMovesRemaining();

	}
	
	/**
	 * Find a player for a disproof<p>
	 *
	 * Returns Vector<p>
	 * <li> - first element -- pointer to player with cards
	 * <li> - second element -- Vector of cards for disprove
	 * <li> - elements -- each Card
	 *
	 */
	public Vector findDisproveCandidate( Card who, Card what, Card where ) {
		Vector results = new Vector();
		
		String output = "";
		Player playerWithCard = null;
		Vector cardsFound = new Vector();
		
		Player currentPlayer = gameController.getBoard().getCurrentPlayer();

		
		// Call who into where.
		
		Player defendant = gameController.getBoard().getCharacter(who.getName());
		
		if(!currentPlayer.getCharacter().equals(who.getName())) {
			if(defendant != null) {
				Position oldRoom = defendant.getPosition();
				Position destination = currentPlayer.getPosition();

				Vector parameters = new Vector();
				parameters.add(destination);
				parameters.add(defendant);		// Who to move..
				gameController.directCommand( "movePlayer", parameters );
			}
		}
                
		// Search all players for a card.

		
		playerWithCard = null;
		cardsFound = new Vector();
		
		// Order the players so we can scan through and check who has cards in the right order.
		Vector playersToParse = localArrangePlayersBy(currentPlayer);

		for(int i = 0; i < playersToParse.size(); i++) {
			playerWithCard = (Player)playersToParse.elementAt(i);
			if(playerWithCard != currentPlayer) {
				if(playerWithCard.hasCard(who))
					cardsFound.add(who);
				if(playerWithCard.hasCard(what))
					cardsFound.add(what);
				if(playerWithCard.hasCard(where))
					cardsFound.add(where);
				if(cardsFound.size() > 0)
					break;
			}
		}
		
		if(cardsFound.size() > 0) {
			output += playerWithCard.getRealName() + " choose which card you will use to disprove :-\n ";
			for(int i = 0; i < cardsFound.size(); i++) {
				output += ((Card)cardsFound.elementAt(i)).getName() + " ";
			}
			//gameConfig.setDisproveMode(true);
		}
		else {
			output += "Unable to disprove the suggestion.";
		}			
		
		outputMessage(output);
		
		results.add(playerWithCard);
		results.add(cardsFound);
		
		return results;
	}
		




	/** Order the players so we can scan through and check who has cards in the right order.
	 * 
	 */
	private Vector localArrangePlayersBy(Player currentPlayer) {
		Vector players = new Vector();
		int playerPosition = gameController.getBoard().getPlayerIndex(currentPlayer);
		Vector livePlayers = gameController.getBoard().getPlayers();
		
		// Add all elements after the player
		for(int i = playerPosition + 1; i < livePlayers.size(); i++)
			players.add(livePlayers.elementAt(i));
		// Add all elements before the player
		if(!( playerPosition == 0)) 
				for(int i = 0; i < playerPosition; i++)
					players.add(livePlayers.elementAt(i));
		return players;

	}
  
	/**
	 * Checks if a suggest was disproved or not with a given suspect and weapon.
	 * 
	 * @parameter suspect the suspect to eliminate (who did it)
	 * @parameter suspect the weapon to eliminate (what did it)
	 * 
	 * @return false - unable to disprove, true - disproved.
	 */
	public void moveDefendant(Card suspect) {
		Player defendant = gameController.getBoard().getCharacter(suspect.getName());
		Player me = gameController.getBoard().getPlayer(myName);

		Card testCard = null;
		Card room = gameController.getBoard().getCardPack().cardByName(me.getPosition().getID());
		if(defendant != null) {
			String output = "";
	        	
			if(defendant != null) {
				Position oldRoom = defendant.getPosition();
				Position destination = me.getPosition();
				
				Vector parameters = new Vector();
				parameters.add(destination);	// Where to move
				parameters.add(defendant);		// Who to move there
				gameController.directCommand( "movePlayer", parameters );
			}
                	
        		output = "Called " + defendant.getRealName() + " into the " + room.getName();
        	
			outputMessage(output);
		}
	}


	public void movePlayer(Position target, int amount) {
		
	}
 }
