/**
 * GUI Package contains View etc.
 * 
 */
package Cluedo.API;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.Vector;

import Cluedo.Game.Card;
import Cluedo.Game.Player;

/**
 * Holds information about a new CluedoGame before it's created by the GameController which modifies the Model.<br>
 * This is increases flexibility by allowing all data about the next game to be loaded and processed externally
 * from the GUI.<BR>
 * Also allows extra information to be found which is required for a save-game, such as card-set, etc.
 *
 * 
 * @author alex
 */
public class CluedoConfig {
	
	private Vector positions = new Vector();
	private Vector players = null;
	private Cluedo.Game.CardCollection cards = new Cluedo.Game.CardCollection();
	private Vector bindings = new Vector();

	// Configurables
	private String cardSet = "default";
	
	private final static String pathSuffix = "Resources/";
	
	/**
	 * The version of the map to be used for the game.
	 */
	private String mapVersion = "default";
	
	private boolean formingGame = false;	

	final int debugLevel = 0;

	Vector mapAreas = new Vector();
	
	/**
	 * Loaded set of XML polygondefinitions
	 * 
	 */
	ArrayList mapAreasXML = null;

	/**
	 * Used in GUI1 only.
	 * 
	 */
	private boolean disproveMode = false;


	/**
	 * Type of game rules to be used for the game, 0 = regular, 1 = special rolling ( 2x6 == go to any room).
	 * 
	 */
	private int gameType = 0;

	
	
	public void setDisproveMode(boolean mode) {
		disproveMode = mode;
	}
	
	public boolean inDisproveMode() {
		return disproveMode;
	}
	
	/**
	 * Configure the map to be used for this game
	 */
	public void setMapVersion(String version) {
		mapVersion = version;
	}
	
	/**
	 * The version of the map to be used for the game.
	 */
	public String getMapVersion() {
		return mapVersion;	
	}

	/**
	 * Loads and parses a points file and then generates a Vector of MapArea instances.
	 * Format: -<br>
	 * # NodeID:XCordxYCoord,XCordxYCoord,XCordxYCoord,XCordxYCoord
	 *
	 */
/*	public void loadMapArea() {
		StringTokenizer lines = null;

		try {
			lines = new StringTokenizer(
					 Cluedo.API.Resources.catTextFile("Maps/"+mapVersion+".pnt", this), "\n" );
		}
		catch(Exception error) {
			System.out.println("[GameConfig:loadBindings] Mapfile "+mapVersion+" does not exist.");
			return;
		}
		
		try {
			StringTokenizer parts = null;	// Each colon separated data piece
			
			String node = "";		// Node to bind to
			String target = "";		// target node for binding
			String points = "";		// List of XxY points

			Cluedo.API.MapArea mapArea = null;
			
			
			while(lines.hasMoreTokens() == true) {
				// Get ID and type
				parts = new StringTokenizer(lines.nextToken(), ":");
				node = parts.nextToken();
				// Comments may exist in data files if the line begins with # (hash)
				if(!(node.charAt(0) == '#')) {
					if(debugLevel >= 3)
						System.out.println("[GameConfig:loadBindings] " + node);
					
					points =  parts.nextToken();

					StringTokenizer pointsParser = new StringTokenizer(points, ",");
					
					float x = -1;
					float y = -1;
					float[][] floatPoints = new float[pointsParser.countTokens()][2];
					String point = "";
					int index = 0;
					while(pointsParser.hasMoreTokens() == true) {
						point = pointsParser.nextToken(); 
						String bits[] = point.split("x");
						x = Float.parseFloat(bits[0]);
						y = Float.parseFloat(bits[1]);
						
						floatPoints[index][0] = x;
						floatPoints[index][1] = y;
						index++;
						if(debugLevel >= 3)
							System.out.println("[GameConfig:loadBindings] ("+x+ " , " + y+")");
					}

					mapArea = new MapArea(node, floatPoints);
					mapAreas.add(mapArea);

				}
			}
		}
		catch(Exception general) {
			System.out.println("[GameConfig:loadBindings] Mapfile "+mapVersion+" incorrect.");
		}
		
	}*/

	public ArrayList getLoadedMapAreas() {
		return mapAreasXML;	
	}
	public void loadMapArea() {
		String fileName = "Resources/Maps/"+mapVersion+".xml";
		
		mapAreasXML = new ArrayList();
		
		if(new File(fileName).exists()==false)
			fileName = "../../Resources/Maps/"+mapVersion+".xml";
		
		Cluedo.API.MapLoader ml = new Cluedo.API.MapLoader();
		URL fileUrl = null;
		try {
			fileUrl = new File(fileName).toURL(); 
			//ml.getClass().getResource(fileName);
		}
		catch(Exception e) {
			
		}
		
		if(fileUrl == null) {
			System.out.println("[CluedoConfig:loadMapArea] Unable to find file.");
			System.exit(-1);
		}		
		File file = new File(fileUrl.getFile());

		fileName = file.getPath();
		//System.out.println(fileName);
		
		try { 
			ml.loadGame(fileName);
			mapAreasXML = ml.getMapAreas();
		}
		catch(IOException error) {
			System.out.println("[GamePersitance:loadGame] Unable to load file.");
		}
		catch (Exception error) {
			System.out.println("[GamePersitance:loadGame] Other error.");
		}
				
	
	}

	public String []getPlayerNames() {
		if(players == null)
			return new String[] {};

		String []names = new String[players.size()];
		for(int i =0;i<players.size();i++)
			names[i] = ((Player)players.elementAt(i)).getRealName();
		return names;
	}

	public boolean playerExists(String name) {
		if(players==null)
			return false;
		
		name = name.toLowerCase();
		for(int i =0;i<players.size();i++)
			if(((Player)players.elementAt(i)).getRealName().toLowerCase().equals(name))
				return true;
				
		return false;
	}
	/**
	* Create and load cards from a file.<p>
	*
        * Format is:<br>
        *    CardName:CardType<br>
	*
	*/
	public void loadCards() {
		String absolute = "../../Resources/Cards/"+cardSet+".cards";
		if(new File(absolute).exists()==false)
			absolute = new File("Resources/Cards/"+cardSet+".cards").getAbsolutePath();

		System.out.println("loadCards() " + cardSet);
	
		try {
			StringTokenizer lines = new StringTokenizer(
					 Cluedo.API.Resources.catTextFileExt(absolute, this), "\n" );


			StringTokenizer parts = null;	// Each comma separated data piece
			String cardName = "";
			String cardType = "";

			while(lines.hasMoreTokens() == true) {
				// Get ID and type
				String line = lines.nextToken();
				if(!(line.charAt(0) == '#')) {
					parts = new StringTokenizer(line, ",");
					cardName = parts.nextToken();
					cardType = parts.nextToken();

					// Insert the newly found card into a card collection					
					cards.insertCard(new Card(cardName, cardType));
					
					if(debugLevel == 2)
						System.out.println("[GameConfig:loadCards] "+cardName+ " loaded as a " + cardType);
				}
			}
		}
		catch(Exception general) {
			System.out.println("[GameConfig:loadCards] Cardsfile "+cardSet+" incorrect or doesn't exist.");
		}
	}

	public void defineCards(ArrayList cards) {
		for(int i = 0; i < cards.size(); i++)
			this.cards.insertCard((Card)cards.get(i));
	}
	

	/**
	* Create node objects and populate bindings based on data in map file.<p>
	*
        * Format is:<br>
        *    NodeID:Type:Node1,Node2,Node3,NodeN<br>
        * or<br>
        *    NodeID:Type:Node1,Node2,Node3,NodeN:StartingPosition<br>
	*
	*/
	public void loadBindings() {
		String absolute = "../../Resources/Maps/"+mapVersion+".map"; 
		if(new File(absolute).exists()==false)
			absolute = new File("Resources/Maps/"+mapVersion+".map").getAbsolutePath();
				
		try {
			StringTokenizer lines = new StringTokenizer(
					Cluedo.API.Resources.catTextFileExt(absolute, this), "\n" );

			StringTokenizer parts = null;	// Each colon separated data piece
			StringTokenizer idList = null;	// Each comma separated target node for binding
			
			String source = "";		// Node to bind to
			String target = "";		// target node for binding
			String type = "";		// Type of node we're reading
			String ids = "";		// A list of target nodes.
			
			String startingCharacter = "";
			
			while(lines.hasMoreTokens() == true) {
				// Get ID and type
				parts = new StringTokenizer(lines.nextToken(), ":");
				source = parts.nextToken();
				
				// Comments may exist in data files if the line begins with # (hash)
				if(!(source.charAt(0) == '#')) {
					type = parts.nextToken();
					ids =  parts.nextToken();
					if(type.equals("Square")) {
						positions.add( new Cluedo.Game.Square( source ) );
					}
					else if(type.equals("Room"))
						positions.add( new Cluedo.Game.Room( source ) );
					else {
						startingCharacter = parts.nextToken();
						positions.add( new Cluedo.Game.StartingSquare( source, startingCharacter ) );
					}
						
						
					// Get target bindings
					idList = new StringTokenizer(ids, ",");
					
					while(idList.hasMoreTokens() == true) {
						target = idList.nextToken();
				
						bindings.add(new Cluedo.API.Binding(source, target));
                                		if(debugLevel == 2)
							System.out.println("[GameConfig:loadBindings] "+source + " is linked to " + target);
					}
				}
			}
		}
		catch(Exception general) {
			System.out.println("[GameConfig:loadBindings] Mapfile "+mapVersion+" incorrect or doesn't exist.");
			general.printStackTrace();
		}
	}

	/**
	 * Returns map nodes for use in the Model
	 */
	public Vector getMapNodes() {
		return positions;
	}
	public Vector getValidCharacterNames() {
		Vector validCards = new Vector();
		for(int i = 0; i < cards.countCards(); i++)
			if((cards.cardAt(i)).getType().equals("Suspect") && characterNameTaken( (cards.cardAt(i)).getName()) == false)
				validCards.add( (cards.cardAt(i)).getName() );

		return validCards;
	}
	
	public Vector getValidCharacterNames(String modifier) {
		Vector validCards = new Vector();
		for(int i = 0; i < cards.countCards(); i++)
			if((cards.cardAt(i)).getType().equals("Suspect") && characterNameTaken( (cards.cardAt(i)).getName()) == false || cards.cardAt(i).getName().equals(modifier))
				validCards.add( (cards.cardAt(i)).getName() );

		return validCards;
	}

	/**
	 * For use in the creation of a new game.
	 */
	private boolean characterNameTaken(String name) {
		boolean result = false;
		if(players==null)
			return false;
		for(int i = 0; i < players.size(); i++)
			if(((Player)players.elementAt(i)).getCharacter().equals(name)) {
				result = true;
				break;
			}
			
		return result;
	}

	/**
	 * Check if the name is one of those in the selected suspect cards
	 */
	public boolean validCharacterName(String name) {
		Card search = cards.cardByName(name);
		if(search != null)
			if(search.getType().equals("Suspect"))
				return true;
	
		return false;
	}
	/**
	* Return a Vector of Player instances
	*
	*/
	public Vector getPlayers() {
		return players;
	}
	/**
	* Game ready to begin?
	*
	*/
	public void setFormingGame(boolean result) {
		formingGame = result;
	}
	/**
	* Game ready to begin?
	*
	*/
	public boolean isFormingGame() {
		return formingGame;
	}
	public void setAmountPlayers(int size) {
		players = new Vector(size);
	}
	/**
	* Amount of elements allocated in Vector for new players
	*
	*/
	public int getAllocatedPlayers() { 
		if(players != null) {
			return players.capacity();
		}
		else
			return -1;
	}
	/**
	* Amount of players "set"
	*
	*/
	public int getAmountPlayers() {
		int increment = 0;
		for(int i = 0; i < players.size(); i++)
			if(players.elementAt(i) != null)
					increment++;
		return increment;
	}
	

	public void addPlayer(Player player) {
		if(players == null) {
			players = new Vector();
		}
		players.add(player);
	}
	
	/**
	* Add a new player to the game config
	*
	* @return false if the name is not valid else true if the player was added
	*/
	public boolean addPlayer(String playerName, String characterName) {
		Player newPlayer = null;
		boolean valid = true;
		for(int i = 0; i < players.size(); i++) {
			newPlayer = (Player)players.elementAt(i);
			if(newPlayer.getRealName().equals(playerName) || newPlayer.getCharacter().equals(characterName)) {
					valid = false;
					break;
			}
		}
		
		if(valid == false)
			return false;

		newPlayer = new Player();
		newPlayer.setName(playerName);
		newPlayer.setCharacter(characterName);
		if(playerName.startsWith("AI"))
				newPlayer.setPlayerType(1);
		else
			newPlayer.setPlayerType(0);

		if(players == null)
			System.out.println("[Simple:addPlayer] Players vector not ready\n");
		if(newPlayer.getPlayerType() >= 1)
			System.out.println("[Simple:addPlayer] Player " + playerName+" now playing as "+characterName+ " playing as an AI.\n");
		else
			System.out.println("[Simple:addPlayer] Player " + playerName+" now playing as "+characterName+ "\n");

		players.add(newPlayer);
		return true;
	}
 
	public void setMapNodes(Vector newPositions) {
		positions = newPositions;
	}
	public Vector getBindings() {
		return bindings;
	}
	/**
	 * @return Returns the mapVersion.
	 */
	public String getBoardMap() {
		return mapVersion;
	}
	/**
	 * @param mapVersion The mapVersion to set.
	 */
	public void setBoardMap(String mapVersion) {
		this.mapVersion = mapVersion;
	}
	/**
	 * @return Returns the cardSet.
	 */
	public String getCardSet() {
		return cardSet;
	}
	/**
	 * @param cardSet The cardSet to set.
	 */
	public void setCardSet(String cardSet) {
		this.cardSet = cardSet;
	}
	/**
	 * @param players The players to set.
	 */
	public void setPlayers(Vector players) {
		this.players = players;
	}
	/**
	 * @param players The players to set.
	 */
	public void setPlayers(ArrayList playerList) {
		this.players = new Vector();
	
		for(int i = 0; i < playerList.size(); i++)
			this.players.add(((Player)playerList.get(i)));
	}
	/**
	 * @return Returns the cards.
	 */
	public Cluedo.Game.CardCollection getCards() {
		return cards;
	}
	/**
	 * @param cards The cards to set.
	 */
	public void setCards(Cluedo.Game.CardCollection cards) {
		this.cards = cards;
	}

	public void removePlayer(String text) {
		if(players == null)
			return;
		for(int i =0;i<players.size();i++)
			if(((Player)players.elementAt(i)).getRealName().toLowerCase().equals(text))
				players.remove(players.elementAt(i));
	}


	public Player getPlayer(String string) {
		if(players == null)
			return null;
		for(int i =0;i<players.size();i++)
			if(((Player)players.elementAt(i)).getRealName().toLowerCase().equals(string))
				return ((Player)players.elementAt(i));
		return null;
	}

	   /**
     * @return Returns the gameType.
     */
    public int getGameType() {
        return gameType;
    }

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