/* Solitaire - robert@cosmicrealms.com */

var gGames = {};

/**
 * Global constants
 */
var CONSTANTS = 
{
	STATE_LOADING : 0,
	STATE_DEALING : 1,
	STATE_PLAYING : 3,
	STATE_WON : 4,
	STATE_LOST : 5,
	
	AUTOPLAY_MIN_INTERVAL : 600,
	AUTOPLAY_MAX_INTERVAL : 1000,
	
	ANIMATE_SPEED_FASTEST : 0.05,
	ANIMATE_SPEED_SLOWEST : 5.0,
	
	CARD_SPACING_X : 20,
	CARD_SPACING_Y : 20,
	
	CARD_WIN_MOVE_SPEED : 0.1,
	CARD_WIN_EXPLODE_SPEED_PER_CARD : 0.05,
	CARD_AUTOPLAY_SPEED : 0.5,
	CARD_DEAL_SPEED : 0.3,
	CARD_MOVE_SPEED : 0.1,
	CARD_DEAL_TURN_OVER_DELAY : 90,
	CARD_TURN_OVER_SPEED : 0.04,
	CARD_TURN_OVER_DELAY : 60,
	
	BOTTOM_OF_STACK : 987654321,
	
	IS_OFFLINE : true
};

function Solitaire()
{
	this.state = CONSTANTS.STATE_LOADING;
	this.lastState = CONSTANTS.STATE_LOADING;
	this.tinyScreen = false;
	this.isResizing = false;
	this.wii = false;
	
	this.CARD_AUTOPLAY_SPEED = CONSTANTS.CARD_AUTOPLAY_SPEED;
	this.CARD_DEAL_SPEED = CONSTANTS.CARD_DEAL_SPEED;
	this.CARD_MOVE_SPEED = CONSTANTS.CARD_MOVE_SPEED;
	this.CARD_DEAL_TURN_OVER_DELAY = CONSTANTS.CARD_DEAL_TURN_OVER_DELAY;
	this.CARD_TURN_OVER_SPEED = CONSTANTS.CARD_TURN_OVER_SPEED;
	this.CARD_TURN_OVER_DELAY = CONSTANTS.CARD_TURN_OVER_DELAY;
	this.CARD_SPACING_X = CONSTANTS.CARD_SPACING_X;
	this.CARD_SPACING_Y = CONSTANTS.CARD_SPACING_Y;
	
	this._lastGameID = "";
	this._currentGame = null;
	this._lastResizeEvent = null;
	this._winAnimationCards = [];
	this._winExplodeStack = null;
	this._winGameNumber = -1;
	this._playspace = null;
	this._startDate = null;
	this._movesCounter = 0;
	this._undoCounter = 0;
	this._score = 0;
	this._animateSpeedSlider = null;
	
	this.About = null;
	this.ChangeLog = null;
	this.StatisticsView = null;
	this.DeckView = null;
	
	/**
	 * First function called, Solitaire begins here!
	 */
	this.init = function()
	{
		var hrefQueryObject = {};
		//var href = window.location.href;
		
		YAHOO.util.DDM.mode = YAHOO.util.DDM.INTERSECT;
		this._playspace = document.getElementById("playspace");
		
		E.add(window, "unload", function() { SOLITAIRE.handleShutdown(); });
		
		// Create our dialogs
		//this.About = new DialogBox("about");
		this.ChangeLog = new DialogBox("changeLog", { "percentageWidth"  : 0.70, "percentageHeight" : 0.50, "overflowable"     : true});
		
		Options.init();
		// RC Added
		//User.init();
		
		
		
		var userID = 1; //User.get("userID", null);
		SOLITAIRE.initPostUserOptions();
				
				
	};
	
	/* new post user options avoiding logging user data*/
	this.initPostUserOptions = function()
	{
		// Lets init our Options and Stats
		Stats.init();

		var statTotalDealt = Stats.get("totalDealt", 0);
		var statTotalPlayed = Stats.get("totalPlayed", 0);
		var statTotalWon = Stats.get("totalWon", 0);
		var statTotalTime = Stats.get("totalTime", 0);
		var statTotalMoves = Stats.get("totalMoves", 0);
		
		Stats.set("totalDealt", statTotalDealt);
		Stats.set("totalPlayed", statTotalPlayed);
		Stats.set("totalWon", statTotalWon);
		Stats.set("totalTime", statTotalTime);
		Stats.set("totalMoves", statTotalMoves);
		
		var optionCardSize = Options.get("optionCardSize", "best");
		var optionAnimateCards = Options.get("optionAnimateCards", true);
		var optionAnimateDeal = Options.get("optionAnimateDeal", true);
		var optionAnimateSpeed = Options.get("optionAnimateSpeed", 1.0);
		var optionAutoPlay = Options.get("optionAutoPlay", "off");
		var optionAutoFlip = Options.get("optionAutoFlip", true);
		var optionShowTimer = Options.get("optionShowTimer", true);
		var optionShowScore = Options.get("optionShowScore", true);
		var optionShowMoves = Options.get("optionShowMoves", true);
		var optionDeck = Options.get("optionDeck", "hm");
		var optionBackground = Options.get("optionBackground", "textures/greenfelt.jpg");
		var optionGame = Options.get("optionGame", "KlondikeTurn3");

		
		Options.set("optionCardSize", optionCardSize);
		Options.set("optionAnimateCards", optionAnimateCards);
		Options.set("optionAnimateDeal", optionAnimateDeal);
		Options.set("optionAnimateSpeed", optionAnimateSpeed);
		Options.set("optionAutoPlay", optionAutoPlay);
		Options.set("optionAutoFlip", optionAutoFlip);
		Options.set("optionShowTimer", optionShowTimer);
		Options.set("optionShowScore", optionShowScore);
		Options.set("optionShowMoves", optionShowMoves);
		Options.set("optionDeck", optionDeck);
		Options.set("optionBackground", optionBackground);
		Options.set("optionGame", optionGame);
						
		//E.add("changeLogLink", "click", SOLITAIRE.ChangeLog.toggle, null, SOLITAIRE.ChangeLog);
		
		E.add(window, "resize", this.handleWindowResize, null, this);
		//E.add("menuSolitaireDealAgain", "click", this.handleDealAgain, null, this);
		//E.add("menuSolitaireUndoMove", "click", this.handleUndoMove, null, this);
	
		Status.show("Loading", -1);
		
		this.startGame();
	};
	

	
	/**
	 * This function is called after deck loading is finished, time to begin!
	 */
	this.startGame = function()
	{
		GameOver.hide();
		//Menu.disable();
		
		var targetDeck = new Deck(Options.get("optionDeck"));
		targetDeck.loadDeck();
		
		Stats.increment("totalDealt");
		//Stats.notifyServer("dealt");

		this.sizePlayspace();
		//Help.resize();
		
		var optionGame = Options.get("optionGame");
		if(!gGames.hasOwnProperty(optionGame))
			optionGame = "KlondikeTurn3";

		this._currentGame = new Game(optionGame, targetDeck);
		this._lastGameID = this._currentGame._game._gameID;

		Status.hide("startGame");

		SOLITAIRE.state = CONSTANTS.STATE_DEALING;		
		this._currentGame.init(this._playspace, function() { SOLITAIRE.gameStarted(); });
	};
	
	/**
	 * Called when dealing of cards is finished, everything is ready to begin playing
	 */
	this.gameStarted = function()
	{
		var timerValue = document.getElementById("timerValue");
		D.clear(timerValue);
		timerValue.appendChild(document.createTextNode("0:00"));

		SOLITAIRE.state = CONSTANTS.STATE_PLAYING;
		//Menu.enable();
		this._movesCounter = 0;
		this._undoCounter = 0;
		this._startDate = null;
		this.undoIncrement(0);
		this.movesIncrement(0);

		this._score = 0;
		this.scoreIncrement(0);
	};
	
	/**
	 * Ticks the timer up
	 */
	this.timerTick = function()
	{
		if(SOLITAIRE.state!==CONSTANTS.STATE_PLAYING)
			return;
		
		var rightNow = new Date();
		var secondsElapsed = 0;
		if(this._startDate!==null)
			secondsElapsed = parseInt(((rightNow.getTime()-this._startDate.getTime())/1000), 10);
	
		var timerValue = document.getElementById("timerValue");
		D.clear(timerValue);
		timerValue.appendChild(document.createTextNode(I.secondsAsClock(secondsElapsed)));
		
		setTimeout(function() { SOLITAIRE.timerTick(); }, 1000);
	};
	
	/**
	 * Increments the moves counter by howMuch
	 */
	this.movesIncrement = function(howMuch)
	{
		if(typeof howMuch=="undefined" || howMuch===null)
			howMuch = 1;
		
		this._movesCounter+=howMuch;

		// We are now officially playing! :)
		if(this._movesCounter===1)
		{
			Stats.increment("totalPlayed");
			//Stats.notifyServer("played");
			
			this._startDate = new Date();
			SOLITAIRE.timerTick();
		}
		
		var movesValue = document.getElementById("movesValue");
		D.clear(movesValue);
		movesValue.appendChild(document.createTextNode(I.formatWithCommas(this._movesCounter)));
		
		var movesLabel = document.getElementById("movesLabel");
		D.clear(movesLabel);
		if(this._movesCounter===1)
			movesLabel.appendChild(document.createTextNode("move"));
		else
			movesLabel.appendChild(document.createTextNode("moves"));
	};
	
	/**
	 * Increments the score counter by howMuch
	 */
	this.scoreIncrement = function(howMuch)
	{
		if(typeof howMuch=="undefined" || howMuch===null)
			howMuch = 100;
			
		this._score+=howMuch;
		
		var scoreValue = document.getElementById("scoreValue");
		D.clear(scoreValue);
		scoreValue.appendChild(document.createTextNode(I.formatWithCommas(this._score)));
	};
		
	/**
	 * Increments the undo counter by howMuch
	 */
	this.undoIncrement = function(howMuch)
	{
		if(typeof howMuch=="undefined" || howMuch===null)
			howMuch = 1;
		
		this._undoCounter+=howMuch;		
	};

	/**
	 * This is called when the user leaves the page
	 */
	this.handleShutdown = function()
	{
		if(SOLITAIRE.state==CONSTANTS.STATE_PLAYING && this._movesCounter>0)
		{
			var rightNow = new Date();
			if(this._startDate!==null)
				Stats.increment("totalTime", parseInt(((rightNow.getTime()-this._startDate.getTime())/1000), 10));
			Stats.increment("totalMoves", this._movesCounter);
		}
	};
	
	/**
	 * This will attempt to undo a move
	 * @param {Object} e
	 * @param {Object} ignored
	 */
	this.handleUndoMove = function(e, ignored)
	{
		if(SOLITAIRE.state<CONSTANTS.STATE_PLAYING)
			return;
		
		if(this._currentGame.undoMove())
		{
			this.undoIncrement();
		}
	};
		
	/**
	 * This will start the current game over again
	 * @param {Object} e
	 * @param {Object} ignored
	 * @param {Object} skipAbandon If this is true, we won't handle it as an abandon
	 */
	this.handleDealAgain = function(e, ignored, skipAbandon)
	{
		var self = this;
		
		if(SOLITAIRE.state<CONSTANTS.STATE_PLAYING)
			return;

		if(SOLITAIRE.state==CONSTANTS.STATE_PLAYING && (typeof skipAbandon==="undefined" || skipAbandon!==true))
			this.handleGameAbandon();

		if(SOLITAIRE.state==CONSTANTS.STATE_PLAYING && this._movesCounter>0)
		{
			var rightNow = new Date();
			if(this._startDate!==null)
				Stats.increment("totalTime", parseInt(((rightNow.getTime()-this._startDate.getTime())/1000), 10));
			Stats.increment("totalMoves", this._movesCounter);
		}
		
		//Menu.disable();
		Status.show("Loading", -1, "startGame");
		
		if(this._currentGame!==null)
			this._currentGame.clear();
		this._currentGame = null;
		
		setTimeout(function() { self.startGame(); }, 0);
	};
	
	/**
	 * This is called when the current game is abandoned
	 */	
	this.handleGameAbandon = function()
	{
		var postOptions = {};
		
		if(this._startDate!==null)
		{
			var rightNow = new Date();
			
			postOptions["undos"] = this._undoCounter;
			postOptions["moves"] = this._movesCounter;
			postOptions["score"] = this._score;
			postOptions["time"] = parseInt(((rightNow.getTime()-this._startDate.getTime())/1000), 10);
		}
		
		//Stats.notifyServer("abandoned", postOptions);
	};
	
	/**
	 * This is called when the user changes games
	 * @param {Object} e
	 * @param {Object} ignored
	 */
	this.handleGameChange = function(e, ignored)
	{
		this.handleGameAbandon();
		
		Options.set("optionGame", GameChooser.getSelectedGame());
		User.saveUserOptions();
		
		this.updateGameChange();
	};
	
	/**
	 * Actually updates our UI with the changes of the option
	 */
	this.updateGameChange = function(skipIfSameGame)
	{
		var self = this;
		
		var optionGame = Options.get("optionGame");
		
		if(typeof skipIfSameGame!="undefined" && skipIfSameGame===true && this._lastGameID==optionGame)
			return;
			
		GameChooser.hide();
		//Help.hide();
		//Menu.disable();
		
		setTimeout(function() { self.handleDealAgain(null, null, true); }, 0);		
	};

		
	/**
	 * This is called when new options have been set by a third party and we need to now use them
	 */
	this.refreshUI = function()
	{
		this.updateAnimateSpeedValue();
		this.updateShowMovesChange();
		this.updateShowScoreChange();
		this.updateShowTimerChange();
		this.updateAutoFlipChange();
		this.updateAutoPlayChange();
		this.updateAnimateDealChange();
		this.updateAnimateCardsChange();
		this.updateCardSizeChange();
		this.updateBackgroundChange();
		this.updateDeckChange();
	};
	
	/**
	 * This is called whenever the window is resized
	 */	
	this.handleWindowResize = function()
	{
		var self = this;
		
		if(SOLITAIRE.state!=CONSTANTS.STATE_PLAYING)
			return;
			
		this._lastResizeEvent = new Date();

		//Menu.disable();
		Status.show("Resizing Cards", -1, "windowResize");
		
		setTimeout(function() { self.handleWindowResizeFollowup(); }, 1000);
	};
	
	/**
	 * Followup to the window resize, prevents lots of resizing taking place too quickly
	 */
	this.handleWindowResizeFollowup = function()
	{
		var self = this;
		
		if(this._lastResizeEvent===null)
			return;
			
		var rightNow = new Date();
		if((rightNow.getTime()-this._lastResizeEvent.getTime())<900 || SOLITAIRE.state!=CONSTANTS.STATE_PLAYING)
		{
			setTimeout(function() { self.handleWindowResizeFollowup(); }, 500);
			return;
		}
		
		this._lastResizeEvent = null;
		this.sizePlayspace();
		//Help.resize();
		DeckView.resize();
				
		if(this._currentGame!==null)
			this._currentGame.updateBoard(function() { Status.hide("windowResize"); Menu.enable(); });			
	};

	/**
	 * Sizes our playspace
	 */
	this.sizePlayspace = function()
	{
		//this._playspace.style.paddingLeft = "-20px";
		this._playspace.style.marginTop = "90px";
		//this._playspace.style.marginLeft = "5%";
		//this._playspace.style.marginRight = "5%";		
		//this._playspace.style.left = this.tinyScreen ? "5px" : "-100px";
		//this._playspace.style.width = "934px"; //D.width(document.body)-(this.tinyScreen ? 15 : 44) + "px";
		this._playspace.style.width = D.width(document.body) + "px";
		//this._playspace.style.height = D.height(document.body) + "px";
		this._playspace.style.height = "800px";	
	};
	
	/**
	 * Is called when a menu is shown
	 * @param {Object} menuItemNumber
	 */
	this.handleMenuShow = function(menuItemNumber)	
	{
		if(menuItemNumber===2)
		{
			if(this._animateSpeedSlider!==null)
				this._animateSpeedSlider.verifyOffset(true);
		}
	};
	
	/**
	 * Function to handle losing
	 */
	this.lostGameHandler = function()
	{
		SOLITAIRE.state = CONSTANTS.STATE_LOST;
		
		GameOver.show(false);
	};
		
	/**
	 * Function to handle winning, kicks off the reward
	 */
	this.wonGameHandler = function()
	{
		var self = this;
				
		var rightNow = new Date();
		
		Stats.increment("totalWon");
		/*Stats.notifyServer("won", { undos : this._undoCounter,
									moves : this._movesCounter,
									score : this._score,
									time  : parseInt(((rightNow.getTime()-this._startDate.getTime())/1000), 10)});*/
		
		Stats.increment("totalTime", parseInt(((rightNow.getTime()-this._startDate.getTime())/1000), 10));
		Stats.increment("totalMoves", this._movesCounter);
		
		this._winGameNumber = Stats.get("totalDealt");
			
		for(var i=0;i<this._currentGame._spotsExpanded.length;i++)
		{
			var spot = this._currentGame._spotsExpanded[i];
			if(spot.hasOwnProperty("base"))
				spot["baseCard"].getDomCard().style.visibility = "hidden";
		}		
		
		this._winAnimationCards = [];
			
		for(i=0;i<this._currentGame._spotsExpanded.length;i++)
		{
			spot = this._currentGame._spotsExpanded[i];
			if(spot.cards.length>0)
				this._winAnimationCards.push(spot.cards);
		}

		setTimeout(function() { self.winAnimateNextStack(); }, 1500);		
	};
	
	/**
	 * Moves the next stack to where it is safe to explode
	 */
	this.winAnimateNextStack = function()
	{
		var self = this;
		
		// All done animating?
		if(this._winAnimationCards.length===0)
		{
			if(this._currentGame!==null && this._winGameNumber==Stats.get("totalDealt"))
			{
				this._currentGame.clear();
				this._currentGame = null;
				
				GameOver.show(true);
			}
			return;
		}
		
		this._winExplodeStack = this._winAnimationCards.pop();
		if(this._winExplodeStack===null || this._winExplodeStack.length<1)
		{
			setTimeout(function() { self.winAnimateNextStack(); }, 100);
			return;	
		}
		
		var domCard = this._winExplodeStack[0].getDomCard();
		var cardHeight = D.height(domCard);
		var cardWidth = D.width(domCard);
					
		var randomLocation = [I.random(cardWidth, D.width(document.body)-(cardWidth*3)),
						      I.random(cardHeight, D.height(document.body)-(cardHeight*3))];
		
		for(var i=0;i<this._winExplodeStack.length;i++)
		{
			var card = this._winExplodeStack[i];
			card.show();
			domCard = card.getDomCard();

			var moveOptions = {    left : { to : randomLocation[0] },
			                        top : { to : randomLocation[1] }};
			var moveAnimation = new YAHOO.util.Anim(domCard, moveOptions, CONSTANTS.CARD_WIN_MOVE_SPEED, YAHOO.util.Easing.easeOut);
			
			if((this._winExplodeStack.length-1)==i)
				moveAnimation.onComplete.subscribe(function() { self.winExplodeStack(); });
				
			moveAnimation.animate();
		}
	};
	
	/**
	 * Explodes the next stack
	 */
	this.winExplodeStack = function()
	{
		var self = this;
		
		for(var i=0;i<this._winExplodeStack.length;i++)
		{
			var card = this._winExplodeStack[i];
			var domCard = card.getDomCard();
			
			var cardXY = D.xy(domCard);
			var cardHeight = D.height(domCard);
			var cardWidth = D.width(domCard);
			var degree = 2*Math.PI*(i/this._winExplodeStack.length);
			var xSpeed = Math.sin(degree);
			var ySpeed = Math.cos(degree);
			var destXLoc = cardXY[0] + (xSpeed*(cardWidth*2));
			var destYLoc = cardXY[1] + (ySpeed*(cardHeight*2));

			var moveOptions = {    left : { to : destXLoc },
			                        top : { to : destYLoc },
								opacity : { to :        0 } };
			var moveAnimation = new YAHOO.util.Anim(domCard, moveOptions, CONSTANTS.CARD_WIN_EXPLODE_SPEED_PER_CARD*this._winExplodeStack.length, YAHOO.util.Easing.easeOut);
			
			if((this._winExplodeStack.length-1)==i)
				moveAnimation.onComplete.subscribe(function() { self.winAnimateNextStack(); });
				
			moveAnimation.animate();
		}
	};	
}
