/*****
*
*	Two-Player Memory Match
*	Version: 1.0
*	Last Revision: 04.09.2007
*	steve@slayeroffice.com
*
*	Also uses the WIM API, documented here:
*	http://developer.aim.com/web-aim
*
******/

function dummy(){};

var d = document;
var mm = {
	cap:"550e8400e29b41d4a716446655440000",
	invitationTimeout:10000,
	inGame:false,
	opponent:null,
	dataIMGranted:false,
	init: function() {
		mm.ui.createDeck();
		mm.ui.createUI();
		mm.ui.dealDeck();
		
		AIM.params.callbacks.listener.dataIM = ["mm.com.acceptIncomingData"];
		AIM.params.callbacks.sendDataIM = ["mm.com.acceptDataIM"];
		AIM.params.callbacks.listener.buddylist = ["dummy"];
		
		AIM.params.DEBUG = false;
		AIM.params.wimKey = "ih1lb685w1HENSGU";
		AIM.params.assertCaps = mm.cap;
		AIM.params.interestCaps = mm.cap;
		AIM.core.subscriptions = "buddylist,dataIM";
		AIM.transactions.getToken(AIM.core.subscriptions);
	},
	com: {
		acceptIncomingData: function(json) {
			switch(json.dataType) {
				case "invite":
					if(mm.inGame) {
						AIM.transactions.sendDataIM(json.source.aimId,"Sorry, " + AIM.params.user + " is already engaged in a match.",mm.cap,"Deny");
						return;
					}
					if(confirm(json.source.displayId + " has challenged you to a match. Accept?")) {
						AIM.transactions.sendDataIM(json.source.aimId, AIM.params.user + " has accepted your invitation.",mm.cap,"Accept");
						mm.opponent = json.source.aimId;
						mm.ui.setTurn(0);
						mm.ui.prepWork();
						mm.ui.myTurn = true;
					} else {
						AIM.transactions.sendDataIM(json.source.aimId,"Sorry, " + AIM.params.user + " has declined your invitation.",mm.cap,"Deny");
						alert("You have declined the invitation.");
					}
					break;
				case "accept":
					d.getElementById("msgBox").innerHTML = json.source.displayId + " has accepted your invitation!";
					mm.opponent = json.source.aimId;
					var i = mm.ui.cards.length;
					var oString = "{oData={dType:'deck',data:[";
					while(i-->0) {
						oString+="{s:'" + mm.ui.cards[i].suit + "',v:'" + mm.ui.cards[i].value + "'},";
					}
					oString += "]}}";
					oString = oString.replace(/,\]/,"]");
					AIM.transactions.sendDataIM(json.source.aimId,oString,mm.cap,"Data");
					mm.ui.myTurn = false;
					mm.ui.setTurn(1);
					mm.ui.prepWork();
					break;
				case "deny":
					alert(json.dataIM);
					d.getElementById("msgBox").innerHTML =  "<a href=\"javascript:mm.com.sendInvite(true);\">Send an invitation</a> or wait for an invite.";
					break;
				case "end":
					alert(mm.opponent + " has ended the session unexpectedly.");
					mm.inGame = false;
					location.reload();
					break;
				case "data":
					var oData = eval(json.dataIM);
					switch(oData.dType) {
						// rebuild the deal so they are the same
						case "deck":
							var i = oData.data.length;
							oData.data = oData.data.reverse();
							while(i-->0) {
								mm.ui.cards[i].suit = oData.data[i].s;
								mm.ui.cards[i].value = oData.data[i].v;
							}
							mm.ui.deleteDeck();
							mm.ui.dealDeck();
							break;
						case "chat":
							var cContainer = d.getElementById("chatWindow");
							var str = "<b style=\"color:blue;\">" + json.source.displayId + "</b>: " + oData.data + "<br />";
							cContainer.innerHTML += str;
							cContainer.scrollTop = cContainer.scrollHeight;
							break;
						case "move":
							var oCard = mm.ui.getElementByXIndex(oData.data);
							oCard.className = "card cardFace " + mm.ui.cards[oData.data].suit;			
							break;
						case "turnOver":
							if(oData.success) {
								var fns = function() {
									mm.ui.getElementByXIndex(oData.card1).style.display = "none";
									mm.ui.getElementByXIndex(oData.card2).style.display = "none";
									mm.ui.myTurn = false;
									mm.ui.opAttempts++;
									mm.ui.opMatches++;
									mm.ui.setTurn(1);
									d.getElementById("dataDisplay").innerHTML = mm.ui.showStats();
									if(mm.ui.matches + mm.ui.opMatches == 26) mm.ui.endGame();
								}
							} else {
								var fns = function() {
									mm.ui.getElementByXIndex(oData.card1).className = "card cardBack";
									mm.ui.getElementByXIndex(oData.card2).className = "card cardBack";
									mm.ui.myTurn = true;
									mm.ui.opAttempts++;
									mm.ui.setTurn(0);
									d.getElementById("dataDisplay").innerHTML = mm.ui.showStats();
								}
							}
							setTimeout(fns,900);
							break;
					}
					break;
			}
		},
		acceptDataIM: function(json) {
			switch(json.response.statusCode) {
				case 200:
					break;
				case 450:
					AIM.core.createAuthWindow(json.response.data.redirectURL + "?k=" + AIM.params.wimKey);
					AIM.core.pendingTransaction = {
						msg:AIM.core.AIMData[json.response.requestId].objData.data,
						to:AIM.core.AIMData[json.response.requestId].objData.to,
						type:"sendDataIM",
						cap:AIM.core.AIMData[json.response.requestId].objData.cap,
						dType:AIM.core.AIMData[json.response.requestId].objData.dType
					}
					break;
				case 602:
					alert("That user is not currently online.");
					d.getElementById("msgBox").innerHTML = "<a href=\"javascript:mm.com.sendInvite(true);\">Send an invitation</a> or wait for an invite.";
					break;
				case 605:
					alert("That user is not currently using the Memory Match client.");
					d.getElementById("msgBox").innerHTML = "<a href=\"javascript:mm.com.sendInvite(true);\">Send an invitation</a> or wait for an invite.";
					break;
				default:
					alert("Something unexpected occured. Error code is " + json.response.statusCode);
					break;
			}
		},
		sendInvite: function(oNew) {
			if(oNew) {
				var oSN = prompt("Enter the screen name of the person you'd like to send in invitation to:","");
			} else {
				var oSN = mm.opponent;
			}
			if(!oSN) return;
			if(oSN.toLowerCase() == AIM.params.user.toLowerCase()) return alert("You can't play yourself!");
			AIM.transactions.sendDataIM(oSN,"hello",mm.cap,"Invite");
			d.getElementById("msgBox").innerHTML = "Invitation sent...waiting for response.";
			var fn = function() {
				alert("The invitation has timed out. Please try again or invite another player.");
				d.getElementById("msgBox").innerHTML = "<a href=\"javascript:mm.com.sendInvite(true);\">Send an invitation</a> or wait for an invite.";
			}
		}
	},
	ui: {
		attempts:0,
		matches:0,
		opAttempts:0,
		opMatches:0,
		canClick:true,
		cards:[],
		myTurn:false,
		gameOver:true,
		opClick:true,
		clickData: {
			firstClick:true,
			clickValue:null,
			clickIndex:null,
			clickElement:null
		},
		firstClick:true,
		firstClickValue:null,
		
		showStats: function() {
			return "<b style=\"color:red;\">" + AIM.params.user + "</b>" + ": Attempts: " + mm.ui.attempts + " || Matches: " + mm.ui.matches + " || Accuracy: " + mm.ui.calculateAccuracy(1) + "%<br /><b style=\"color:blue;\">" + mm.opponent + "</b>: Attempts: " + mm.ui.opAttempts + " || Matches: " + mm.ui.opMatches + " || Accuracy: " + mm.ui.calculateAccuracy(0) + "%";
		},
		
		setTurn: function(who) {
			if(who) {
				d.getElementById("turn").className="theirTurn";
				d.getElementById("turn").setAttribute("title","It is your opponent's turn.");
			} else {
				d.getElementById("turn").className="yourTurn";
				d.getElementById("turn").setAttribute("title","It is your turn.");
			}
		},
		
		prepWork: function() {
			d.getElementById("dataDisplay").innerHTML = mm.ui.showStats();
			d.getElementById("chatWindow").style.display = "block";
			d.getElementById("chatInput").style.display = "block";
			d.getElementById("msgBox").style.display = "none";
			d.getElementById("turn").style.display = "block";
			mm.inGame = true;
			mm.ui.gameOver = false;
		},
		
		createDeck: function() {
			var suits = ["spade","heart","club","diamond"];
			var values = ["A","2","3","4","5","6","7","8","9","10","J","Q","K"];
			var i = suits.length;
			var k = 0;
			while(i-->0) {
				var j = values.length;
				while(j-->0) {
					mm.ui.cards[k] = {
						suit:suits[i],
						value:values[j],
						clicked:0
					}
				k++;
				}
			}
			fn = function() {return (Math.round(Math.random())-0.5); }
			mm.ui.cards = mm.ui.cards.sort(fn);
		},
		
		deleteDeck: function() {
			var oCards = AIM.util.getElementsByClassName(d.getElementById("mContainer"),"div","card");
			var i = oCards.length
			while(i-->0) oCards[i].parentNode.removeChild(oCards[i]);
		},
		createUI:function() {
			var oAttempts = d.createElement("div");
			oAttempts.setAttribute("id","dataDisplay");
			d.getElementById("mContainer").appendChild(oAttempts);
			
			var oMessage = d.createElement("div");
			oMessage.innerHTML = "<a href=\"javascript:mm.com.sendInvite(true);\">Send an invitation</a> or wait for an invite.";
			oMessage.setAttribute("id","msgBox");
			d.getElementById("mContainer").appendChild(oMessage);
			
			var oTurn = d.createElement("div");
			oTurn.innerHTML = "";
			oTurn.setAttribute("id","turn");
			d.getElementById("mContainer").appendChild(oTurn);
			
			var oChatWindow = d.createElement("div");
			oChatWindow.setAttribute("id","chatWindow");
			d.getElementById("mContainer").appendChild(oChatWindow);
			
			var oChatInput = d.createElement("input");
			oChatInput.setAttribute("id","chatInput");
			oChatInput.setAttribute("type","text");
			oChatInput.onkeyup = function(e) {
				var keyCode = window.event?event.keyCode:e.keyCode;
				if(keyCode == 13) {
					var cContainer = d.getElementById("chatWindow");
					cContainer.innerHTML += "<b style=\"color:red;\">" + AIM.params.user + "</b>: " + this.value + "<br />";
					cContainer.scrollTop = cContainer.scrollHeight;
					AIM.transactions.sendDataIM(mm.opponent,"{nData={dType:'chat',data:'" + this.value + "'}}",mm.cap,"Data");
					this.value = "";	
				}
			}
			d.getElementById("mContainer").appendChild(oChatInput);
		},
		
		dealDeck: function() {
			var i = mm.ui.cards.length;
			var oFrag = d.createDocumentFragment();
			var x=5,y=5;
			while(i-->0) {
				var o = d.createElement("div");
				o.xIndex = i;
				o.className = "card cardBack";
				o.style.top = y + "px";
				o.style.left = x + "px";
				o.onclick = function() {
					if(!mm.ui.canClick || mm.ui.gameOver || !mm.ui.myTurn)return;
					var oVal = mm.ui.cards[this.xIndex].value;
					AIM.transactions.sendDataIM(mm.opponent,"{nData={dType:'move',data:" + this.xIndex + "}}",mm.cap,"Data");
					this.className = "card cardFace " + mm.ui.cards[this.xIndex].suit;
					var me = this;
					if(mm.ui.clickData.firstClick) {
						mm.ui.clickData.clickValue = oVal;
						mm.ui.clickData.clickIndex = this.xIndex;
						mm.ui.clickData.firstClick = false;
						mm.ui.clickData.clickElement = this;
						mm.ui.cards[this.xIndex].clicked++;
					} else {
						if(this.xIndex == mm.ui.clickData.clickIndex) return;
						mm.ui.cards[this.xIndex].clicked++;
						mm.ui.attempts++;
						if(oVal == mm.ui.clickData.clickValue) {
							var fn = function() {
								me.style.display = "none";
								mm.ui.clickData.clickElement.style.display = "none";
								mm.ui.canClick = true;
								mm.ui.matches++;
								if(mm.ui.matches + mm.ui.opMatches == 26) mm.ui.endGame();
								 mm.ui.setTurn(0);
								d.getElementById("dataDisplay").innerHTML =mm.ui.showStats();
							}
							var success = 1;
							mm.ui.myTurn = true;
						} else {
							mm.ui.canClick = false;
							var fn = function() { 
								me.className = "card cardBack";
								mm.ui.clickData.clickElement.className = "card cardBack";
								mm.ui.canClick = true;
								mm.ui.setTurn(1);
								d.getElementById("dataDisplay").innerHTML = mm.ui.showStats();
							}
							var success = 0;
							mm.ui.myTurn = false;
							
						}
						AIM.transactions.sendDataIM(mm.opponent,"{nData={dType:'turnOver',success:" + success + ",card1:" + this.xIndex + ",card2:" + mm.ui.clickData.clickElement.xIndex + "}}",mm.cap,"Data"); 
						setTimeout(fn,1000);
						mm.ui.clickData.firstClick = true;
					}
				}
				
				x+=40;
				if(x>=512) {
					x=5; y+=50;
				}
				o.innerHTML = mm.ui.cards[i].value;
				oFrag.appendChild(o);
			}
			d.getElementById("mContainer").appendChild(oFrag);
		},
		
		replay: function(sameOp) {
			mm.ui.deleteDeck();
			mm.ui.createDeck();
			mm.ui.dealDeck();
			if(sameOp) {
				mm.com.sendInvite(false);
			} else {
				mm.com.sendInvite(true);
			}
		},
		
		calculateAccuracy: function(who) {
			if(who) {
				if(mm.ui.matches==0) {
					return 0;
				} else {
					return Math.round((mm.ui.matches/mm.ui.attempts)*100);
				}
			} else {
				if(mm.ui.opMatches == 0) {
				 	return 0;
				} else {
					return Math.round((mm.ui.opMatches/mm.ui.opAttempts)*100);
				}
			}
		},
		
		getElementByXIndex: function(xIndex) {
			var oCards = AIM.util.getElementsByClassName(d.getElementById("mContainer"),"div","card");
			var i = oCards.length;
			while(i-->0) if(oCards[i].xIndex == xIndex) return oCards[i];
			return 0;
		},
		
		endGame: function() {
			mm.inGame = false;
			mm.ui.gameOver = true;
			mm.ui.matches = 0; mm.ui.opMatches = 0;
			mm.ui.attempts = 0; mm.ui.opAttempts = 0;
			mm.ui.clickData.firstClick=true;
			mm.ui.clickData.clickValue=null;
			mm.ui.clickData.clickIndex=null;
			mm.ui.clickData.clickElement=null;
			mm.ui.firstClick=true,
			mm.ui.firstClickValue=null,
			d.getElementById("msgBox").innerHTML = "GAME OVER!<br /> <a href=\"javascript:mm.ui.replay(1);\">Play each other again?</a><br /><a href=\"javascript:mm.ui.replay(0);\">Play Someone Else</a>";
			d.getElementById("msgBox").style.display = "block";
		}
	}
}
window.onload = mm.init;
window.onunload = function() {
	if(mm.inGame) {
		mm.inGame = false;
		AIM.transactions.sendDataIM(mm.opponent,"bye",mm.cap,"End");
		AIM.transactions.endSession();
	}
}
