﻿/*  Jacks or Better (iPhone version)
 *  (c) 2007 elenor.net
 *
 *  I am not a JavaScript developer. This is a bit
 *  hacky. OOP JS is weird.
 *
/*-------------------------------------------------------------*/


function Game() {
	this.hands = null;
	this.hand = null;
	this.dealt = null;
	
	//initialize the game properties
	this.init = function() {
		if (this.hand) {
			hands[this.hand].on = false;
			hands[this.hand].display();
		}
			
		this.hands = new Array();
		this.hand = null;
		this.dealt = false;
	}
}

function Deck() {
	this.cards = new Array();
	this.rank = new Array('2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A');
	this.suit = new Array('club', 'spade', 'heart', 'diamond');
	this.chars = new Array('&clubs;', '&spades;', '&hearts;', '&diams;');
	
	this.init = function() {
		for (var i = 0; i < 52; i++) {
			this.cards[i] = i;
		}
		
		while (--i && i > 0) {
			var j = Math.floor(Math.random() * (i + 1));
			var ix = this.cards[i];
			var jx = this.cards[j];
			this.cards[i] = jx;
			this.cards[j] = ix;
		}
	}

	this.deal = function() {
		card = this.cards.pop();
		
		// set card suit and rank depending on the card number
		if (card < 13) {
			suit = 0;
			rank = card;
		} else if (card < 26) {
			suit = 1;
			rank = card - 13;
		} else if (card < 39) {
			suit = 2;
			rank = card - 26;
		} else {
			suit = 3;
			rank = card - 39;
		}
		
		return new Array(suit, rank);
	}
}

function Amount(id, label, amount) {
	this.id = id;
	this.label = label;
	this.amount = amount;
	this.element = null;
	
	//initializes the money div and adds it to the page
	this.init = function() {
		if (!this.element) {
			var amount = document.createElement('div');
			amount.setAttribute('id', this.id);
			
			var parentElement = document.getElementById('amounts');
			parentElement.appendChild(amount);
		}
		
		this.element = document.getElementById(this.id);
		this.display();
	}
	
	//sets the label and value of the amount
	this.display = function() {
		this.element.innerHTML = this.label + ': ' + this.amount;
	}
	
	//adds to the amount
	this.add = function(amount) {
		this.amount += amount;
		this.display();
	}
	
	//subtracts from the amount
	this.subtract = function(amount) {
		if (amount > this.amount) {
			return false;
		}
		
		this.amount -= amount;
		this.display();
	}
}

function Card(id, listener) {
	this.id = id;
	this.listener = listener;
	this.element = null;
	this.hold = false;
	this.suit = null;
	this.rank = null;
	
	//initializes the card div and adds it to the page
	this.init = function() {
		if (!this.element) {
			var card = document.createElement('div');
			card.setAttribute('id', 'card' + this.id);
			card.addEventListener('mousedown', this.listener, false);
			
			var rank = document.createElement('div');
			rank.setAttribute('class', 'rank');
			card.appendChild(rank);
		
			var suit = document.createElement('div');
			suit.setAttribute('class', 'suit');
			card.appendChild(suit);
			
			var parentElement = document.getElementById('cards');
			parentElement.appendChild(card);
		}
		
		this.element = document.getElementById('card' + this.id);
		this.display();
	}
	
	//sets the style and rank of the card element
	this.display = function() {
		this.element.setAttribute('class', 'card' + (this.hold ? ' hold' : ''));
		
		this.element.childNodes[0].innerHTML = this.rank != null ? deck.rank[this.rank] : '';
		this.element.childNodes[1].innerHTML = this.suit != null ? deck.chars[this.suit] : '';
	}
	
	//assigns the card a suit and rank
	this.deal = function() {
		if (!this.hold) {
			cardValue = deck.deal();
			this.suit = cardValue[0];
			this.rank = cardValue[1];
		}
		
		this.display();
	}
	
	//toggles the card between held and not held
	this.toggle = function() {
		if (this.suit != null && this.rank != null) {
			this.hold = !this.hold;
			this.display();
		}
	}
}

function Button(id, label, action, listener) {
	this.id = id;
	this.label = label;
	this.action = action;
	this.listener = listener;
	this.element = null;
	
	//initializes the button div and adds it to the page
	this.init = function() {
		if (!this.element) {
			var button = document.createElement('div');
			button.setAttribute('id', 'button' + this.id);
			button.addEventListener('mousedown', this.listener, false);
		
			var parentElement = document.getElementById('buttons');
			parentElement.appendChild(button);
		}
		
		this.element = document.getElementById('button' + this.id);
		this.display();
	}
	
	//sets the class and the label of the button
	this.display = function() {
		this.element.setAttribute('class', 'button ' + this.action);
		this.element.innerHTML = this.label;
	}
}

function Hand(id, name, multiplier, jackpot) {
	this.id = id;
	this.name = name;
	this.multiplier = multiplier;
	this.jackpot = jackpot;
	this.element = null;
	this.on = false;
	
	//initializes the hand row and adds it to the table
	this.init = function(table, minBet, maxBet) {
		if (!this.element) {
			var row = document.createElement('tr');		
			row.setAttribute('id', 'hand' + this.id);
				
			var cell = document.createElement('th');
			cell.innerHTML = this.name;
			row.appendChild(cell);
			
			for (var j = minBet; j <= maxBet; j++) {
				cell = document.createElement('td');
				cell.innerHTML = (j == maxBet && this.jackpot ? this.jackpot : j * this.multiplier);
				row.appendChild(cell);
			}
			
			table.appendChild(row);
			this.element = document.getElementById('hand' + this.id);
		}
	}
	
	//sets the class of the hand
	this.display = function() {
		if (this.on) {
			this.element.setAttribute('class', 'won');	
		} else {
			this.element.setAttribute('class', '');
		}
	}
}

function Message() {
	this.element = null;
	
	this.init = function() {
		if (!this.element) {
			var message = document.createElement('div');
			message.setAttribute('id', 'message');
			
			var parentElement = document.getElementsByTagName('body').item(0);
			parentElement.appendChild(message);
			
			this.element = document.getElementById('message');
		}
	}
	
	this.display = function(message) {
		this.element.innerHTML = message;
		this.element.style.opacity = 1;
		this.element.style.display = 'block';
	}
	
	this.remove = function() {
		this.element.style.display = 'none';
	}
	
	this.opacity = function(opacity) {
		this.style.opacity = opacity;
	}
}


/*-------------------------------------------------------------*/


function hold(event) { 
	if (game.dealt) {
		var element = event.target.parentNode.id;
		
		if (element.substring(0, 4) == 'card') {
			cards[element.substring(4)].toggle();
			event.stopPropagation();
		}
	}
}

function bet(event) {
	if (!game.dealt) {
		var element = event.target.id;
		var bet = parseInt(element.substring(6));
		
		//minimum bets increment, unlike max bets
		if (bet == minBet) {
			bet += amounts.bet.amount;
		}
		
		//if the bet is too outside the range, set it to the minimum
		if (bet > maxBet || bet < minBet) {
			bet = minBet;
		}
		
		//make sure there are enough credits
		if (bet >= amounts.credits.amount) {
			bet = amounts.credits.amount;
		}
		
		amounts.bet.amount = bet;
		amounts.bet.display();
	}
	
	event.stopPropagation();
}

function deal() {
	if (!game.dealt) {
		if (!amounts.bet.amount) {
			return shout('Place your bet');
		}
		
		amounts.credits.subtract(amounts.bet.amount);
		amounts.credits.display();
		
		game.init();
		deck.init();
	}

	//deal new cards
	for (var i = 0; i < cards.length; i++) {
		cards[i].deal();
	}

	//swap the deal/draw label
	if (game.dealt) {
		buttons.deal.label = 'Deal';
		buttons.deal.display();
		game.dealt = false;
	} else {
		buttons.deal.label = 'Draw';
		buttons.deal.display();
		game.dealt = true;
	}
	
	//cards drawn, clean up and check for win
	if (!game.dealt) {
		for (var i = 0; i < cards.length; i++) {
			cards[i].hold = true;
			cards[i].display();
			
			cards[i].hold = false;
		}
		
		//get all winning hands
		if (!checkXofaKind(cards)) {
			checkFlush(cards);	
			checkStraight(cards);
		}
		
		//check for best winning hand
		for (var i = 0; i < hands.length; i++) {
			if (game.hands[hands[i].id]) {
				game.hand = i;
				break;
			}
		}
		
		if (game.hand !== null) {
			hands[game.hand].on = true;
			hands[game.hand].display();
			
			amounts.win.amount = amounts.bet.amount * hands[game.hand].multiplier;
			amounts.win.display();
			
			amounts.credits.amount += amounts.win.amount;
			amounts.credits.display();
		} else {
			amounts.win.amount = 0;
			amounts.win.display();
		}
		
		if (amounts.credits.amount < 1) {
			shout('Game Over');
			reset();
		}
	}
}

function reset() {
	for (var i = 0; i < cards.length; i++) {
		cards[i].suit = cards[i].rank = null;
		cards[i].display();
	}	
	
	amounts.credits.amount = credits;
	amounts.credits.display();
}

function shout(alert) {
	message.display(alert);
	setTimeout('message.remove()', 800);
}


/*-------------------------------------------------------------*/


function checkXofaKind(cards) {
	var ranks = new Array();
	var counts = new Array();
	counts[2] = new Array();
	
	//count the number of cards of each rank
	for (var i = 0; i < cards.length; i++) {
		ranks[cards[i].rank] = (ranks[cards[i].rank] ? ranks[cards[i].rank] + 1 : 1);
	}
	
	//loop through the rank counts and set the count vars
	for (var i = 0; i < ranks.length; i++) {
		switch (ranks[i]) {
			case 2:
				counts[2][counts[2].length] = i;
				break;
				
			case 3:
				counts[3] = i;
				break;
				
			case 4:
				counts[4] = i;
				break;
				
			default:
				break;
		}
	}
	
	//check the count var for winning hand combinations
	if (counts[4]) {
		game.hands['4x'] = true;
		return true;
	} else if (counts[3] && counts[2].length == 1) {
		game.hands['fh'] = true;
		return true;
	} else if (counts[3]) {
		game.hands['3x'] = true;
		return true;
	} else if (counts[2].length == 2) {
		game.hands['2p'] = true;
		return true;
	} else if (counts[2].length == 1 && counts[2][0] >= 9) {
		game.hands['jx'] = true;
		return true;
	} else {
		return false;
	}
}

function checkFlush(cards) {
	for (var i = 0; i < cards.length - 1; i++) {
		if (cards[i].suit != cards[i + 1].suit) {
			return false;
		}
	}
	
	game.hands['fl'] = true;
	return true;
}

function checkStraight(cards) {
	var ranks = new Array();
	
	//store all the ranks in an array; add 1 to account for ace as low card
	for (var i = 0; i < cards.length; i++) {
		ranks[i] = cards[i].rank + 1;	
	}
	
	//sort the array numerically asc
	ranks = ranks.sort(function(i, j) { return i - j; });
	
	//if the low card is two and the high card is ace, shift ace around to the low card
	if (ranks[0] == 1 && ranks[ranks.length - 1] == deck.rank.length) {
		ranks.pop();
		ranks.unshift(0);
	}
	
	//check for consecutive numbers
	for (var i = 0; i < ranks.length - 1; i++) {
		if (ranks[i] + 1 != ranks[i + 1]) {
			return false;
		}
	}
	
	//if the user has a flush, check for a royal or straight flush
	if (game.hands['fl']) {
		if (ranks[ranks.length - 1] == deck.rank.length) {
			game.hands['rf'] = true;
		} else {
			game.hands['sf'] = true;
		}
	} else {
		game.hands['st'] = true;
	}
	
	return true;
}


/*-------------------------------------------------------------*/


function payouts(minBet, maxBet) {
	var table = document.createElement('table');
	var parentElement = document.getElementById('payouts');
	parentElement.appendChild(table);
	
	for (var i = 0; i < hands.length; i++) {
		hands[i].init(table, minBet, maxBet);
	}
}


/*-------------------------------------------------------------*/


var minBet = 1;
var maxBet = 5;
var credits = 200;

var game = new Game();
var deck = new Deck();
var cards = new Array();
var buttons = new Array();
var amounts = new Array();
var hands = null;
var message = null;


window.onload = function() {
	for (var i = 0; i < 5; i++) {
		cards[i] = new Card(i, hold);
		cards[i].init();
	}	
	
	amounts['credits'] = new Amount('credits', 'Credits', credits);
	amounts['credits'].init();
	amounts['win'] = new Amount('win', 'Win', 0);
	amounts['win'].init();
	amounts['bet'] = new Amount('bet', 'Bet', 0);
	amounts['bet'].init();
		
	buttons['minBet'] = new Button(minBet, 'Bet One', 'bet', bet);
	buttons['minBet'].init();
	buttons['maxBet'] = new Button(maxBet, 'Bet Max', 'bet', bet);
	buttons['maxBet'].init();
	buttons['deal'] = new Button('deal', 'Deal', 'deal', deal);
	buttons['deal'].init();
	
	hands = new Array(
		new Hand('rf', 'Royal Flush', 250, 4000),
		new Hand('sf', 'Straight Flush', 50),
		new Hand('4x', 'Four of a Kind', 25),
		new Hand('fh', 'Full House', 9),
		new Hand('fl', 'Flush', 6),
		new Hand('st', 'Straight', 4),
		new Hand('3x', 'Three of a Kind', 3),
		new Hand('2p', 'Two Pair', 2),
		new Hand('jx', 'Jacks or Better', 1)
	);
	
	payouts(minBet, maxBet);
	
	message = new Message();
	message.init();
}
