function print_r( array, return_val ) { // Prints human-readable information about a variable
    //
    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // + namespaced by: Michael White (http://crestidg.com)
 
    var output = "", pad_char = " ", pad_val = 4;
 
    var formatArray = function (obj, cur_depth, pad_val, pad_char) {
        if(cur_depth > 0)
            cur_depth++;
 
        var base_pad = repeat_char(pad_val*cur_depth, pad_char);
        var thick_pad = repeat_char(pad_val*(cur_depth+1), pad_char);
        var str = "";
 
        if (obj instanceof Array || obj instanceof Object) {
            str += "Array\n" + base_pad + "(\n";
            for(var key in obj) {
                if(obj[key] instanceof Array || obj[key] instanceof Object) {
                    str += thick_pad + "["+key+"] => "+formatArray(obj[key], cur_depth+1, pad_val, pad_char);
                } else {
                    str += thick_pad + "["+key+"] => " + obj[key] + "\n";
                }
            }
            str += base_pad + ")\n";
        } else {
            str = obj.toString(); // They didn't pass in an array.... why? -- Do the best we can to output this object.
        };
 
        return str;
    };
 
    var repeat_char = function (len, char) {
        var str = "";
        for(var i=0; i < len; i++) { str += char; };
        return str;
    };
 
    output = formatArray(array, 0, pad_val, pad_char);
 
    return output;
}

function Class () { };
Class.prototype.construct = function () { };
Class.extend = function (def)
{
	var classDef = function ()
	{
		if (arguments [0] !== Class)
		{
			this.construct.apply (this, arguments);
		}
	};
	var proto = new this (Class);
	var superClass = this.prototype;
	
	for (var n in def)
	{
		var item = def [n];
		if (item instanceof Function)
		{
			item.$ = superClass;
		}
		else
		{
			classDef [n] = item;
		}
		proto [n] = item;
	}
	classDef.prototype = proto;
	classDef.extend = this.extend;
	
	return classDef;	
}

var Cell = Class.extend ({
	construct: function ()
	{
		this.value = 0;
		this.chip = null;
	},
	content: function ()
	{
		if (arguments.length == 0)
		{
			return [this.value, this.content];
		}
		this.value = arguments [0];
		this.chip = arguments [1];
	},
	remove: function ()
	{
		this.value = 0;
		this.chip = null;
	}
});

var Chip = Class.extend ({
	construct: function (sprite)
	{
		this.sprite = null;
		this.spriteName = sprite;
	},
	setSprite: function (sprite)
	{
		this.spriteName = sprite;
		if (this.sprite)
		{
			this.sprite.attr ('src', sprite);
		}
		return this;
	},
	display: function (x, y, width, height, margin)
	{
		this.sprite = $('<img />').attr ('src', this.spriteName).css ({top: y * (height + margin), left: x * (width + margin)}).appendTo ($('#game'));
		return this;
	},
	remove: function ()
	{
		if (this.sprite)
		{
			this.sprite.remove ();
		}
	}
});

var Board = Class.extend ({
	construct: function (toWin, cols, rows, cellWidth, cellHeight)
	{
		this.toWin = toWin;
		this.x = -1;
		this.y = -1;
		this.cols = cols;
		this.rows = rows;
		this.cellWidth = cellWidth;
		this.cellHeight = cellHeight;
		this.cells = [];
		for (var i = 0; i < cols; i++)
		{
			for (var j = 0; j < rows; j++)
			{
				if (typeof this.cells [i] == 'undefined')
				{
					this.cells [i] = [];
				}
				this.cells [i][j] = new Cell ();
			}
		}
	},
	getCols: function ()
	{
		return this.cols;
	},
	getRows: function ()
	{
		return this.rows;
	},
	getCellWidth: function ()
	{
		return this.cellWidth;
	},
	getCellHeight: function ()
	{
		return this.cellHeight;
	},
	getCell: function (i, j)
	{
		return typeof this.cells [i] != 'undefined' && this.cells [i][j] != 'undefined' ? this.cells [i][j] : { value: 0, content: null };
	},
	click: function (x, y)
	{
		this.x = x;
		this.y = y;
	},
	clicked: function ()
	{
		return this.x >= 0 && this.y >= 0 ? {x: this.x, y: this.y} : null;
	},
	getChips: function ()
	{	
		var cols = this.getCols (), rows = this.getRows (), chips = { col: new Array (cols), row: new Array (rows), left: new Array (cols), right: new Array (cols) };	
		
		chips.left [0] = { count: 0};
		chips.right [0] = { count: 0};
		for (var x = 0; x < cols; x++)
		{
			chips.col [x] = { count: 0};
			
			for (var y = 0; y < rows; y++)
			{
				if (typeof chips.row [y] == 'undefined')
				{
					chips.row [y] = { count: 0};
				}
				var v1 = this.getCell (x, y).value, v2 = this.getCell (y, x).value;
				chips.col [x].count += v1;
				chips.row [x].count += v2;
			}
			
			var v3 = this.getCell (x, x).value, v4 = this.getCell (cols-x-1, x).value;
			chips.left [0].count += v3;
			chips.right [0].count += v4;
		}
		
		return chips;
	},
	nextCoordinates: function (toWin)
	{
		var chips = this.getChips (), allCoordinates = [], rows = this.getRows ();
		
		//$('#log').html ('<pre>'+print_r (chips)+'</pre>');
		
		for (var type in chips)
		{
			for (var i in chips [type])
			{
				for (var j = 0; j < rows; j++)
				{
					var cell = this.getCell (type == 'row' ? j : i, type == 'row' ? i : j);
					if (Math.abs (chips [type][i].count) == toWin)
					{
						if (toWin == this.toWin)
						{
							return { win: true };
						}
						else
						{
							if (!cell.value)
							{
								return {x: type == 'row' ? j : i, y: type == 'row' ? i : j, turn: true};
							}
						}
					}
					else
					{
						if (!cell.value)
						{
							allCoordinates.push ({x: type == 'row' ? j : i, y: type == 'row' ? i : j});
						}
					}
				}
			}
		}
		return allCoordinates;
	}
});

var Player = Class.extend ({
	construct: function (board)
	{
		if (!(board instanceof Board))
		{
			return;
		}
		this.board = board;
	},
	turn: function ()
	{
		return this.board.clicked (); 
	}
});

var Computer = Player.extend ({
	construct: function (board)
	{
		arguments.callee.$.construct.call (this, board);
	},
	turn: function ()
	{
		return this.calculate ();
	},
	calculate: function ()
	{
		
		var nextCoordinates = this.board.nextCoordinates (this.board.toWin-1), cols = this.board.getCols (), rows = this.board.getRows ();
		
		// win
		if (typeof nextCoordinates.win != 'undefined')
		{
			return { win: true };
		}
		
		//block or win
		if (typeof nextCoordinates.turn != 'undefined')
		{
			return nextCoordinates;
		}
		
		// center
		var cell = this.board.getCell (Math.floor (cols / 2), Math.floor (rows / 2));
		if (!cell.value)
		{
			return {x: Math.floor (cols / 2), y: Math.floor (rows / 2)};
		}
		
		// on corners
		var corners = [{x:0, y:0}, {x: 0, y:rows-1}, {x:cols-1, y:0}, {x:cols-1, y:rows-1}];
		for (var i = 0, count = corners.length; i < count; i++)
		{
			var cell = this.board.getCell (corners [i].x, corners [i].y);
			if (!cell.value)
			{
				return corners [i];
			}
		}
		
		// on sides
		for (var i = 0; i < rows; i++)
		{
			if (!this.board.getCell (0, i).value)
			{
				return {x: 0, y: i};
			}
			if (!this.board.getCell (cols-1, i).value)
			{
				return {x: cols-1, y: i};
			}
		}
		for (var i = 0; i < cols; i++)
		{
			if (!this.board.getCell (i, 0).value)
			{
				return {x: i, y: 0};
			}
			if (!this.board.getCell (i, rows-1).value)
			{
				return {x: i, y: rows-1};
			}
		}
		
		// random turn
		var cLen = nextCoordinates.length;
		if (cLen > 0)
		{
			return nextCoordinates [Math.floor (Math.random () * cLen)];
		}
		
		// nobody win
		return { nobody: true };
	}
});

var Game = Class.extend ({
	offsets: [],
	construct: function (toWin, cols, rows, cellWidth, cellHeight)
	{
		this.board = new Board (toWin, cols, rows, cellWidth, cellHeight);
		this.count = [0, 0];
		this.players = [];
		this.turn = 0;
		this.finish = false;
		this.chips = ['x', 'o'];
	},
	appendPlayer: function (player)
	{
		if (!(player instanceof Player))
		{
			return;
		}
		player.board = this.board;
		this.players.push (player);
		return this;
	},
	getBoard: function ()
	{
		return this.board;
	},
	next: function ()
	{
		this.turn = 1 - this.turn;
	},
	computerTurn: function ()
	{
		return this.turn === 1;
	},
	doTurn: function ()
	{	
		if (this.players.length == 0)
		{
			return;
		}
		var coordinates = this.players [this.turn].turn ();
		
		if (typeof coordinates.win != 'undefined')
		{
			// computer win
			this.finish = true;
		}
		
		if (typeof coordinates.nobody != 'undefined')
		{
			// nobody win
			//alert ('nobody');
			this.finish = true;
		}
		
		var x = coordinates.x, y = coordinates.y;
		
		if (x >= this.board.getCols () || y >= this.board.getRows ())
		{
			return;
		}
		
		if (this.finish)
		{
			return;
		}
		
		var cell = this.board.getCell (x, y);
		if (cell.value != 0)
		{
			return;
		}
		cell.content (this.computerTurn () ? -1 : 1, (new Chip (this.chips [this.turn]+(parseInt (x)+1)+''+(parseInt (y)+1)+'.png')).display (x, y, this.board.getCellWidth (), this.board.getCellHeight (), 0));
		
		var coordinates = this.board.nextCoordinates (this.board.toWin);
		if (typeof coordinates.win != 'undefined')
		{
			if (this.computerTurn ())
			{
				// computer win
			}
			else
			{
				// player win
			}
			this.finish = true;	
		}
		
		if (!this.finish)
		{
			this.next ();
		}
		else
		{
			//alert ('win');
		}
	}
});

$(document).ready (function ()
{
	var cols = 3, rows = 3, game = new Game (3, cols, rows, 90, 42);
	for (var i = 0; i < cols; i++)
	{
		for (var j = 0; j < rows; j++)
		{
			$('<div class="cell" id="cell-'+j+'-'+i+'"></div>').appendTo ($('#game'));
		}
	}
	game.appendPlayer (new Player ());
	game.appendPlayer (new Computer ());
	$('#game div.cell').click (function ()
	{
		if (!game.computerTurn ())
		{
			var tmp = $(this).attr ('id').split ('-');
			game.getBoard ().click (tmp [1], tmp [2]);
			game.doTurn ();
			game.doTurn ();
		}
	});
	var p = $('#article p'), count = p.length, page = 0, perpage = 3, pages = Math.ceil (count / perpage) - 1, i, st, cur;
	p.hide ();
	if (pages > 0)
	{
		$('#next').show ();
	}
	for (i = 0; i < perpage; i++)
	{
		st = page * perpage + i;
		cur = p.filter (':eq('+st+')');
		if (typeof cur[0] != 'undefined')
		{
			cur.show ();
		}
	}
	$('#next').click (function ()
	{
		$('#prev').show ();
		p.hide ();
		page++;
		if (page >= pages)
		{
			$(this).hide ();
		}
		for (i = 0; i < perpage; i++)
		{
			st = page * perpage + i;
			cur = p.filter (':eq('+st+')');
			if (typeof cur[0] != 'undefined')
			{
				cur.show ();
			}
		}
	});
	$('#prev').click (function ()
	{
		$('#next').show ();
		p.hide ();
		page--;
		if (page == 0)
		{
			$(this).hide ();
		}
		for (i = 0; i < perpage; i++)
		{
			st = page * perpage + i;
			cur = p.filter (':eq('+st+')');
			if (typeof cur[0] != 'undefined')
			{
				cur.show ();
			}
		}
	});
	$('#portfolio a').hover (
		function ()
		{
			$('<p></p>').text ($(this).attr ('title')).appendTo ($(this));
			$(this).find ('img').attr ('src', $(this).find ('img').attr ('src').replace ('.jpg', 'a.jpg'));
		},
		function ()
		{
			$(this).find ('p').remove ();
			$(this).find ('img').attr ('src', $(this).find ('img').attr ('src').replace ('a.jpg', '.jpg'));
		}
	);
	$('#works').click (function ()
	{
		$('#title').removeClass ('works').removeClass ('about').addClass ('works');
		$('#article').hide ();
		$('#portfolio').show ();
		$(this).addClass ('active');
		$('#about').removeClass ('active');
	});
	$('#about').click (function ()
	{
		$('#title').removeClass ('works').removeClass ('about').addClass ('about');
		$('#article').show ();
		$('#portfolio').hide ();
		$(this).addClass ('active');
		$('#works').removeClass ('active');
		
		page = 0;
		
		p.hide ();
		$('#prev').hide ();
		$('#next').show ();
		for (i = 0; i < perpage; i++)
		{
			st = page * perpage + i;
			cur = p.filter (':eq('+st+')');
			if (typeof cur[0] != 'undefined')
			{
				cur.show ();
			}
		}
		
	});
	$('#censored').hover (
		function ()
		{
			$(this).addClass ('no-bg');
		},
		function ()
		{
			$(this).removeClass ('no-bg');
		}
	);
});
