// sokoban.js - a Javascript/HTML Sokoban game clone of the KDE game KSokoban
// By Fredrik Fornwall, fredrikfornwall@gmail.com

IMAGE_SIZE = isNaN(parseInt(getCookie('bestSize'))) ? 48 : parseInt(getCookie('bestSize'))

// preload images
try {
	var images = ['blank.png', 'halfstone.png', 'object.png', 'goal.png', 'manblank.png', 'objectgoal.png', 'mangoal.png'];
	for (var i = 0; i < images.length; i++) {
		var image = new Image();
		image.src = images[i];
	}
} catch (e) { }

/**
 * "'"   -   Blank space (black background)
 * " "   -   Blank space inside of stones
 * "#"   -   Stones
 * "$"   -   Diamond on free ground
 * "*"   -   Diamond on target
 * "@"   -   Player starting position on free ground
 * "."   -   Target (where diamonds should be pushed)
 */
levelMaps = [
["'''###'''''",
"''## #'####",
"'##  ###  #",
"## $      #",
"#   @$ #  #",
"### $###  #",
"''#  #..  #",
"'## ##.# ##",
"'#      ##'",
"'#     ##''",
"'#######'''"],

["'##'#####",
"##'## . #",
"#'## $. #",
"'## $   #",
"## $@ ###",
"# $  ##''",
"#.. ##'##",
"#   #'##'",
"#####'#''"],

["'''''''''''#####",
"''''''''''##   #",
"''''''''''#    #",
"''''####''# $ ##",
"''''#  ####$ $#'",
"''''#     $ $ #'",
"'''## ## $ $ $#'",
"'''#  .#  $ $ #'",
"'''#  .#      #'",
"##### #########'",
"#.... @  #''''''",
"#....    #''''''",
"##  ######''''''",
"'####'''''''''''"],

["''###########",
"'##     #  @#",
"### $ $$#   #",
"# ##$    $$ #",
"#  #  $ #   #",
"###### ######",
"#.. ..$ #*##'",
"# ..    ###''",
"#  ..#####'''",
"#########''''"],

["''###########''",
"'##    #    ##'",
"### $ $#$ $ ###",
"# #$ $ # $ $# #",
"# $  ..#..  $ #",
"#  $...#...$  #",
"# $ .. * .. $ #",
"###### @ ######",
"# $ ..   .. $ #",
"#  $...#...$  #",
"# $  ..#..  $ #",
"# #$ $ # $ $# #",
"### $ $#$ $ ###",
"'##    #    ##'",
"''###########''"],

["''###########''",
"###.  .$.  .###",
"'## $  $  $ ##'",
"''## ..$.. ##'''",
"'''##$#$#$##''''",
"''''#.$ $.#'''''",
"''''#  @  #'''''",
"''''### ###'''''",
"'''## $ $ ##''''",
"'''#.  $  .#''''",
"'''### . ###''''",
"'''''#####''''''"],

["'''''''''''######''",
"''''####''##    #''",
"''###  #''#  ## ###",
"###    #### #   $ #",
"#  $ @ ...*..  $  #",
"# $ $  ## ###   ###",
"### ###   #'#####''",
"'#      ###''''''''",
"'#   ####''''''''''",
"'#####'''''''''''''"],

["''''#######''",
"''''#     ##'",
"##### ###  ##",
"#       #  ##",
"#@$***. ##$ #",
"#  #    ## .#",
"##  ##  # $ #",
"'##  ####.$.#",
"''##        #",
"'''######  ##",
"''''''''####'"],

["#########",
"#. .    #",
"#.$. .  #",
"## ###@ #",
"'#  $  ##",
"'# $$ ##'",
"'#  $ #''",
"'#  ###''",
"'####''''"],

["''''''######''''''",
"''''''#    #''''''",
"''''''# @  ###''''",
"####''#      #''''",
"#  ####..#.#$#####",
"# $ $ ##...      #",
"#     .....#$$   #",
"###### ##$## #####",
"'''''#  $    #'''''",
"'''''#### ####'''''",
"'''''''#  #''''''''",
"'''''''#  #####''''",
"'''''### $    #''''",
"'''''#  $ $   #''''",
"'''''# #$# ####''''",
"'''''#     #'''''''",
"'''''#######'''''''"],

["''####'''''",
"###  ####''",
"#   @   ##'",
"# #. .#.###",
"# $$$ $$$ #",
"###.#.#.# #",
"'##       #",
"''####  ###",
"'''''####''"],

["''''''''''''''#####''''''''",
"''''''''''''''#   #''''''''",
"''''#####'''''#   #######''",
"''''#   #####'#   ..... #''",
"##### # ##  #'#     # # #''",
"# $ $ $ $ $ #'##   ## $ #''",
"# # ##......#### ### $$ ###",
"#      ## * #    #'#  $$  #",
"##########+$$   ##'#      #",
"'''''''''#.$ $# #''########",
"'''''''''#.##   #''''''''''",
"'''''''''########''''''''''"],

["'''#######''",
"'###     ##'",
"'#   ###  #'",
"'#      # #'",
"###$#@  # #'",
"#   ##### #'",
"#   #  *. #'",
"##$$#  *.##'",
"'#     *..#'",
"'#### #...##",
"''''# #$$$ #",
"''''#   $  #",
"''''#####  #",
"''''''''####"],

["'#######''",
"##     #'#",
"#  *.$.#''",
"#  *.#.###",
"# #$@$$  #",
"#   ## # #",
"######   #",
"'''''#####"],
     
["'''####''''",
"'''#@ #''''",
"''##  ##'''",
"''# .$#####",
"''#$. #   #",
"###..$# # #",
"#  ..$  $ #",
"# $ $ # ###",
"##### # #''",
"''''#   #''",
"''''###.#''",
"''''''###''"],

["''######'''''''",
"'## #  ###'''''",
"## #   # ##''''",
"# #   $.# #''''",
"##  $ $.# #''''",
"# #####. ##''''",
"#     $. @#''''",
"#     $. ####''",
"### # #*# # ###",
"''#### .$     #",
"''''#  .$     #",
"''''## .##### #",
"''''# #.$ $  ##",
"''''# #.$   # #",
"''''## #   # ##",
"'''''###  # ##'",
"'''''''######''"]

]

function init() {
	sel = document.getElementsByTagName('select').item(0)
	for (var i = 1; i < levelMaps.length; i++) {
		var option = document.createElement('option')
		option.setAttribute('value', '' + i)
		option.appendChild(document.createTextNode('Level ' + i))
		sel.appendChild(option)
	}
	loadLevel()
}

function createImage(id, src) {
	var result = document.createElement('img');
	result.setAttribute('id', id);
	result.setAttribute('width', IMAGE_SIZE);
	result.setAttribute('height', IMAGE_SIZE);
	result.setAttribute('src', src);
	return result;
}

function stripPath(location) {
	// Fix: Opera has the full src path (eg. '/fornwall.net/games/sokoban/blank.png'), so remove the prepended path
	if (location.indexOf('/') != -1) {
		var index = location.indexOf('/');
		var prevIndex = index;
		while (index != -1) {
			prevIndex = index;
			index = location.indexOf('/', index + 1);
		}
		location = location.substring(prevIndex + 1);
	}
	return location;
}

document.onkeydown = function(e) {
	var newX = playerX;
	var newY = playerY;

	switch (e.keyCode) { // wasd keys: different range for Mozilla and Konqueror
		case 38: newY--; break;
		case 39: newX++; break;
		case 40: newY++; break;
		case 37: newX--; break;
		default: return;
	}

	e.preventDefault(); // XXX: Does nothing?

	var target = document.getElementById(newX + ',' + newY);
	var targetData = stripPath(target.getAttribute('src'));
	if (targetData == 'halfstone.png') return false;

	if (targetData == 'object.png' || targetData == 'objectgoal.png') {
		var pushingTo = document.getElementById((2 * newX - playerX) + ',' + (2 * newY - playerY));
		var pushingToData = stripPath(pushingTo.getAttribute('src'));

		if (pushingToData != 'blank.png' && pushingToData != 'goal.png') return false;
		if (pushingToData == 'goal.png') correctlyPlaced++;
		if (targetData == 'objectgoal.png') correctlyPlaced--;
		targetData = (targetData == 'objectgoal.png') ? 'goal.png' : 'blank.png';

		var pushedTo = createImage((2 * newX - playerX) + ',' + (2 * newY - playerY), (pushingToData == 'goal.png') ? 'objectgoal.png' : 'object.png');
		pushingTo.parentNode.replaceChild(pushedTo, pushingTo);
	}
        
	var old = document.getElementById(playerX + ',' + playerY);
	var newData = 'man' + targetData;

	var image = createImage(playerX + ',' + playerY, (stripPath(old.getAttribute('src')) == 'mangoal.png') ? 'goal.png' : 'blank.png');
        old.parentNode.replaceChild(image, old);
        
        var image2 = createImage(newX + ',' + newY, newData);
        target.parentNode.replaceChild(image2, target);

        playerX = newX;
        playerY = newY;

        if (correctlyPlaced == numberOfObjects) {   // Player has finished the game
                var name = prompt('Congratulations!\n\nEnter your name for the highscore list:');
                var body = document.getElementsByTagName('body')[0];
                var form = document.createElement('form');
        	form.setAttribute('method', 'post');
        	form.setAttribute('action', '/games/highscore/?game=sokoban');
        	form.setAttribute('style', 'display: none;');
        	body.appendChild(form);
	        var input = document.createElement("input");
	        input.setAttribute('type', 'text');
	        input.setAttribute('name', 'name');
	        input.setAttribute('value', name);
	        form.appendChild(input);
	        var scoreInput = document.createElement('input');
	        scoreInput.setAttribute('type', 'text');
	        scoreInput.setAttribute('name', 'level');
	        scoreInput.setAttribute('value', document.forms[0].newLevel.selectedIndex);
	        form.appendChild(scoreInput);
	        form.submit();
        }

	return false;
}

function loadLevel() {
        var currentLevel = document.forms[0].newLevel.selectedIndex;
        document.forms[0].newLevel.blur(); // remove focus from the level selector so it doesn't scroll when player moves
        var tableBody = document.getElementById('tableBody');
        while (tableBody.hasChildNodes()) tableBody.removeChild(tableBody.firstChild);
        
        correctlyPlaced = 0;
        numberOfObjects = 0;
        
        var levelMap = levelMaps[currentLevel];
        
        for (var row = 0; row < levelMap.length; row++) {
                var newRow = document.createElement('tr')
                tableBody.appendChild(newRow)
                for (var column = 0; column < levelMap[0].length; column++) {
                        var source = null;
                        switch (levelMap[row].charAt(column)) {
                                case '#':  source = 'halfstone.png'; break;
                                case '$':  source = 'object.png'; numberOfObjects++; break;
                                case '.':  source = 'goal.png'; break;
                                case '@':  source = 'manblank.png'; playerX = column; playerY = row; break;
                                case '\'': source = null; break;
                                case '*':  source = 'objectgoal.png'; numberOfObjects++; correctlyPlaced++; break;
                                case '+':  source = 'mangoal.png'; playerX = column; playerY = row; break;
				case ' ':  source = 'blank.png'; break;
				default: alert('Unrecognized map character: "' + levelMap[row].charAt(column) + '"');
                        }
                        var newColumn = document.createElement('td')
                        if (source != null) {
                                newColumn.appendChild(createImage(column + ',' + row, source));
                        }
                        newRow.appendChild(newColumn)
                }
        }
}

function changeSize(change) {
	IMAGE_SIZE += change;
	try {
		document.cookie = 'bestSize = ' + IMAGE_SIZE + '; expires=' + new Date("January 1, 2006").toGMTString();
	} catch (e) {
		alert('Could not write cookie!');
	}
	var images = document.getElementsByTagName('img');
	for (var i = 0; i < images.length; i++) {
		images[i].setAttribute('width', IMAGE_SIZE);
		images[i].setAttribute('height', IMAGE_SIZE);
	}
}

function getCookie(name) {
        var prefix = name + "=";
        var begin = document.cookie.indexOf("; " + prefix);
        if (begin == -1) {
                begin = document.cookie.indexOf(prefix);
                if (begin != 0) return null;
        } else {
                begin += 2;
        }
        var end = document.cookie.indexOf(";", begin);
        if (end == -1) end = document.cookie.length;
        return unescape(document.cookie.substring(begin + prefix.length, end));
}

function help() {
	alert('Move the man with the arrow keys.\nTry to push the red diamonds onto the green areas without getting stuck.');
}
