//turn summary: p12d34
// 0 (p, d), (play, discard)
// 1 color
// 2 value
// 3 (c, d), (drew from cards, drew from discard
// 4 color
// 5 value


//TODO: Prune function on discard to take out cards opponent has claimed.
//TODO: Hascard function for the hand so that when processing updates, if it doesn't have a card it should it takes it.
//TODO: Some way to detect what cards  have been taken from the discard (preferrably without using the last move to do so)
//TODO: prevent dragging of cards from discard until draw is enabled.
//TODO: prevent card drawn from discard from going back to discard during an update
//TODO: Endgame continuation option
//CLAIMING A CARD SENDS PLAYED/DISCARDED CARD BACK TO THE HAND

// Known bugs:
// correct determination of drag/drop locations when game is not at 0,0.
// Correct function call and XH ready check for turn sender

LC = function(targetDiv){
	this.gameStatusPage = "gameStatus.php"
	this.gameAreaXStart = 187;
	this.gameAreaXEnd = 790;
	//this.gameStatusPage = "fakeStatus2.xml";
	this.name = targetDiv;
	if(!window.lcGames) window.lcGames = [];
	window.lcGames[this.name] = this;
	
	this.cardsInRack = 8;
	this.cardValues = ['2','3','4','5','6','7','8','9','10','x','x','x'];
	this.expeditions = ["earth", "jungle", "water", "ice", "sand"];
	this.colors = ["red", "green", "blue", "white", "yellow"];
	this.fontColors = ["white", "white", "white", "black", "black"];
	this.numExps = this.expeditions.length;
	
	// -1 is unknown, 0 is self, 1 is opponent
	this.gameStarted = false;
	
	this.lcDiv = document.getElementById(targetDiv);
	if(!this.lcDiv){
		this.debug("The game space could not be found. When calling LC(), you must pass the ID of the div that you wish the application to appear in");
		return;
	}
	
	this.createBoard();
	window.setInterval("window.lcGames['" + this.name + "'].checkStatus()", 5000);
};


//Grrr. I SO wanted this game to run with only one variable, but alas drag
//and drop requires document-wide listeners and allowing multiple instances
//of the game means this is impossible to avoid luckily, nobody will be dragging
//two cards in two games at the same time.
var LC_MOVING_CARD = false;

LC.prototype.createBoard = function(){
	this.lcDiv.innerHTML="";
	this.lcDiv.className = "lc_Game";
	this.chatDiv = this.createElement("div","lc_Chat")
	this.lcDiv.appendChild(this.chatDiv);
	this.statusField = this.createElement("div","lc_GameStatus");
	this.lcDiv.appendChild(this.statusField);
	this.showStatusMessage("<b>Welcome to Lost Cities!</b>");
	this.mainBoard = this.createElement("div", "lc_MainBoard");
	this.lcDiv.appendChild(this.mainBoard);
	this.discard = new LC_DISCARD(this);
	this.leftSide = this.createElement("div","lc_LeftSide");
	this.lcDiv.appendChild(this.leftSide);
	this.deck = new LC_DECK(this);
	this.opp = new LC_PLAYER(this, "opponent");

	this.you = new LC_PLAYER(this, "player");
	
	this.hand = new LC_HAND(this);
	
	this.status = new LC_GAMESTATUS(this);
	

	this.initChat();
	
};


LC.prototype.initChat = function(){
	this.chat = new LC_CHAT(this);
};

LC.prototype.showStatusMessage = function(msg){
	this.statusField.innerHTML += "<p>" + msg + "</p>";
	this.statusField.scrollTop = this.statusField.scrollHeight;
};

LC.prototype.debug = function(txt){
	this.showStatusMessage("<font color=\"green\">" + txt + "</font>");
};



LC.prototype.createElement = function(type, className, id){
	var el = document.createElement(type);
	if(className) el.className = className;
	if(id) el.id = id;
	return el;
};

LC.prototype.checkStatus = function(){
	this.statusChecker = this.makeXHR();
	this.sendQuery(this.statusChecker, this.gameStatusPage + "?chatLastId=" + this.chat.lastId, "window.lcGames['" + this.name + "'].processStatus()");
	
};

var foo;
LC.prototype.processStatus = function(){
	if(!this.isXHRReady(this.statusChecker)) return;
	var status = this.statusChecker.responseXML.documentElement;
	var game = status.getElementsByTagName('game').item(0);
	//alert(foo);
	this.processGame(game);
	this.chat.processUpdate(status.getElementsByTagName('chat').item(0));
};

LC.prototype.processTurnEnd = function(){
	if(!this.isXHRReady(this.turnSender)) return;
	var status = this.turnSender.responseXML.documentElement;
	this.processGame(status.getElementsByTagName('game').item(0));
	this.chat.processUpdate(status.getElementsByTagName('chat').item(0));
};

LC.prototype.processGame = function(game){
	if(this.gameUpdateCode){
		if(this.gameUpdateCode >= game.getAttribute("updatecode")) return;
	}
	this.gameUpdateCode = game.getAttribute("updatecode");
	var status = game.getAttribute("status");
	this.opp.processUpdate(game.getElementsByTagName('opp').item(0));
	this.you.processUpdate(game.getElementsByTagName('you').item(0));
	this.deck.processUpdate(game.getElementsByTagName('cards').item(0));
	switch(status){
		case "waiting":
			break;
		case "inprogress":
			if(this.gameStarted == false) this.beginGame();
			this.status.setTurn(game.getElementsByTagName('turn').item(0).getAttribute('who'));
			this.hand.process(game.getElementsByTagName('hand').item(0))
			this.status.processLastMove(game.getElementsByTagName('lastmove').item(0));
			this.discard.processUpdate(game.getElementsByTagName('discard').item(0));
			break;
		case "gameover":
			this.status.setTurn(0);
			this.hand.process(game.getElementsByTagName('hand').item(0))
			this.status.processLastMove(game.getElementsByTagName('lastMove').item(0));
			if(this.you.roundScore > this.opp.roundScore) this.showStatusMessage("The game is over. You are victorious!");
			else if(this.you.roundScore < this.opp.roundScore) this.showStatusMessage("The game is over. You got spanked!");
			else this.showStatusMessage("Game over. You tied!");
			break;
			
			
	}
			
			
};
	



LC.prototype.beginGame = function(){
	this.gameStarted = true;
	this.showStatusMessage("The game begins!");

};

LC.prototype.beginPlayerTurn = function(){

};

LC.prototype.endPlayerTurn = function(){

};


LC.prototype.makeXHR = function(){
	try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
	try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
	try { return new XMLHttpRequest(); } catch(e) {}
	alert("XMLHttpRequest not supported");
	return null;
};

LC.prototype.sendQuery = function(xhr, url, receiver){
	// Ugh. Eval. Trying to get everything working in IE and FF. IE is complaining about types.
	xhr.onreadystatechange = function(){ eval(receiver) };
	//alert(typeof url);
	xhr.open("GET", url, true);
	xhr.send(null);	
};

LC.prototype.sendPostQuery = function(xhr, url, receiver, data){
	xhr.onreadystatechange = function(){ eval(receiver) };
	xhr.open("POST", url, true);
	var params = null;
	for(key in data){
		if(params == null) params = key + "=" + escape(data[key]);
		else params += "&" + key + "=" + escape(data[key]);
	}
	xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
	xhr.send(params);	
};

LC.prototype.isXHRReady = function(xhr){
	//this.showStatusMessage(xhr.readyState + " " + xhr.status);
	// status is 0 when running locally as a file
	try{return (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0));}
	// accessing xhr attributes can cause problems sometimes.
	catch(E){return false;}
};

//returns where on the page the person clicked
LC.prototype.getClientX = function(event){
	return event.clientX
};

        
LC.prototype.getClientY = function(event){
	return event.clientY;
};
        
//gets where on the card the person clicked
LC.prototype.getLayerX = function(event){
	
	return (event.layerX || event.offsetX);
};
        
LC.prototype.getLayerY = function(event){
	return (event.layerY || event.offsetY);
};

	
LC.prototype.getSection = function(x, y){
	if(x == null || y == null || x < this.gameAreaXStart || x > this.gameAreaXEnd) return false;
	if(y >= 159 && y <= 237) return "discard";
	if(y >= 238 && y <= 387) return "expeditions";
	if(y >= 388 && y <= 470) return "rack";
	return false;
};

LC.prototype.highlightActiveArea = function(x, y, color){
	switch(this.getSection(x, y)){
		case "discard":
			this.discard.highlight(color);
			this.you.expeditions.unhighlight();
			this.hand.unhighlight();
			break;
		case "expeditions":
			this.you.expeditions.highlight(color);
			this.discard.unhighlight();
			this.hand.unhighlight();
			break;
		case "rack":
			this.hand.highlight();
			this.discard.unhighlight();
			this.you.expeditions.unhighlight();
			break;
		default:
			this.hand.unhighlight();
			this.discard.unhighlight();
			this.you.expeditions.unhighlight();
			break;
	}
};

LC.prototype.finishTurn = function(playMove, playCard, drawMove, drawCard){
	var move = "";
	if(playMove == "discard") move += "d";
	else move += "p";
	move += playCard.str;
	if(drawMove == "discard"){
		move += "d";
		move += drawCard.str;
	}
	else{
		move += "c";
		move += "xx";
	}
	this.turnSender = this.makeXHR();
	//this.gameStatusPage = "fakestatus3.xml";
	this.status.card = false;
	this.status.action = false;
	this.sendQuery(this.turnSender, this.gameStatusPage + "?chatLastId=" + this.chat.lastId + "&move=" + move, "window.lcGames['" + this.name + "'].processTurnEnd()");
}

LC_GAMESTATUS = function(game){
	this.game = game;
	this.status = "waiting";
	this.card = false;
	this.action = false;
	this.lastMove = false;
	this.turn = -1;
};

LC_GAMESTATUS.prototype.setStatus = function(status){
	this.status = status;
};


LC_GAMESTATUS.prototype.recordAction = function(action, card, x){
	if((action == "expeditions" && this.game.you.expeditions.isCardEligible(card, this.card)) || action == "discard"){
		if(this.card == card){
			if(this.action == "discard"){
				this.game.discard.removeCard(this.card);
			}
			else{
				this.game.you.expeditions.removeCard(this.card);
			}
		}
		else if(this.card !== false){
			if(this.action == "discard"){
				this.game.discard.removeCard(this.card);
			}
			else{
				this.game.you.expeditions.removeCard(this.card);
			}
			this.game.hand.removeCard(card);
			this.game.hand.addCard(this.card);
		}
		else{
			this.game.hand.removeCard(card);
		}
		this.enableCardDraw();
		this.card = card;
		this.action = action;
		if(action == "expeditions") this.game.you.expeditions.addCard(card);
		else this.game.discard.addCard(card);
		return;
	}
	else if (action == "rack"){
		if(this.card == card){
			if(this.action == "discard"){
				this.game.discard.removeCard(this.card);
			}
			else{
				this.game.you.expeditions.removeCard(this.card);
			}
			this.action = false;
			this.card = false;
			this.disableCardDraw();
		}
		else{
			this.game.hand.removeCard(card);
		}
		this.game.hand.freeSlot(this.game.hand.getSlotFromCoords(x));
		this.game.hand.addCard(card, this.game.hand.getSlotFromCoords(x));
		return;	
	
	
	}
	else if (action === false){
		if(card.where == "hand"){
			this.game.hand.removeCard(card);
			this.game.hand.addCard(card);
		}
		else if(card.where == "discard"){
			this.game.discard.removeCard(card);
			this.game.discard.addCard(card);
		}
		else if(card.where == "expeditions"){
			this.game.you.expeditions.removeCard(card);
			this.game.you.expeditions.addCard(card);
		}
		
	}
	else{
		this.game.hand.removeCard(card);
		this.game.hand.addCard(card);
		if(action == "expeditions") this.game.showStatusMessage("Cards must be played in ascending order");
	}
	
};

LC_GAMESTATUS.prototype.enableCardDraw = function(){
	this.cardDrawEnabled = true;
	this.game.deck.enableCardDraw();
	this.game.discard.enableCardDraw();

};

LC_GAMESTATUS.prototype.disableCardDraw = function(){
	this.cardDrawEnabled = false;
	this.game.deck.disableCardDraw();
	this.game.discard.disableCardDraw();

};

LC_GAMESTATUS.prototype.setTurn = function(turn){
	if(this.turn != turn){
		this.turn = turn;
		if(this.turn == 0){
			this.game.opp.gainTurnPossession();
			this.game.you.loseTurnPossession();
		}
		else if(this.turn == 1){
			this.game.you.gainTurnPossession();
			this.game.hand.setMovable();
			this.game.opp.loseTurnPossession();		
		}
	}
};

LC_GAMESTATUS.prototype.isItMyTurn = function(){
	return (this.turn == 1);
};

LC_GAMESTATUS.prototype.claimCard = function(card){
	if(this.cardDrawEnabled && this.card != card){
		if(card != null){
			var from = "discard";
			this.game.hand.addCard(card);
		}
		else{
			var from = "deck";
		}
		this.disableCardDraw();
		this.game.finishTurn(this.action, this.card, from, card);
	}
};


//turn summary: p12d34
// 0 (p, d), (play, discard)
// 1 color
// 2 value
// 3 (c, d), (drew from cards, drew from discard
// 4 color
// 5 value
LC_GAMESTATUS.prototype.processLastMove = function(move){
	if(move == null) return;
	var lastMove = move.getAttribute('str');
	if(lastMove == this.lastMove) return;
	this.lastMove = lastMove;
	var output = "";
	if(this.isItMyTurn()){
		output += this.game.opp.getStyledName();
	}
	else{
		output += this.game.you.getStyledName();
	}
	if(lastMove.charAt(0) == 'p') output += " played ";
	else output += " discarded ";
	if(this.game.cardValues[parseInt('0x' + lastMove.charAt(2))] == 'x'){
		output += "an investment card";
	}
	else{
		output += "a " + this.game.cardValues[parseInt('0x' + lastMove.charAt(2))];
	}
	output += " from the <font color=\"" + this.game.colors[lastMove.charAt(1)] + "\">" + this.game.expeditions[lastMove.charAt(1)] + "</font> expedition, and drew";
	if(lastMove.charAt(4) == 'x'){
		output += " a card from the deck.";
	}
	else{
		if(this.game.cardValues[parseInt('0x' + lastMove.charAt(4))] == 'x'){
			output += " an investment card";
		}
		else{
			output += " a " + this.game.cardValues[parseInt('0x' + lastMove.charAt(5))];
		}	
		output += " from the <font color=\"" + this.game.colors[lastMove.charAt(4)] + "\">" + this.game.expeditions[lastMove.charAt(4)] + "</font> expedition from ";
		if(lastMove.charAt(3) == 'c') output += "the deck.";
		else output += "discard.";
	}

	this.game.showStatusMessage(output);
	if(this.isItMyTurn() && lastMove.charAt(3) != 'c'){
		var card = this.game.hand.knownCards[lastMove.substring(4, 6)];
		this.game.discard.removeCard(card);
		this.game.opp.sendCardToPlayer(card);
	}
};
	

	
LC_DECK = function(game){
	this.game = game;
	this.cardDeck = this.game.createElement("div", "lc_CardDeck");
	this.cardDeck.ownerObject = this;
	this.game.leftSide.appendChild(this.cardDeck);
	this.setDeckCards(0);
	this.cardDeck.onclick = this.claimCardHandler;
	
};

LC_DECK.prototype.setDeckCards = function(cards){
	if(this.cardsLeft != cards){
		this.cardsLeft = cards;
		if(cards == 0){
			this.cardDeck.style.backgroundColor = "#C96";
			this.cardDeck.style.color = "#000";
		}
		else{
			this.cardDeck.style.backgroundColor = "#669";
			this.cardDeck.style.color = "#FFF";
		}
		this.cardDeck.innerHTML = "<br/>Cards Left<br/><b>" + cards + "</b>";
	}
};

LC_DECK.prototype.processUpdate = function(deck){
	this.setDeckCards(deck.getAttribute("deckSize"));
};

LC_DECK.prototype.enableCardDraw = function(){
	this.cardDeck.style.backgroundColor = "#6C9";
	this.drawEnabled = true;
	this.cardDeck.style.cursor = "pointer";
};

LC_DECK.prototype.disableCardDraw = function(){
	this.cardDeck.style.backgroundColor = "#669";
	this.drawEnabled = false;
	this.cardDeck.style.cursor = "default";
};

LC_DECK.prototype.claimCardHandler = function(evt){
	var e;
	if(evt) e = evt;
	else e = event;
	if(this.ownerObject.claimCard){
		this.ownerObject.claimCard();
	}
	else if(e.target.ownerObject.claimCard){
		e.target.ownerObject.claimCard();
	}
	else if(e.srcElement && e.srcElement.ownerObject.claimCard){
		e.srcElement.ownerObject.claimCard();
	}
};

LC_DECK.prototype.claimCard = function(){
	this.game.status.claimCard();
};


LC_PLAYER = function(game, type){
	this.game = game;
	this.div = this.game.createElement("div", "lc_PlayerSlot");
	this.type = type;
	if(type == "player"){
		this.div.style.top = "312px";
		this.playerColor = "blue";
	}
	else if(type == "opponent"){
		this.div.style.top = "5px";
		this.playerColor = "red";
	}
	this.nameDiv = this.game.createElement("div", "lc_PlayerName");
	this.div.appendChild(this.nameDiv);
	this.roundScoreDiv = this.game.createElement("div", "lc_PlayerRoundScore");
	this.div.appendChild(this.roundScoreDiv);
	this.totalScoreDiv = this.game.createElement("div", "lc_PlayerTotalScore");
	this.div.appendChild(this.totalScoreDiv);
	this.setName("Waiting...");
	this.setRoundScore(0);
	this.setTotalScore(0);
	this.game.leftSide.appendChild(this.div);
	
	this.expeditions = new LC_EXPEDITIONS(game, type);
};



LC_PLAYER.prototype.setName = function(name){
	if(this.name != name){
		if(name != "Waiting..." && this.name == "Waiting..."){
			this.name = name;
			this.nameDiv.innerHTML = name;
			this.game.showStatusMessage(this.getStyledName() + " joins the game.");
		}
		else{
			this.name = name;
			this.nameDiv.innerHTML = name;
		}
	}
	
};

LC_PLAYER.prototype.getStyledName = function(){
	return "<font color=\"" + this.playerColor + "\">" + this.name + "</font>";

};

LC_PLAYER.prototype.setRoundScore = function(score){
	this.roundScore = score;
	this.roundScoreDiv.innerHTML = "Round Score: " + score;
};

LC_PLAYER.prototype.setTotalScore = function(score){
	this.totalScore = score;
	this.totalScoreDiv.innerHTML = "Total Score: " + score;
};

LC_PLAYER.prototype.processUpdate = function(update){
	if((update.hasAttribute && update.hasAttribute("name")) || (update.getAttribute("name") != null)) this.setName(update.getAttribute("name"));
	if((update.hasAttribute && update.hasAttribute("roundscore")) || (update.getAttribute("roundscore") != null)) this.setRoundScore(update.getAttribute("roundscore"));
	if((update.hasAttribute && update.hasAttribute("totalscore")) || (update.getAttribute("totalscore") != null)) this.setTotalScore(update.getAttribute("totalscore"));
	if((update.hasAttribute && update.hasAttribute("playedCards")) || (update.getAttribute("playedCards") != null)) this.expeditions.processUpdate(update.getAttribute("playedCards"));
};

LC_PLAYER.prototype.gainTurnPossession = function(){
	this.div.style.backgroundColor = "#CCF";
	this.game.showStatusMessage("It's " + this.getStyledName() + "'s turn");
};

LC_PLAYER.prototype.loseTurnPossession = function(){
	this.div.style.backgroundColor = "";
	this.div.style.backgroundColor = null;
};

LC_PLAYER.prototype.sendCardToPlayer = function(card){
	card.animateToPosition(100, 100, 0, 0);
};


/* TODO: Make an object of each expedition */
LC_EXPEDITIONS = function(game, type){
	this.game = game;
	this.scoreSlots = [];
	this.cards = [];
	this.cardStacks = [];
	this.expeditionSlots = [];
	this.highlightedColor = false;
	this.cardWidth = 111;
	this.cardHeight = 145;
	if(type == "player"){
		this.cardY = 245;
		this.player = this.game.you;
	}
	else{
		this.cardY = 10;
		this.player = this.game.opp;
	}
	this.cardX = 187;
	for(var i = 0; i < this.game.numExps; i++){
		this.cards[i] = [];
		var expSlot = this.game.createElement("div", "lc_ExpeditionSlots");
		expSlot.style.left = (this.getXCoordFromColor(i) - this.cardX) + "px";
		expSlot.style.top = (this.cardY-5) + "px";;
		expSlot.style.height = this.cardHeight;
		expSlot.style.backgroundColor = this.game.colors[i];
		this.expeditionSlots[i] = expSlot;
		this.game.mainBoard.appendChild(expSlot);
		var expSlotScore = this.game.createElement("div", "lc_ExpSlotScore");
		expSlotScore.style.color = this.game.fontColors[i];
		expSlotScore.style.left = (this.getXCoordFromColor(i) - this.cardX) + "px";
		expSlotScore.style.top = this.cardY + 100 + "px";
		this.scoreSlots[i] = expSlotScore;
		this.game.mainBoard.appendChild(expSlotScore);
		this.setSlotScore(i);
		var expSlotCards = this.game.createElement("div", "lc_ExpSlotCards");
		expSlotCards.style.left = (this.getXCoordFromColor(i) - this.cardX) + "px";
		expSlotCards.style.top = this.cardY + "px";
		this.cardStacks[i] = expSlotCards;
		this.game.mainBoard.appendChild(expSlotCards);
		this.updateCardStack(i);
	}
	

};

LC_EXPEDITIONS.prototype.processUpdate = function(str){
	var cards = str.divide(2);
	
	for(var i = 0; i < cards.length; i++){
		if(this.game.hand.isNewCard(cards[i])){
			this.addCard(new LC_CARD(this.game, cards[i]));
		}
		else if(!this.hasCard(cards[i])){
			this.addCard(this.game.hand.knownCards[cards[i]]);
		}
		this.game.hand.knownCards[cards[i]].movable = false;
	}
		  

};

LC_EXPEDITIONS.prototype.getXCoordFromColor = function(color){
	return this.cardX + 5 + (color * (this.cardWidth + 10));
};

LC_EXPEDITIONS.prototype.highlight = function(color){
	if(this.highlightedColor !== false){
		this.expeditionSlots[this.highlightedColor].style.opacity = 0.25;
		this.expeditionSlots[this.highlightedColor].style.filter = "alpha(opacity=25)";
	}
	this.highlightedColor = color;
	this.expeditionSlots[color].style.opacity = 1;
	this.expeditionSlots[color].style.filter = "alpha(opacity=100)";
};

LC_EXPEDITIONS.prototype.unhighlight = function(){
	if(this.highlightedColor !== false){
		this.expeditionSlots[this.highlightedColor].style.opacity = 0.25;
		this.expeditionSlots[this.highlightedColor].style.filter = "alpha(opacity=25)";
	}
	this.highlightedColor = false;
};

LC_EXPEDITIONS.prototype.isCardEligible = function(card, withoutThisCard){
	if((this.cards[card.color].length == 0) || this.cards[card.color][this.cards[card.color].length-1].isLessThan(card)){
		return true;
	}
	else if(withoutThisCard != null && withoutThisCard != false){
		if(this.cards[card.color][this.cards[card.color].length-1] == withoutThisCard){
			if((this.cards[card.color].length == 1) || this.cards[card.color][this.cards[card.color].length-2].isLessThan(card)){
				return true;
			}
		}
	}
	return false;
};

LC_EXPEDITIONS.prototype.addCard = function(card){
	card.where = "expeditions";
	this.cards[card.color].push(card);
	card.slot = false;
	this.sendCardToSlot(card);
	this.setSlotScore(card.color);
	this.updateCardStack(card.color);
};

LC_EXPEDITIONS.prototype.removeCard = function(card){
	this.cards[card.color].pop();
	this.setSlotScore(card.color);
	this.updateCardStack(card.color);
};

LC_EXPEDITIONS.prototype.updateCardStack = function(color){
	var output = "";
	for(var i = 0; i < this.cards[color].length - 1; i++){
		output += this.cards[color][i].value + "<br/>";
	}
	this.cardStacks[color].innerHTML = output;		

}

LC_EXPEDITIONS.prototype.sendCardToSlot = function(card){
	card.animateToPosition(this.getXCoordFromColor(card.color), this.cardY, this.cardWidth, this.cardHeight, this.cards[card.color].length);
};

LC_EXPEDITIONS.prototype.setSlotScore = function(slot){
	var numCards = this.cards[slot].length;
	if(numCards == 0){
		this.scoreSlots[slot].style.display = "none";
	}
	else{
		var score = this.calcSlotScore(slot);
		var output = numCards + " card";;
		this.scoreSlots[slot].style.display = "block";
		if(numCards != 1) output += "s";
		output += "<br/>" + score + " point";
		if(score != 1) output += "s";
		this.scoreSlots[slot].innerHTML = output;
	}
};

LC_EXPEDITIONS.prototype.hasCard = function(str){
	var color = str.charAt(0);
	for(var i = 0; i < this.cards[color].length; i++){
		if(this.cards[color][i].matches(str)) return true;
	}
	return false;
};


LC_EXPEDITIONS.prototype.calcSlotScore = function(slot){
	if(this.cards[slot].length == 0) return 0;
	var slotscore = -20;
	var multiplier = 1;
	for(var i = 0; i < this.cards[slot].length; i++){
		if(this.cards[slot][i].value == 'x') multiplier++;
		else slotscore += parseInt(this.cards[slot][i].value);
	}
	return multiplier * slotscore;
};
		

LC_DISCARD = function(game){
	this.game = game;
	this.cardWidth = 70;
	this.cardHeight = 70;
	this.cardY = 164;
	this.discardArea = this.game.createElement("div","lc_Discard");
	this.game.mainBoard.appendChild(this.discardArea);
	this.slotColors = [];
	this.slots = [];
	for(var i = 0; i < this.game.numExps; i++){
		var discardSlot = this.game.createElement("div","lc_DiscardSlot");
		discardSlot.style.left = (25 + (i * 121)) + "px";
		discardSlot.style.backgroundColor = this.game.colors[i];
		this.game.mainBoard.appendChild(discardSlot);
		this.slotColors[i] = discardSlot;
		this.slots[i] = [];
	}
	this.highlightedColor = false;
};


LC_DISCARD.prototype.processUpdate = function(update){
	var cards = update.getAttribute("playedCards").divide(2);
	/*
	// Remove old cards before adding new ones
	// This mess makes sure that the cards the opponent draws are sent to the opponent
	for(var i = 0; i < this.slots.length; i++){
		for(var j = 0; j < this.slots[i].length; j++){
			if(cards.exists(this.slots[i][j].str) === false){
				this.game.opp.sendCardToPlayer(this.slots[i][j]);
				this.removeCard(this.slots[i][j]);
			}
		}
	}
	*/	
			
	for(var i = 0; i < cards.length; i++){
		if(!this.hasCard(cards[i])){
			if(this.game.hand.isNewCard(cards[i])){
				this.addCard(new LC_CARD(this.game, cards[i]));
				
			}
			else{
				this.addCard(this.game.hand.knownCards[cards[i]]);
			}
			this.game.hand.knownCards[cards[i]].movable = false;
		}
	}
};

LC_DISCARD.prototype.highlight = function(color){
	if(this.highlightedColor !== false){
		this.slotColors[this.highlightedColor].style.opacity = 0.25;
		this.slotColors[this.highlightedColor].style.filter = "alpha(opacity=25)";
	}
	this.highlightedColor = color;
	this.discardArea.style.backgroundColor = "#696969";
	this.slotColors[color].style.opacity = 1;
	this.slotColors[color].style.filter = "alpha(opacity=100)";
};

LC_DISCARD.prototype.unhighlight = function(){
	if(this.highlightedColor !== false){
		this.slotColors[this.highlightedColor].style.opacity = 0.25;
		this.slotColors[this.highlightedColor].style.filter = "alpha(opacity=25)";
		this.discardArea.style.backgroundColor = "#666";
	}
	this.highlightedColor = false;
};

LC_DISCARD.prototype.getXCoordFromColor = function(color){
	return 187 + (25 + (color * 121));
};

LC_DISCARD.prototype.addCard = function(card){
	card.where = "discard";
	card.slot = false;
	this.slots[card.color].push(card);
	this.sendCardToSlot(card);
};


LC_DISCARD.prototype.sendCardToSlot = function(card){
	card.animateToPosition(this.getXCoordFromColor(card.color), this.cardY, this.cardWidth, this.cardHeight, this.slots[card.color].length);
};

LC_DISCARD.prototype.removeCard = function(card){
	if(this.slots[card.color].exists(card) !== false) this.slots[card.color].midpop(this.slots[card.color].exists(card));
		
};

LC_DISCARD.prototype.enableCardDraw = function(){
	this.drawEnabled = true;

};

LC_DISCARD.prototype.disableCardDraw = function(){
	this.drawEnabled = false;
};

LC_DISCARD.prototype.hasCard = function(cardStr){
	var color = parseInt(cardStr.charAt(0));
	for(var i = 0; i < this.slots[color].length; i++){
		if(this.slots[color][i].matches(cardStr)) return true;
	}
	return false;
};


LC_HAND = function(game){
	this.game = game;
	this.cards = [];
	this.knownCards = [];
	this.cardSize = 75;
	this.rackDiv = this.game.createElement("div","lc_YourCards");
	this.game.mainBoard.appendChild(this.rackDiv);
	this.cardSlots = [];
	for(var i = 0; i < this.game.cardsInRack; i++){
		var cardSlot = this.game.createElement("div","lc_CardSlot");
		cardSlot.style.left = (5 + (i * this.cardSize)) + "px";
		this.cardSlots[i] = cardSlot;
		this.game.mainBoard.appendChild(cardSlot);
	}
};	

LC_HAND.prototype.highlight = function(){
	for(var i = 0; i < this.cardSlots.length; i++){
		this.cardSlots[i].style.backgroundColor = "#000";
	}
};

LC_HAND.prototype.unhighlight = function(){
	for(var i = 0; i < this.cardSlots.length; i++){
		this.cardSlots[i].style.backgroundColor = "#333";
	}
};

LC_HAND.prototype.process = function(hand){
	this.setHand(hand.getAttribute('cards'));
};

LC_HAND.prototype.setHand = function(hand){
	var cards = hand.divide(2);
	for(var x = 0; x < cards.length; x++){
		
		if(this.isNewCard(cards[x])){
			this.addCard(new LC_CARD(this.game, cards[x]));
		}
		else if(this.hasCard(cards[x]) === false){
			if(this.game.status.card != this.knownCards[cards[x]]){
				this.addCard(this.knownCards[cards[x]]);
			}
		}
	}
};

LC_HAND.prototype.addCard = function(card, slot){
	card.where = "hand";
	if(slot == null || this.cards[card.slot] != null) slot = this.availableSlot();
	this.cards[slot] = card;
	card.slot = slot;
	this.sendCardToSlot(card, slot);
};

LC_HAND.prototype.setMovable = function(){
	for(var x = 0; x < this.cards.length; x++){
		this.cards[x].movable = true;
	}
};

LC_HAND.prototype.sendCardToSlot = function(card, slot){
	card.animateToPosition(this.getXCoordFromSlot(slot), 397, this.cardSize-5, this.cardSize-5, 1);
};

LC_HAND.prototype.removeCard = function(card){
	this.cards[card.slot] = null;
	card.slot = null;
};

LC_HAND.prototype.availableSlot = function(){
	for(var x = 0; x < this.game.cardsInRack; x++){
		if(this.cards[x] == null) return x;
	}
	return -1;
};

LC_HAND.prototype.isNewCard = function(cardStr){
	if(this.knownCards[cardStr] != null) return false;
	return true;
};

LC_HAND.prototype.getSlotFromCoords = function(x, y){
	if(y != null && this.game.getSection(x,y) != "rack") return false;
	return Math.floor((x - 5 - this.game.gameAreaXStart) / this.cardSize);
};

LC_HAND.prototype.getXCoordFromSlot = function(slot){
	if(slot !== false) return this.game.gameAreaXStart + 5 + (slot * this.cardSize);
	return false;
};

LC_HAND.prototype.freeSlot = function(slot){
	if(this.cards[slot] == null) return;
	for(var i = 1; i < this.game.cardsInRack; i++){
		if(slot - i >= 0){
			if(this.cards[slot - i] == null){
				for(var j = slot - i; j < slot; j++){
					this.cards[j] = this.cards[j + 1];
					this.cards[j].slot = j;
					this.sendCardToSlot(this.cards[j], j);
					
				}
				this.cards[slot] = null;
				return;
			}
		}
		if(slot + i < this.game.cardsInRack){
			if(this.cards[slot + i] == null){
				for(var j = slot + i; j > slot; j--){
					this.cards[j] = this.cards[j - 1]
					this.cards[j].slot = j;
					this.sendCardToSlot(this.cards[j], j);
				}
				this.cards[slot] = null;
				return;
			}
		}
	}
	

};

LC_HAND.prototype.hasCard = function(cardStr){
	for(var i = 0; i < this.cards.length; i++){
		if(this.cards[i] != null && this.cards[i].matches(cardStr)) return true;
	}
	return false;
};


LC_CARD = function(game, str){
	this.game = game;
	this.str = str;
	this.slot = null;
	this.where = "hand";
	this.color = parseInt(this.str.charAt(0));
	this.movable = true;
	this.value = this.game.cardValues[parseInt('0x' + this.str.charAt(1))];	
	this.game.hand.knownCards[str] = this;
	this.cardDiv = this.game.createElement("div", "lc_Card");
	this.cardDiv.style.backgroundColor = this.game.colors[this.color];
	this.cardDiv.style.color = this.game.fontColors[this.color];
	var cardExpedition = this.game.createElement("div", "lc_CardExpedition");
	cardExpedition.innerHTML = this.game.expeditions[this.color];
	this.cardDiv.appendChild(cardExpedition);
	var cardValue = this.game.createElement("div", "lc_CardValue");
	cardValue.innerHTML = this.value;
	this.cardDiv.appendChild(cardValue);
	this.cardDiv.ownerCard = this;
	this.game.lcDiv.appendChild(this.cardDiv);
	this.moveCard(23,168);
	this.resize(140,140);
	this.cardDiv.onmousedown = this.dragCardHandler;
	this.cardDiv.onclick = this.claimCardHandler;
};

LC_CARD.prototype.matches = function(str){
	return this.str == str;
};


LC_CARD.prototype.moveCard = function(x, y) {
	this.setX(x);
	this.setY(y);  
};

LC_CARD.prototype.setX = function(x){
	x = Math.round(x);
	if(x < 0) x = 0;
	this.x = x;
	this.cardDiv.style.left = this.x + "px";
};

LC_CARD.prototype.setY = function(y){
	y = Math.round(y);
	if(y < 0) y = 0;
	this.y = y;
	this.cardDiv.style.top = this.y + "px";
};

LC_CARD.prototype.resize = function(width, height) {
	this.setWidth(width);
	this.setHeight(height);
};

LC_CARD.prototype.setWidth = function(width){
	width = Math.floor(width);
	this.width = width;
	this.cardDiv.style.width = width + "px";
};

LC_CARD.prototype.setHeight = function(height){
	height = Math.floor(height);
	this.height = height;
	this.cardDiv.style.height = height + "px";
};



//I created these handlers because the definition of 'this' and the difference in event handling between browsers can cause confusion.
LC_CARD.prototype.dragCardHandler = function(evt){
	var e;
	if(evt) e = evt;
	else e = event;
	if(this.ownerCard.dragCard){
		this.ownerCard.dragCard(e);
	}
	else if(e.target.ownerCard.dragCard){
		e.target.ownerCard.dragCard(e);
	}
	else if(e.srcElement.ownerCard.dragCard){
		e.srcElement.ownerCard.dragCard(e);
	}
};


LC_CARD.prototype.dragCard = function(event){
	if(this.game.status.isItMyTurn() == false || this.movable == false) return;
	LC_MOVING_CARD = this;
	this.xSave = this.x;
	this.ySave = this.y;
	
	this.offsetX = this.game.getClientX(event) - this.x;// this.game.getLayerX(event);
	this.offsetY = this.game.getClientY(event) - this.y;//this.game.getLayerY(event);

         
	var zindexSave = this.cardDiv.style.zIndex; 
   
	this.cardDiv.style.zIndex = 99;
	if(document.addEventListener){
		document.addEventListener("mousemove", LC_MOVING_CARD.moveCardHandler, true);
		document.addEventListener("mouseup", LC_MOVING_CARD.dropCardHandler, true);
	}
	else{
		document.attachEvent("onmousemove", LC_MOVING_CARD.moveCardHandler);
		document.attachEvent("onmouseup", LC_MOVING_CARD.dropCardHandler);
	}
	if(event.stopPropagation) event.stopPropagation();
	
	
};

LC_CARD.prototype.moveCardHandler = function(evt){
	var e;
	if(evt) e = evt;
	else e = event;
	LC_MOVING_CARD.moveCardDrag(e);
	if(e.stopPropagation) e.stopPropagation();
};

LC_CARD.prototype.moveCardDrag = function(event){
	this.moveCard(this.game.getClientX(event) - this.offsetX, this.game.getClientY(event) - this.offsetY);
	this.game.highlightActiveArea(this.game.getClientX(event), this.game.getClientY(event), this.color);
};

LC_CARD.prototype.dropCardHandler = function(evt){
	var e;
	if(evt) e = evt;
	else e = event;
	LC_MOVING_CARD.dropCard(e);
};
        

LC_CARD.prototype.dropCard = function(event) {
	this.game.status.recordAction(this.game.getSection(this.game.getClientX(event), this.game.getClientY(event)), this, this.game.getClientX(event));
	this.game.highlightActiveArea();
	if(document.addEventListener){
		document.removeEventListener("mouseup", LC_MOVING_CARD.dropCardHandler, true);
		document.removeEventListener("mousemove", LC_MOVING_CARD.moveCardHandler, true);
	}
	else{
		document.detachEvent("onmousemove", LC_MOVING_CARD.moveCardHandler);
		document.detachEvent("onmouseup", LC_MOVING_CARD.dropCardHandler);
	}
	this.cardDiv.style.zIndex = 5;
	if(event.stopPropagation) event.stopPropagation();
};



// TODO: Card must animate from slot not in hand, since not all animated cards are in hand slots.
// probably use the knowncards array.
LC_CARD.prototype.animateToPosition = function(goalX, goalY, goalWidth, goalHeight, zIndex){
	var frames = 20;
	var frameTime = 25;
	var dX = goalX - this.x;
	var dY = goalY - this.y;
	var dW = goalWidth - this.width;
	var dH = goalHeight - this.height;
	if(dX != 0 || dY != 0 || dW != 0 || dH != 0){
		//var amt = frames*frames/4;
		var newx, newy, neww, newh;
		var ma;
		var z = 2/(frames * frames);
		for(var i = 0; i < frames; i++){
			if(i <= frames/2) ma = z*(i*(i+1));
			else ma = 1 - (z*((frames-i)*((frames-i)+1)));
			newx = (this.x + (dX*ma));
			newy = (this.y + (dY*ma));
			neww = (this.width + (dW*ma));
			newh = (this.height + (dH*ma));
			// I know, I know, eval-ing code is slow and ugly. I'll find a way around this hopefully.
			setTimeout("window.lcGames['" + this.game.name + "'].hand.knownCards['" + this.str + "'].moveAndResize(" + newx + "," + newy + "," + neww + "," + newh + ", 99)", (i+1)*frameTime);
		}
		setTimeout("window.lcGames['" + this.game.name + "'].hand.knownCards['" + this.str + "'].moveAndResize(" + goalX + "," + goalY + "," + goalWidth + "," + goalHeight + "," + zIndex + ")", (frames+1)*frameTime);
	}
};





LC_CARD.prototype.moveAndResize = function(x, y, w, h, z){
	this.resize(w, h);
	this.moveCard(x, y);
	if(z != -1 && z != null) this.cardDiv.style.zIndex = z;
};


LC_CARD.prototype.claimCardHandler = function(evt){
	var e;
	if(evt) e = evt;
	else e = event;
	if(this.ownerCard.claimCard){
		this.ownerCard.claimCard();
	}
	else if(e.target.dragCard){
		e.target.ownerCard.claimCard();
	}
	else if(e.srcElement.dragCard){
		e.srcElement.ownerCard.claimCard();
	}
};

LC_CARD.prototype.claimCard = function(){
	if(this.where == "discard" && this.game.discard.drawEnabled == true){
		this.game.status.claimCard(this);
	}
};

LC_CARD.prototype.isLessThan = function(card){
	if(this.value == 'x') return true;
	if(card.value == 'x') return false;
	if(parseInt(this.value) < parseInt(card.value)) return true;
	return false;
};


LC_CHAT = function(game){
	this.game = game;
	this.sendChatPage = "chatSendComment.php";
	this.lastId = 0;
	this.chatSender = this.game.makeXHR();
	this.game.chatDiv.innerHTML += "<div class=\"lc_ChatBox\"></div>"
		+ "<input type=\"text\" maxlength=\"300\" class=\"lc_ChatInput\" onKeyDown=\"return window.lcGames['" + this.game.name + "'].chat.messageKeyDown(event);\"/>"
		+ "<input type=\"button\" onClick=\"window.lcGames['" + this.game.name + "'].chat.sendComment()\" value=\"Send\" CLASS=\"lc_ChatBtn\"/>";
	//Messing with innerHTML causes the pointers to lose track of where they are
	this.chatBox = this.game.chatDiv.firstChild;
	this.chatMessage = this.chatBox.nextSibling;
	this.chatBtn = this.chatMessage.nextSibling;
};

// borrowed from http://www.webreference.com/programming/java_dhtml/chap8/2/
LC_CHAT.prototype.messageKeyDown = function(evt) {
	evt = (evt) ? evt : event;
	var charCode = (evt.charCode) ? evt.charCode : ((evt.which) ? evt.which : evt.keyCode);
	if (charCode == 13 || charCode == 3) {
		this.sendComment();
		return false;
	} else {
		return true;
	}
};

// Sends the comment to the server
LC_CHAT.prototype.sendComment = function(){
	if(this.chatMessage.value != ""){
		this.disableChat();
		this.game.sendPostQuery(this.chatSender, this.sendChatPage, "window.lcGames['" + this.game.name + "'].chat.commentSent()", {"msg" : this.chatMessage.value});
	}
};

// After the comment is sent, re-enables the chat boxes.
LC_CHAT.prototype.commentSent = function(){
	if(this.game.isXHRReady(this.chatSender)){
		this.enableChat();
		this.chatMessage.value = "";
		this.chatMessage.focus();
	}
};

LC_CHAT.prototype.enableChat = function(){
	this.chatMessage.disabled = false;
	this.chatBtn.disabled = false;
	this.chatMessage.className = "lc_ChatInput";
	this.chatBtn.className = "lc_ChatBtn";
};

LC_CHAT.prototype.disableChat = function(){
	this.chatMessage.disabled = true;
	this.chatBtn.disabled = true;
	this.chatMessage.className = "lc_ChatInput lc_Disabled";
	this.chatBtn.className = "lc_ChatBtn lc_Disabled";
};

LC_CHAT.prototype.processUpdate = function(update){
	if(update.getAttribute("lastId") != "")this.lastId = update.getAttribute("lastId");
	newPosts = update.getElementsByTagName('msg');
	for(var i = 0; i < newPosts.length; i++){
		this.postMessage(newPosts[i].getAttribute('name'), newPosts[i].firstChild.data);
	}
	if(newPosts.length > 0) this.chatBox.scrollTop = this.chatBox.scrollHeight;
};

LC_CHAT.prototype.postMessage = function(name, message){
	var className;
	if(name == "Guest") className="lc_ChatGuest";
	else if(name == this.game.you.name) className = "lc_ChatPlayerName";
	else className = "lc_ChatOppName";
	this.chatBox.innerHTML += "<span class=\"" + className + "\">" + name + ":</SPAN>&nbsp;&nbsp;&nbsp;<span>" + message + "</span><br/>";
};

//divides a string into an array of substrings, each with the passed length
String.prototype.divide = function(length){
	if(length <= 0) return;
	var arr = [];
	for(var x = 0; x * length < this.length; x++){
		arr[arr.length] = this.substring(x*length, Math.min((x+1)*length, this.length));
	}
	return arr;
		
};


// pops from the middle of the array, shifts everything afterward down, deletes the last item
Array.prototype.midpop = function(index) {
	var temp = this[index];
	for(var i = index; i + 1 < this.length; i++){
		this[i] = this[i+1];
	}
	if(this.length > 0) this.pop();
    return temp;
};

Array.prototype.exists = function(obj){
	for(var i = 0; i < this.length; i++){
		if(this[i] == obj) return i;
	}
	return false;
};


