	/*---------------------------------------------------------------------
    Original: Charles Chen, Nov. 2004 (cchen@charliedigital.com)
    Visit: http://www.charliedigital.com

    You may copy and modify the contents of this file and reuse it for
    *non-commercial* purposes only.  Please leave this notice intact!
    -----------------------------------------------------------------------
    Description: This file contains the classes which kind of act as an 
	abstraction of the raw HTML object which they loosely represent.
    ---------------------------------------------------------------------*/	
	
	/* OBJECTS */
	/* CONSOLIDATE THESE LATER */
	var Suits = ['S','H','D','C'];
	var SuitNames = {S:'spades',H:'hearts',D:'diamonds',C:'clubs'};
	var SuitCodes = {S:'&#9824;',H:'&#9829;',D:'&#9830;',C:'&#9827;'};

	// DECK
	function Deck(htmlObj){
		function Deck(){
			this.Cards = new Array();
			this.HtmlObj = htmlObj;
		}

		with(Deck){
			method('Shuffle',function(){
				var i1;
				var i2;
				var temp;

				for(var i = 0; i < 1000; i++){
					i1 = Math.floor(Math.random() * 52);
					i2 = Math.floor(Math.random() * 52);

					temp = this.Cards[i1];
					this.Cards[i1] = this.Cards[i2];
					this.Cards[i2] = temp;
				}
			});

			method('Initialize',function(){
			   var s;
			   for(var i = 0; i < Suits.length; i++){
					s = Suits[i];
					for(var j = 1; j < 14; j++){
						this.Cards.push(new Card({S:s,T:GetTextValue(j),V:j}));
					}
			   }

			   this.Shuffle();
			});
		}

		/* PRIVATE FUNCTIONS */
		function GetTextValue(arg){
			var output;

			switch(arg){
				case 11: output = 'J'; break;
				case 12: output = 'Q'; break;
				case 13: output = 'K'; break;
				case 1: output = 'A'; break;
				default: output = String(arg);
			}

			return output;
		}

		return new Deck();
	}

	// CARD
	function Card(args, htmlObj){
		function Card(){
			if(args.C){
				// CREATING A CARD FROM A LIST ELEMENT
				var argsArray = args.C.id.split("_");
				this.Id = args.C.id;
				this.Suit = argsArray[0];
				this.Value = argsArray[1];
				this.Text = argsArray[2];
				this.FaceDown = ((argsArray[3].toString() == 'true') ? true : false);
				this.HtmlObj = args.C;
			}
			else{
				// CREATING A NEW CARD
				this.Suit = args.S;
				this.Text = args.T;
				this.Value = args.V;
				this.FaceDown = true;
				this.Id = GetId(this);
			}

			if(!this.HtmlObj)
				this.HtmlObj = htmlObj;

			if(!(this.Suit && this.Text && this.Value)){
				alert("Error initializing card.");
			}
		}

		with(Card){
			method('Flip',function(){
				this.FaceDown = !this.FaceDown;
				this.Id = GetId(this);
			});

			method('Render',function(showBg){
				var output = "";				
				var bgStart = "";
				var bgEnd = "";

				if(showBg){
					bgStart = "<span>";
					bgEnd = "</span>";
				}
				

				if(this.FaceDown){
					output += "<li><div class=\"card\" id=\"" + this.Id + "\">&nbsp;</div></li>";
				}
				else{
					output += "<li><div class=\"card " + SuitNames[this.Suit] + "\" ";
					output += "id=\"" + this.Id + "\">" + bgStart;
					output += this.Text + "<sub>" + SuitCodes[this.Suit] + "</sub>" + bgEnd + "</div></li>";
				}

				return output;
			});

			method('Detach',function(){
				Debug.Write('Detached card.');
			});

			method('Test',function(){
				alert("Suit:" + this.Suit + "\nText:" + this.Text + "\nValue:" + this.Value);
			});
		}

		function GetId(obj){
			return obj.Suit + "_" + obj.Value + "_" + obj.Text + "_" + obj.FaceDown.toString();
		}

		return new Card();
	}

	// STACKS. HANDLE REFERS TO THE HTML ELEMENT THAT THIS STACK IS VISUALLY REPRESENTED BY.
	function UnplayedStack(htmlObj){
		function UnplayedStack(){
			this.HtmlObj = htmlObj;
		}

		with(UnplayedStack){
			method('GetCard',function(){
				return new Card({C:this.HtmlObj.removeChild(this.HtmlObj.lastChild).firstChild});
			});

			method('AddCard',function(card){
				this.HtmlObj.innerHTML += card.Render(Game.ShowTextBackground); // THIS VALUE SHOULD BE PASSED IN
			});

			method('Count',function(){
				return this.HtmlObj.childNodes.length;
			});
		}

		return new UnplayedStack();
	}

	function ColumnStack(htmlObj){
		function ColumnStack(){
			this.HtmlObj = htmlObj;
		}

		with(ColumnStack){
			method('FindCardIndex',function(card){
				var output = -1;

				for(var i = 0; i < this.HtmlObj.childNodes.length; i++){
					if(this.HtmlObj.childNodes[i].firstChild && this.HtmlObj.childNodes[i].firstChild.id){
						if(this.HtmlObj.childNodes[i].firstChild.id == card.Id){
							output = i;
							break;
						}
					}
				}

				return parseInt(output);
			});

			method('AddCard',function(card){
				this.HtmlObj.innerHTML += card.Render(Game.ShowTextBackground); // THIS VALUE SHOULD BE PASSED IN
			});

			method('GetLastCard',function(){
				var card = null;

				if(this.HtmlObj.childNodes){
					card = this.GetCardByIndex(this.HtmlObj.childNodes.length - 1);
				}

				return card;
			});

			method('GetFirstCard',function(){	
				return this.GetCardByIndex(0);				
			});

			method('GetCardByIndex',function(index){
				var card = null;

				var cards = this.HtmlObj.childNodes;
				if(cards
					&& cards.length > 0
					&& cards[index]
					&& cards[index].firstChild
					&& cards[index].firstChild.tagName
					&& cards[index].firstChild.tagName.toLowerCase() == "div")
				{
					card = new Card({C:cards[index].firstChild});
					Debug.Write("First card in stack is: " + card.Id);
				}

				return card;	
			});

			method('HasMoreCards',function(){
				return this.HtmlObj.hasChildNodes();
			});

			method('RemoveCard',function(card){
				Debug.Write("Remove card: " + card.Id + " from " + this.HtmlObj.id);
				var index = this.FindCardIndex(card);
				Debug.Write("Card found at index: " + index);

				Debug.Write("Stack " + this.HtmlObj.id + " has: " + (this.HtmlObj.childNodes ? this.HtmlObj.childNodes.length : 0) + " cards.");
				if(index > -1 && index < this.HtmlObj.childNodes.length){
					Debug.Write("Start removing card...");
					var node = this.HtmlObj.childNodes[index];
					if(node){
						Debug.Write("Removed card: " + card.Id);
						return this.HtmlObj.removeChild(node);
					}
				}

				return null;
			});

			method('RemoveAll',function(){
				var total = this.Count();
				Debug.Write("Removing all " + total + " cards from: " + this.HtmlObj.id);
				for(var i = (total - 1); i >= 0; i--){
					this.HtmlObj.removeChild(this.HtmlObj.childNodes[i]);
				}
			});

			method('Count',function(){
				return (this.HtmlObj.childNodes ? this.HtmlObj.childNodes.length : 0);
			});
		}

		return new ColumnStack();
	}

	function ScoreDisplay(htmlObj,enabled){
		function ScoreDisplay(){
			this.HtmlObj		= htmlObj;
			this.Intervals	= 10;
			this.Interval	= null;
			this.Enabled	= enabled || false;
		}

		with(ScoreDisplay){
			method('Show',function(x,y,value){
				if(value != 0 && enabled){
					if(this.Interval)
						window.clearInterval(this.Interval);
					this.Intervals = 10;
					Dhtml.ShiftTo(this.HtmlObj,x-65,y-45);
					this.HtmlObj.style.display = "block";
					this.HtmlObj.innerHTML = value;
					this.HtmlObj.style.color = ((value > 0) ? "#0000FF" : "#aa0000");

					this.Interval = window.setInterval("Game.ScoreDisplay.Go()", 50);
				}
			});

			method('Go',function(){
				Dhtml.ShiftBy(this.HtmlObj,0,-1);

				var vis = parseInt(this.Intervals) * 10;
				Dhtml.SetAlpha(this.HtmlObj,vis);

				this.Intervals--;

				if(this.Intervals <= 0){
					this.HtmlObj.style.display = "none";
					window.clearInterval(this.Interval);
				}
			});
		}

		return new ScoreDisplay();
	}

	function GameBoard(htmlObj){
		function GameBoard(){
			this.HtmlObj	= htmlObj;
			this.Timeout = null;
			this.Timeouts = 10;
		}

		with(GameBoard){
			method('Show',function(){
				var vis = 100 - (5 * this.Timeouts);
				if(vis < 100){
					this.Timeout = window.setTimeout("Game.GameBoard.Show()", 40);
				}
				Dhtml.SetAlpha(this.HtmlObj, vis);
				this.Timeouts--;
			});

			method('Fade',function(){
				Dhtml.SetAlpha(this.HtmlObj, 50);
			});
		}

		return new GameBoard();
	}

	function ResultsBoard(htmlObj){
		function ResultsBoard(){
			this.HtmlObj	= htmlObj;
			this.Timeout = null;
			this.Timeouts = 20;
		}

		with(ResultsBoard){
			method('Show',function(msg,score,win){
				this.HtmlObj.style.display = "block";
				with(this.HtmlObj.firstChild){
					innerHTML = "<div id=\"msg\">" + msg + "</div>";
					innerHTML += "<div>Your final score of:</div>";
					innerHTML += "<div id=\"score_pct\">" + score + "%</div>";
					if(win)
						innerHTML += "<div>is fantastic! Play again for a different pic!</div>";
					else
						innerHTML += "<div>just didn't cut it. Please try again!</div>";
				}
			});

			method('Hide',function(){
				this.HtmlObj.style.display = "none";
			});
		}

		return new ResultsBoard();
	}

	/*---------------------------------------------------------------------
    DO NOT MODIFY OR REMOVE.
    -----------------------------------------------------------------------
    jsSolitaire - Skinnable Javascript Solitaire Game
    Copyright (C) 2004 Charles Chen (cchen@charliedigital.com)

    This software package is distributed and protected under the: 
    W3CŪ SOFTWARE NOTICE AND LICENSE
    http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231

    See readme.html and readme.txt for the full versions.    
	---------------------------------------------------------------------*/