/**
 * @author Egor Hmelyoff (egor@design.ru)
 * @copyright Art.Lebedev Studio (http://www.artlebedev.ru)
 */

function randomNum(iMin, iMax){
	return Math.round(Math.random()*(iMax - iMin) + iMin);
}

Array.prototype.shuffle = function() {
	var len = this.length;
	for (var i=0; i < len; i++) {
		var rand = Math.floor(Math.random()*len);
		var temp = this[i];
		this[i] = this[rand];
		this[rand] = temp;
	}
}


/**
 * Labirint game 
 */
var Labirint={
	
	paused: true,
	finish: false,
	idEvt: null,
	idMove: null,
	finishY: 13,
	highlighted: false,
	begined: false,

	blockWidth: 10,
	blockHeight: 10,
	
	/* some 41x43 level */
	lab: [
		[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
		[1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1],
		[1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1],
		[1,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1],
		[1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1],
		[1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,1],
		[1,0,1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1],
		[1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1],
		[1,1,1,1,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1],
		[1,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1],
		[1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1],
		[1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,1],
		[1,0,1,0,1,1,1,1,1,0,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,1,1,0,1],
		[1,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,0,0,0,0,1,0,0,0,1],
		[1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,0,1],
		[1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1],
		[1,0,1,0,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,0,3],
		[1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,0,0,1],
		[1,0,1,1,1,1,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,0,1,1,1],
		[1,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1],
		[1,0,1,0,1,0,1,1,1,0,1,1,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,0,1],
		[1,0,0,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1],
		[1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1],
		[1,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1],
		[1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,0,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,0,1,1,1,0,1,0,1],
		[1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,1],
		[1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1],
		[1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1],
		[1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,0,1,0,1],
		[1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1],
		[1,1,1,0,1,0,1,1,1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1],
		[1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1],
		[1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,1,1,0,1],
		[1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,1],
		[1,0,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,0,1,0,1],
		[1,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,1],
		[1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,0,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,1,1,1,1],
		[1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,1],
		[1,0,1,0,1,1,1,0,1,1,1,1,1,0,1,0,1,0,1,1,1,1,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,0,1],
		[1,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1],
		[1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,0,1,0,1],
		[2,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1],
		[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
	],
	
	state: {
		s2: [
			[
				[0,1,0],
				[0,1,0],
				[0,0,0]
			],
			[
				[0,0,0],
				[0,1,1],
				[0,0,0]
			],
			[
				[0,0,0],
				[0,1,0],
				[0,1,0]
			],
			[
				[0,0,0],
				[1,1,0],
				[0,0,0]
			]
		],
		s3: [
			[
				[0,1,0],
				[0,1,1],
				[0,0,0]
			],
			[
				[0,0,0],
				[0,1,1],
				[0,1,0]
			],
			[
				[0,0,0],
				[1,1,0],
				[0,1,0]
			],
			[
				[0,1,0],
				[1,1,0],
				[0,0,0]
			],
			
			[
				[0,1,0],
				[0,1,0],
				[0,1,0]
			],
			[
				[0,0,0],
				[1,1,1],
				[0,0,0]
			],
		],
		s4: [
			[
				[0,1,0],
				[1,1,1],
				[0,0,0]
			],
			[
				[0,1,0],
				[0,1,1],
				[0,1,0]
			],
			[
				[0,0,0],
				[1,1,1],
				[0,1,0]
			],
			[
				[0,1,0],
				[1,1,0],
				[0,1,0]
			]
		],
		s5: [
			[
				[0,1,0],
				[1,1,1],
				[0,1,0]
			]
		]
	},
	
	init: function(id){

		this.container = $(id);

		/* Generate random labirint */
		this.lab = new LabGenerator(20,20);
		this.container.css({ height: Labirint.lab.length*Labirint.blockHeight, width: Labirint.lab[0].length*Labirint.blockWidth })
		
		this.isInit();
		
		/* Add start point to labirint */
		this.lab[this.lab.length-2][0] = 2;
		
		/* Add finish point to labirint */
		this.lab[this.finishY][this.lab[this.finishY].length-1] = 3;
		if(this.lab[this.finishY][this.lab[this.finishY].length-2] == 1)
			this.lab[this.finishY][this.lab[this.finishY].length-2] = 0;

		this.createField(this.lab);
		this.createPlayer();
		this.initEvents();
	},
	
	isInit: function(){
		
	},
	
	highlight: function(){
		if(!this.begined && !this.highlighted){
			this.highlighted = true;
			this.startHelp.fadeOut("fast").fadeIn("fast").fadeOut("fast").fadeIn("fast", function(){
				Labirint.highlighted = false;
			});
		}
	},
	
	createField: function(lab){
		for (var i=0; i < lab.length; i++) {
			for (var j=0; j < lab[i].length; j++) {
				if(lab[i][j] == 1){
					var s = this.getState(i, j);
					$(document.createElement('div')).addClass('wall').addClass('s'+s.state+'_i'+s.index).css({ top: i*Labirint.blockWidth, left: j*Labirint.blockHeight, width: Labirint.blockWidth, height: Labirint.blockHeight }).appendTo(this.container);
				}

				if(lab[i][j] == 2){
					this.aStart = [i, j];
				}

				if(lab[i][j] == 3){
					this.aFinish = [i, j];
				}
			
			};
		};
	},
	
	getState: function(row, col){
		var a = new Array();
		var s = 0;
		for (var i=0; i < 3; i++) {
			a[i] = new Array();
			for (var j=0; j < 3; j++) {
				if(this.lab[row+(i-1)] && this.lab[row+(i-1)][col+(j-1)] && ((i-1)*(j-1) == 0)){
					a[i][j] = this.lab[row+(i-1)][col+(j-1)];
					if(a[i][j] > 1)
						a[i][j] = 0;
				} else {
					a[i][j] = 0;
				}
				
				if(a[i][j] == 1)
					s++;
			};
		};
		
		var ind = 0;
		if(this.state['s' +s]){
			for (var k=0; k < this.state['s'+s].length; k++) {
				if(this.state['s' +s][k] && this.state['s' +s][k].join() == a.join()){
					ind = k;
				}
			}
		}
		
		return { state: s, index: ind };
	},
	
	createPlayer: function(){
		this.player = $(document.createElement('div')).addClass('player').css({ left: (Labirint.aStart[1]-4)*Labirint.blockWidth, top: (Labirint.aStart[0])*Labirint.blockHeight }).appendTo(this.container);
		this.startHelp = $(document.createElement('div')).addClass('help').css({ left: (Labirint.aStart[1]-2)*Labirint.blockWidth, top: (Labirint.aStart[0])*Labirint.blockHeight }).appendTo(this.container);
	},
	
	initEvents: function(){

		this.keybug = $(document.createElement('input')).attr({ type: 'text' }).appendTo(this.container);
		this.keybug.css({ opacity: 0 }).blur(function(){ if(Labirint.isBlockKeyboard){ $(this).focus(); } });
		$(document.body).click(function(){ if(Labirint.isBlockKeyboard){ Labirint.keybug.focus(); } });
		
		$(document).keydown(function(evt){
			if(Labirint.idEvt != evt.keyCode){
				Labirint.idEvt = evt.keyCode;
				switch(evt.keyCode){
					case 37: // left
						clearTimeout(Labirint.idMove);
						Labirint.moveLeft();
						Labirint.idMove = setTimeout(function(){ Labirint.idMove = setInterval(function(){ Labirint.moveLeft() }, 60); }, 300);
						break;
					case 38: // top
						clearTimeout(Labirint.idMove);
						Labirint.moveTop();
						Labirint.idMove = setTimeout(function(){ Labirint.idMove = setInterval(function(){ Labirint.moveTop() }, 60); }, 300);
						break;
					case 39: // right
						clearTimeout(Labirint.idMove);
						Labirint.moveRight();
						Labirint.idMove = setTimeout(function(){ Labirint.idMove = setInterval(function(){ Labirint.moveRight() }, 60); }, 300);
						break;
					case 40: // down
						clearTimeout(Labirint.idMove);
						Labirint.moveDown();
						Labirint.idMove = setTimeout(function(){ Labirint.idMove = setInterval(function(){ Labirint.moveDown() }, 60); }, 300);
						break;
					case 32: // space
						break;
				}
			}
		});

		$(document).keyup(function(evt){
			clearTimeout(Labirint.idMove);
			Labirint.idEvt = null;
		});
		
	},
	
	moveLeft: function(){
		if(!this.finish && !this.paused){
			if(this.moveAvailable(this._current.i, this._current.j-1)){
				this.setPosition(this._current.i, this._current.j-1);
			}
		}
	},

	moveTop: function(){
		if(!this.finish && !this.paused){
			if(this.moveAvailable(this._current.i-1, this._current.j)){
				this.setPosition(this._current.i-1, this._current.j);
			}
		}
	},

	moveRight: function(){
		if(!this.finish){
			if(this.paused){
				if(!this.begined){
					this.begined = true;
					this.startHelp.fadeOut("normal");
					$(".labirint p span.link").removeClass("link");
					this.player.animate({ left: Labirint.aStart[1]*Labirint.blockWidth, top: Labirint.aStart[0]*Labirint.blockHeight }, 300, function(){
						$(this).css({ left: (Labirint.aStart[1]+1)*Labirint.blockWidth, top: Labirint.aStart[0]*Labirint.blockHeight });
						Labirint._current = { i: Labirint.aStart[0], j: Labirint.aStart[1]+1 }
						Labirint.paused = false;
						Labirint.focus();
					})
				}
			} else {
				if(this.moveAvailable(this._current.i, this._current.j+1)){
					this.setPosition(this._current.i, this._current.j+1);
				}
			}
		}
	},

	moveDown: function(){
		if(!this.finish && !this.paused){
			if(this.moveAvailable(this._current.i+1, this._current.j)){
				this.setPosition(this._current.i+1, this._current.j);
			}
		}
	},
	
	focus: function(){
		Labirint.keybug.focus();
		Labirint.isBlockKeyboard = true;
	},

	blur: function(){
		Labirint.isBlockKeyboard = false;
		Labirint.keybug.blur();
	},
	
	moveAvailable: function(i, j){
		if(this.lab[i][j] != 1 && this.lab[i][j] != 2){
			if(this.lab[i][j] == 3){
				this.finishGame();
				return false;
			} else {
				return true;
			}
		} else {
			return false;
		}
	},
	
	setPosition: function(i, j){
		this.player.css({ left: j*Labirint.blockWidth, top: i*Labirint.blockHeight });
		this._current = { i: i, j: j };
	},
	
	finishGame: function(){
		this.finish = true;
		this.paused = true;
		this.player.animate({ left: Labirint.aFinish[1]*Labirint.blockWidth+($(".queen").offset().left - (Labirint.container.offset().left + Labirint.container.width()) + 50), top: Labirint.aFinish[0]*Labirint.blockHeight }, 300, function(){
			$(this).hide();
		});
		var _o = $(".queen div.selected");
		var w = _o.width();
		$(".queen div.selected").css({ width: 0 }).animate({ width: w }, 200);
		Labirint.blur();
	}

}


/** 
 * Use Depth-first search algorithm
 */
function LabGenerator(cols, rows){
	
	this.cols = cols;
	this.rows = rows;
	
	this.cellStack = new Array();
	this.totalCells = this.cols * this.rows;
	this.visitedCells = 0;
	
	this.map = new Array();
	
	for (var y=0; y < this.rows; y++) {
		this.map[y] = new Array();
		for (var x=0; x < this.cols; x++) {
			this.map[y][x] = new Array();
			this.map[y][x]["bottom"] = 1;
			this.map[y][x]["right"] = 1;
			this.map[y][x]["visited"] = false;
		}
	}

	x = randomNum(0, this.cols-1);
	y = randomNum(0, this.rows-1);

	this.cellStack.push([x, y]);
	this.map[y][x]["visited"] = true;
	this.visitedCells = 1;
	
	while (this.visitedCells < this.totalCells) {

		var directions = new Array();

		if (y > 0 && this.map[y-1][x]["visited"] == false) directions.push("n");
		if (y < this.rows-1 && this.map[y+1][x]["visited"] == false) directions.push("s");
		if (x > 0 && this.map[y][x-1]["visited"] == false) directions.push("w");
		if (x < this.cols-1 && this.map[y][x+1]["visited"] == false) directions.push("e");

		directions.shuffle();

		var newDir = "";
		var newX = x;
		var newY = y;
		for (var d=0; d < directions.length; d++) {
			if (directions[d] == "n") {
				if (this.map[y-1][x]["visited"] == false) {
					newX = x; newY = y-1; newDir = "n"; break;
				}
			} else if (directions[d] == "s") {
				if (this.map[y+1][x]["visited"] == false) {
					newX = x; newY = y+1; newDir = "s"; break;
				}
			} else if (directions[d] == "e") {
				if (this.map[y][x+1]["visited"] == false) {
					newX = x+1; newY = y; newDir = "e"; break;
				}
			} else if (directions[d] == "w") {
				if (this.map[y][x-1]["visited"] == false) {
					newX = x-1; newY = y; newDir = "w"; break;
				}
			}
		}

		if (newDir != "") {
			switch (newDir) {
				case "n": this.map[y-1][x]["bottom"] = 0; break;
				case "e": this.map[y][x]["right"] = 0; break;
				case "s": this.map[y][x]["bottom"] = 0; break;
				case "w": this.map[y][x-1]["right"] = 0; break;
			}

			this.cellStack.push([newX, newY]);

			x = newX;
			y = newY;

			this.map[y][x]["visited"] = true;
			this.visitedCells ++;

		} else {

			temp = this.cellStack.pop();
			x = temp[0];
			y = temp[1];
		}

	}

	/* Standardizing labirint */
	this.myMap = new Array();

	for (var i=0; i < this.map.length*2+1; i++) {
		this.myMap[i] = new Array();
		for (var j=0; j < this.map[0].length*2+1; j++) {
			this.myMap[i][j] = 1;
		};
	};
	
	for (var i=0; i < this.map.length; i++) {
		for (var j=0; j < this.map[i].length; j++) {
			this.myMap[1+i*2][1+j*2] = 0;
			if(this.map[i][j]["right"] == 0){
				this.myMap[1+i*2][1+j*2+1] = 0;
			}
			if(this.map[i][j]["bottom"] == 0){
				this.myMap[1+i*2+1][1+j*2] = 0;
			}
		};
	};
	
	return this.myMap;
	
}

$(document).ready(function(){
	Labirint.init('#Labirint');
})
