package Cluedo.API;

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

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import Cluedo.Game.Card;
import Cluedo.Game.CardCollection;
import Cluedo.Game.DetectivePad;
import Cluedo.Game.Player;
import Cluedo.Game.Position;

/**
 * Class handles the restoration of the game/model's state, which is stored on disk
 * in an XML file. <P>
 * To invoke this, use loadGame()<br>
 * And to fetch the gameConfig use getPlayers(), getGameCardSet() and getGameConfig()<br>
 * <p>
 * NB
 * <p>
 * XML parsed with sax, therefore Java SDK 1.4.* is required.
 *
 * @author Alex
 */
public class GamePersistance {
	
	ArrayList players = new ArrayList();
	ArrayList gameCardSet = new ArrayList();
	boolean gameCardSetRead = false;
	
	int debug = 0;
	int currentPlayer = -1;	
	CardCollection envelope = null;
	
	CluedoConfig restoredGameConfig = null;
	
	private void setEnvelope(CardCollection e) {
		envelope = e;
	}
	
	public CardCollection getEnvelope() {
		return envelope;
	}
	
	
	DefaultHandler defaultLoadHandler = new DefaultHandler() {
		boolean inPlayer = false;

		boolean inPlayerCards = false;
		
		boolean inCard = false;
		
		boolean inDPad = false;
		
		boolean inPlayerDCards = false;
		
		boolean inPlayers = false;	
		boolean inGame = false;
		boolean inEnvelope = false;
		
		CardCollection cards = null;
		CardCollection envelope = null;
		
		Player player = null;
		Card card = new Card();
		String lastSection = "";
		String map = "default";
		
		/**
		 * Process the call-back for reading characters
		 */
		public void characters(char [] buf, int offset, int len) {	
			String s = new String(buf, offset, len);

			// Return if it's a blank line, or piece of data.
			if( (s = s.trim()) .equals(""))
				return;

			// ************************************************************
			// G A M E   A T T R I B U T E S
			//
			// inGame=true, inPlayers=false
			// ************************************************************
			if(inGame == true && inPlayers == false) {
				if(lastSection.equals("currentplayer")) {
					try  {
						currentPlayer = Integer.parseInt(s);
						//System.out.println("Current player : " + currentPlayer);
					}
					catch(Exception error) {
						System.out.println("Error parsing current player : " + s);
						currentPlayer   = 0;
					}
				}
				if(lastSection.equals("map")) {
					map = s;
					//System.out.println("map " + s);
				}
				if(inCard == true) {
					if(lastSection.equals("name"))
						card.setName(s);
					else if(lastSection.equals("type"))
						card.setType(s);
				}
					
			}

			// ************************************************************
			// P L A Y E R   A T T R I B U T E S
			// 
			// inPlayer=true, inPlayerCards=inPlayerDCards=false
			// ************************************************************
			else if(inPlayer == true && inPlayerDCards == false && inPlayerCards == false) {

				if(lastSection.equals("name")) {
					player.setName(s);
				}
				else if(lastSection.equals("character")) {
					player.setCharacter(s);					
				}
				else if(lastSection.equals("position")) {
					player.setPosition(new Position(s));
				}
				else if(lastSection.equals("playertype")) {
					try  {
						int type = Integer.parseInt(s);

						player.setPlayerType(type);
					}
					catch(Exception error) {
						System.out.println("Error parsing number: " + s);
					}
					
				}
				else if(lastSection.equals("moved")) {
					if(s.equals("true"))
						player.setMoved(true);
					else
						player.setMoved(false);
				}
				else if(lastSection.equals("rolled")) {
					if(s.equals("true"))
						player.setRolled(true);
					else
						player.setRolled(false);
				}
				else if(lastSection.equals("interactive")) {
					if(s.equals("true"))
						player.setInteractive(true);
					else
						player.setInteractive(false);
				}
				else if(lastSection.equals("suggested")) {
					if(s.equals("true"))
						player.setSuggestionOnLastTurn(true);
					else
						player.setSuggestionOnLastTurn(false);
				}
				else if(lastSection.equals("movesRemaining")) {
					try  {
						int moves = Integer.parseInt(s);
						player.setMovesRemaining(moves);
					}
					catch(Exception error) {
						System.out.println("Error parsing number: " + s);
						player.setMovesRemaining(0);
					}
				}
			}
			

			// ************************************************************
			// D E T E C T I V E   P A D 
			//
			// inPlayerDCards == true
			// ************************************************************
			else if(inPlayerDCards == true) {
				
					if(inCard == true) {
						if(lastSection.equals("name")) {
							card.setName(s);
						}
						else if(lastSection.equals("type")){
							card.setType(s);
						}
						else if(lastSection.equals("eliminated")) {
							boolean result = false;
							if(s.equals("true"))
								result = true;
							card.setEliminated(result);
							//System.out.println(player.getRealName() +", name="+card.getName() + ", elim="+((card.isEliminated()+"").toUpperCase()) );
						}
					}
				}

			// ************************************************************
			// P L A Y E R   C A R D S 
			//
			// inPlayerDCards == true
			// ************************************************************
				else if(inPlayerCards == true) {
					if(inCard == true) {
						if(lastSection.equals("name")) {
							card.setName(s);
						}
						else if(lastSection.equals("type")) {
							card.setType(s);
						}
					}
                        	
				}
				
		}

		public void endElement(String namespaceURI, String localName, String qName) {
			
			// System.out.println("End: [" + qName+"]");
			
			if (qName.equals("players")) {
				inPlayers = false;
				restoredGameConfig.setMapVersion(map);
				
			}
//************************************************************
//C L O S I N G   T H E   R E A D I N G   A   P L A Y E R
//************************************************************
			else if (qName.equals("player")) {
				if (player != null) {
					players.add(player);
				}
				else {
					System.out.println("endElement: Player missing?! found null player!!");
				}
				
				inPlayer = false;
				player = null;
			}
//************************************************************
//C L O S I N G   T H E   R E A D I N G   A   C A R D
//************************************************************
			else if (qName.equals("card")) {
				
				// ************************************************************			
				// Reading detective pad cards and making list of game cards
				//
				// qName == "card"
				// inPlayerDCards == true, gameCardSetRead == false
				// ************************************************************
				if (inPlayerDCards == true && gameCardSetRead == false) {
					//System.out.println("gameCardSet.add( " + card.getName() + " " + card.getType()+")"); 
					gameCardSet.add(card);
				}
				
				// otherwise just add the card in the current card set
				cards.insertCard(card);

				inCard = false;
			
			}
			else if (qName.equals("detectivepad")) {
				inDPad = false;
				gameCardSetRead = true;
			}
			

// ************************************************************
// C L O S I N G   T H E   R E A D I N G   O F  C A R D S 
// ************************************************************
			else if (qName.equals("cards")) {
				
				// ************************************************************
				// For marking END of reading envelope cards 
				// ************************************************************
				if (inEnvelope == true) {
					inEnvelope = false;
					envelope = cards;
					
					setEnvelope(envelope);

					System.out.println("Envelope Cards");

					for (int i = 0; i < envelope.countCards(); i++)
						System.out.println("\t--"+envelope.cardAt(i).getName());
				}
				// ************************************************************
				// For reading in player detective pad cards
				// 
				// ************************************************************
				else if (inPlayerDCards == true) {
					DetectivePad detectivePad = new DetectivePad();
					/*for(int i=0;i<cards.countCards();i++) {
						System.out.println(((Card)cards.cardAt(i)).getName() + " " +  ((Card)cards.cardAt(i)).isEliminated());
					}*/
					detectivePad.setCardBag(cards);
					player.setDetectivePad(detectivePad);
					//System.out.println("DetectivePad done: (" + cards.countCards() + "), for = " + player.getRealName());
					inPlayerDCards = false;
					//inDPad = false;
					
				}
				// ************************************************************
				// For reading in player cards 
				// 
				// ************************************************************
				else if (inPlayerCards == true) {
					player.setCards(cards);
					inPlayerCards = false;
					//System.out.println("Private cards read. " + cards.countCards() + ", for = " + player.getRealName());
				}
			}
		}


		public void startElement(String namespaceURI, String localName, String qName, Attributes atts) {
			
			lastSection = qName;
			
			
//			************************************************************
//			O P E N   G A M E
//          ************************************************************
			if (qName.equals("game")) {
			    String gameType = null;

			    // Build new object for game config pointer
			    restoredGameConfig = new CluedoConfig();

			    if((gameType = atts.getValue("type")) != null) {
			        try {
			            int value = Integer.parseInt(gameType);
			            restoredGameConfig.setGameType(value);
			        }
			        catch(Exception e) {
			            e.printStackTrace();
			        }
			    }
				inGame = true;

			}
//			************************************************************
//			O P E N   E N V E L O P E
//          ************************************************************
			else if (qName.equals("envelope")) {
				inEnvelope = true;
				envelope = new CardCollection();
				
			}
//			************************************************************
//			O P E N   P L A Y E R S
//          ************************************************************
			else if (qName.equals("players")) {
				inPlayers = true;
			}
//			************************************************************
//			O P E N   P L A Y E R
//          ************************************************************
			else if (qName.equals("player")) {
				inPlayer = true;
				try {
					player = new Player();
				} catch (RuntimeException e) {
					e.printStackTrace();
				}
				
			}
//			************************************************************
//			O P E N   C A R D S
//          ************************************************************
			else if (qName.equals("cards")) {
				cards = new CardCollection();

				if (inPlayer == true) {
					
					// If player cards not already read then the order denotes we are processing
					// player cards first.
					if (player.getCards() == null || player.getCards().countCards() == 0 ) {
						inPlayerCards = true;
					}
					else {
						inPlayerDCards = true;
						
					}
					
				}
				else if (inPlayer == false) {
					inEnvelope = true;
				}

			}
//			************************************************************
//			O P E N   C A R D 
//          ************************************************************
			else if (qName.equals("card")) {
				
				card = new Card();
				inCard = true;

			}
//			************************************************************
//			O P E N   D E T E C T I V E   P A D 
//          ************************************************************
			else if (qName.equals("detectivepad")) {
				inDPad = true;
			}
		}

	};
	
	public GamePersistance() {
		
	}

	public ArrayList getPlayers() {
		return players;
	}
	
	public ArrayList getGameCardSet() {
		return gameCardSet;	
	}

	public int getCurrentPlayer() {
		return currentPlayer;
	}

	public CluedoConfig getGameConfig() {
		restoredGameConfig.setPlayers(getPlayers());
		System.out.println("[GamePeristance:getGameConfig]");
		for (int i = 0; i < gameCardSet.size(); i++)
			System.out.println("+ With: " + ((Card) gameCardSet.get(i)).getName());

		for (int i = 0; i < players.size(); i++) {
			Player p = (Player) players.get(i);
			System.out.println("+ " + p.getRealName());
			for (int j = 0; j < p.getCards().countCards(); j++)
				System.out.println("\t" + p.getCards().cardAt(j).getName());
		}

		return restoredGameConfig;
	}
	
	public static void main(String[] args) {
		String fileName = "../../Resources/Saves/alex.xml";

		GamePersistance gp = new GamePersistance();
		URL fileUrl = gp.getClass().getResource(fileName);

		if (fileUrl == null) {
			System.out.println("Unable to find file.");
			System.exit(-1);
		}
		File file = new File(fileUrl.getFile());

		fileName = file.getPath();
		System.out.println(fileName);

		try {
			gp.loadGame(fileName);

		}
		catch(IOException error) {
			System.out.println("[GamePersitance:loadGame] Unable to load file.");
		}
		catch(NullPointerException error){
			System.out.println("[GamePersitance:loadGame] Null pointer...");			
		}
		catch(SAXException error){
			System.out.println("[GamePersitance:loadGame] Sax exception.");
			error.printStackTrace();
		}
		catch(ParserConfigurationException error){
			System.out.println("[GamePersitance:loadGame] Bad parser config exception.");			
		}
		catch (Exception error) {
			System.out.println("[GamePersitance:loadGame] Other error.");
		}

	}
	
	public void loadGame(String fileName) throws ParserConfigurationException, SAXException, IOException, NullPointerException  {

		SAXParserFactory factory = SAXParserFactory.newInstance();
		SAXParser saxParser = factory.newSAXParser();
		saxParser.parse( fileName, defaultLoadHandler );

		try {
			if(debug >= 4)
				for(int i = 0; i < players.size(); i++)
					System.out.println("[GamePersitance:loadGame] "+ ((Player)players.get(i)).toXML() + "\n");
			if(debug >= 3)
				for(int i = 0; i < 	envelope.countCards(); i++)
					System.out.println("[GamePersitance:loadGame] "+ envelope.cardAt(i).toXML() + "\n");
			if(debug >= 2) {
					System.out.println("[GamePersitance:loadGame] Current player: " + currentPlayer + "\n");
			}
		}
		catch(Exception error){
			System.out.println("[GamePersitance:loadGame] null error");
		}
	}

}
