/* Poker.js */

// (version 0.2)
//
// Copyright (c) 2006. Adobe Systems Incorporated.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of Adobe Systems Incorporated nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

var gSuits = { club: 0, spade: 0, diamond: 0, heart: 0 };
var gDeckArray = [];
var gHashArray = {};
var gNextID = 0;

var JACK  = 11;
var QUEEN = 12;
var KING  = 13;
var ACE   = 14;

var ONEMONTH = 2592000000; // (1000*60*60*24*30)

for (var suit in gSuits)
{
	for (var i = 2; i < 15; i++)
	{
		var cardObj = new Object;
		cardObj.suit = suit;
		cardObj.number = i;
		cardObj.ds_RowID = gNextID++;
		if (i == JACK)
			cardObj.string = "J";
		else if (i == QUEEN)
			cardObj.string = "Q";
		else if (i == KING)
			cardObj.string = "K";
		else if (i == ACE)
			cardObj.string = "A";
		else
			cardObj.string = cardObj.number + "";

		gDeckArray.push(cardObj);
		gHashArray[cardObj.ds_RowID] = cardObj;
	}
}

var gDealNewHand = true;
var gHand = [ -1, -1, -1, -1, -1 ];
var gNextCardIndex = 0;
var gScore = 200;
var gMaxBet = 5;

var dsCardDeck = new Spry.Data.DataSet();
dsCardDeck.data = gDeckArray;
dsCardDeck.dataHash = gHashArray;
dsCardDeck.filterFunc = function() { return null; };
/*
var obs = {};
obs.onDataChanged = function(ds, type) { setTimeout( function() { dsCardDeck.removeObserver(obs); DealCards(); }, 0); };
dsCardDeck.addObserver(obs);
*/
dsCardDeck.loadData();

function RandomSort(a, b)
{
	if (Math.random() * 2 > 1)
		return 1;
	return -1;
}

function ShuffleDeck()
{
	for (var i = 0; i < 5; i++)
		dsCardDeck.getUnfilteredData().sort(RandomSort);
	gNextCardIndex = 0;
	ResetHand();
}

function ResetHand()
{
	for (var i = 0; i < 5; i++)
		gHand[i] = -1;
}

function filterHand(ds, row, rowNumber)
{
	if (rowNumber >= 5)
		return null;
	return ds.getUnfilteredData()[gHand[rowNumber]];
}

function UpdateScore()
{
	$("score").innerHTML = gScore;
	if ($("saveCB").checked)
		SaveScore(gScore);
}

function DealCards()
{
	$("dealBtn").disabled = true;

	if (gDealNewHand)
	{
		ShuffleDeck();
		if (!gScore)
			gScore = 200;
		gScore -= gMaxBet;
		UpdateScore();
	}

	var numCardsDealt = 0;
	var origHand = [];
	for (var i = 0; i < 5; i++)
	{
		origHand[i] = gHand[i];
		if (gHand[i] == -1)
		{
			gHand[i] = gNextCardIndex++;
			++numCardsDealt;
		}
	}

	var ffunc = function() {
		if (!gDealNewHand)
		{
			var result = CheckHand();
			Spry.Data.getRegion("result").setState(result);
			if (result != "Loser")
			{
				gScore += gWinnings[result].points;
				UpdateScore();
			}
		}

		ResetHand();
		gDealNewHand = !gDealNewHand;
		$("dealBtn").disabled = false;
	}

	if (numCardsDealt > 0)
	{
		AddDealerAnimation(origHand, ffunc);
		dsCardDeck.filter(filterHand);
	}
	else
		ffunc();
}

function getRemoveClassFunc(ele, className)
{
	return function() { Spry.Utils.removeClassName($(ele), className); };
}

function AddDealerAnimation(hand, efunc)
{
	var obj = {};
	obj.onPostUpdate = function() {
		var hasEffects = false;
		Spry.Data.Region.removeObserver("cardContainer", obj);
		var e = new Spry.Effect.Cluster({ finish: efunc });

		// Add the effects to deal out the cards.

		for (var i = 0; i < 5; i++)
		{
			if (hand[i] == -1)
			{
				var cardEle = $("Card"+i);
				Spry.Utils.addClassName(cardEle, "cardback");
				e.addNextEffect(new Spry.Effect.Move(cardEle, {x: -1500, y: 0, units: "px"}, { x: 0, y: 0, units: "px"}, { duration: 500, transition: Spry.sinusoidalTransition }));
				hasEffects = true;
			}
			else
			{
				$("Card"+i).style.left = "";
				$("Card"+i).style.top = "";
			}
		}

		// Add the effects to flip over the cards.

		for (var i = 0; hasEffects && i < 5; i++)
		{
			if (hand[i] == -1)
			{
				e.addNextEffect(new Spry.Effect.Opacity($("Card"+i), 1, 0, { duration: 250, finish: getRemoveClassFunc("Card"+i, "cardback") }));
				e.addNextEffect(new Spry.Effect.Opacity($("Card"+i), 0, 1, { duration: 250 }));
			}
		}

		if (hasEffects)
			e.start();
	};
	Spry.Data.Region.addObserver("cardContainer", obj);
}

function ToggleHold(cardNumber)
{
	if (gDealNewHand)
		return;
		
	gHand[cardNumber] = (gHand[cardNumber] == cardNumber) ? -1 : cardNumber;
	if (gHand[cardNumber] == -1)
		Spry.Utils.removeClassName($("Card" + cardNumber), "hold");
	else
		Spry.Utils.addClassName($("Card" + cardNumber), "hold");
}

function Winning(name, points)
{
	this.name = name;
	this.points = points;
}

var gWinnings = {};
gWinnings["RoyalFlush"] =    new Winning("Royal Flush",      4000);
gWinnings["StraightFlush"] = new Winning("Straight Flush",    250);
gWinnings["FourOfAKind"] =   new Winning("Four Of A Kind",    125);
gWinnings["FullHouse"] =     new Winning("Full House",         45);
gWinnings["Flush"] =         new Winning("Flush",              30);
gWinnings["Straight"] =      new Winning("Straight",           20);
gWinnings["ThreeOfAKind"] =  new Winning("Three Of A Kind",    15);
gWinnings["TwoPairs"] =      new Winning("Two Pairs",          10);
gWinnings["Pair"] =          new Winning("Pair",                5);

var gHandType = [];
gHandType[17] = "FourOfAKind";
gHandType[13] = "FullHouse";
gHandType[11] = "ThreeOfAKind";
gHandType[9] = "TwoPairs";
gHandType[7] = "Pair";

function CheckHand()
{
	var retType = "Loser";
	var cardCount = [];
	var suitCount = { club: 0, spade: 0, diamond: 0, heart: 0 };
	
	var data = dsCardDeck.getUnfilteredData();
	var card = null;
	var val = 0;
	var lastSuit = data[gHand[0]].suit;
	var isFlush = true;

	// Count the number of times a specific number and suit
	// appears in our hand.

	for (var i = 0; i < 5; i++)
	{
		card = data[gHand[i]];
		val = cardCount[card.number];
		cardCount[card.number] = (!val) ? 1 : ++val;
		isFlush = isFlush && (lastSuit == card.suit);
	}

	if (isFlush)
		retType = "Flush";
	else
	{
		// We don't have a flush, so add together all of the counts
		// we just calculated. If the number is greater than 5, we
		// know we have a pair or better.
	
		var isJackOrBetter = false;
		var total = 0;
	
		for (i = 0; i < 5; i++)
		{
			card = 	data[gHand[i]];
			val = cardCount[card.number];
			if (!isJackOrBetter && card.number >= 11 && val > 1)
				isJackOrBetter = true;
			total += val;
		}
	
		// If a number appears more than once in our hand,
		// make sure it is at least a pair of jacks or better,
		// and return.
	
		var handType = gHandType[total];
		if (handType)
		{
			if (handType == "Pair")
			{
				if (!isJackOrBetter)
					return retType;
			}
	
			return handType;
		}
	}

	// Now check to see if we have a straight. We do this by
	// running through our cardCount array and making sure we
	// have 5 consecutive non-zero counts.

	var scount = 0;
	for (i = 0; i < cardCount.length; i++)
	{
		if (cardCount[i])
		{
			if (i == 2 && cardCount[ACE])
			{
				// Our first card is a '2', so count the
				// ACE as a '1' card.
				++scount;
			}

			if (++scount == 5)
			{
				// We have a straight! Check to see if it's a Royal or Straight Flush.

				if (retType == "Flush")
					retType = (i == ACE) ? "RoyalFlush" : "StraightFlush";
				else
					retType = "Straight";
			}
		}
		else
		{
			if (scount)
				break;
		}
	}

	return retType;
}

function SetCookie(name, value, expires)
{
	document.cookie = name + "=" + escape(value) + "; expires=" + expires;
}

function ClearCookie(name)
{
	var expires = new Date;
	expires.setTime(expires.getTime() - 1);
	document.cookie = name + "=; expires=" + expires;
}

function GetCookie(name)
{
	var c = document.cookie;
	var value = null;

	if (c)
	{
		name += "=";

		var start = c.indexOf(name);
		if (start != -1)
		{
			start += name.length;
			end = c.indexOf(";", start);
			end = (end == -1) ? c.length : end;
			value = unescape(c.substring(start, end));
		}
	}

	return value;
}

function SaveScore(score)
{
	var expires = new Date;
	expires.setTime(expires.getTime() + ONEMONTH);
	SetCookie("score", gScore, expires.toGMTString());
}

function ClearSavedScore()
{
	ClearCookie("score");
}

function GetSavedScore()
{
	var score = GetCookie("score");
	if (!score)
		return -1;
	return parseInt(score);
}

function ToggleSave()
{
	if ($("saveCB").checked)
		SaveScore(gScore);
	else
		ClearSavedScore();
}

Spry.Utils.addLoadListener(function()
{
	var score = GetSavedScore();
	if (score != -1)
	{
		$("saveCB").checked = true;
		gScore = score;
		UpdateScore();
	}
	else
		$("saveCB").checked = false;
});