
Array.prototype.max = function () {
	if (this.length==0) return; 
	for(var m=this[0],i=0; i<this.length; i++) {
		if (this[i] > m) m = this[i];
	}
	return m;
}
Array.prototype.add = function (a) {
	var r = [];
	for (var i=0; i<this.length; i++) {
		r[i] = (i<a.length) ? this[i]+a[i] : this[i];
	}
	return r;
}
document._createElement = document.createElement;
document.createElement = function (type) {
    var e = document._createElement(type);
    e.deleteClass = function (str) {
        this.className = (" "+this.className+" ").replace(" "+str+" ", " ").trim();
    }
    e.addClass = function (str) {
        if (!this.hasClass(str)) this.className = (this.className+' '+str).trim();
    }
    e.hasClass = function (str) {
        return ('  '+this.className+' ').indexOf(' '+str+' ') > 0;
    }
    return e;
}
String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,'');};
function SudokuBoard() {
    var self = this;
    var cell = [[], [], [], [], [], [], [], [], []];
    var oTime = false;
    var oBoard;
    var oSelectedCell = false;

    this.hasSelectedCell = function () {
        return oSelectedCell != false;
    }
    
    this.drawBoard = function () {
        oBoard = document.createElement('div');
        oBoard.className = 'board';
        for (var s=0; s<9; s++) {
            var square = document.createElement('div');
            square.className = 'square';
            for (var c=0; c<9; c++) {
                var obj = document.createElement('div');
                obj.x = (s%3)*3 + c%3;
                obj.y = Math.floor(s/3)*3 + Math.floor(c/3);
                obj.className = 'cell';
                obj.board = this;
                obj.onclick = function (event) {
                    var cell = event ? event.target : window.event.srcElement;
                    cell.board.setSelectedCell(cell);
                }
                cell[obj.x][obj.y] = obj;
                square.appendChild(obj);
            }
            oBoard.appendChild(square);
        }
        oBoard.started = false;
        oBoard.paused = false;
        return oBoard;
    }
    
    this.updateTime = function () {
        var seconds = this.getTime();
        var text = Math.floor(seconds/60)+':'+((seconds%60)<10 ? '0' : '') + (seconds%60);
        while(oTime.hasChildNodes()) oTime.removeChild(oTime.lastChild);
        oTime.appendChild(document.createTextNode(text));
    }
    var startTime = function () {
        oTime.gameStarted = new Date().getTime();
    }
    var stopTime = function () {
        if (!oTime.gameStarted) return;
        oTime.offsetTime += Math.floor((new Date().getTime() - oTime.gameStarted)/1000);
        oTime.gameStarted = 0;
    }
    var resetTime = function () {
        oTime.offsetTime = 0;
        oTime.gameStarted = 0;
    }
    this.getTime = function () {
        if (!oTime.gameStarted) return oTime.offsetTime;
        return Math.floor((new Date().getTime() - oTime.gameStarted)/1000) + oTime.offsetTime;
    }
    this.drawTime = function () {
        oTime = document.createElement('div');
        oTime.className = 'time';
        oTime.offsetTime = 0;
        oTime.gameStarted = 0;
        return oTime;
    }
   
    this.moveSelectedCell = function (i) {
        if (!oSelectedCell) return;
        var x = oSelectedCell.x;
        var y = oSelectedCell.y;
        switch (i) {
            case 0 : x = (x+8) % 9; break;
            case 1 : y = (y+8) % 9; break;
            case 2 : x = (x+1) % 9; break;
            case 3 : y = (y+1) % 9; break;
        }
        this.setSelectedCell(cell[x][y]);
    }
    this.setSelectedCell = function (obj) {
        if (oSelectedCell) {
            oSelectedCell.deleteClass('selected');
        }
        oSelectedCell = obj;
        if (obj) {
            oSelectedCell.addClass('selected');
        }
    }
    this.setSelectedValue = function (value) {
        if (!oSelectedCell || oSelectedCell.hasClass('given')) return;
        if (!oBoard.hasClass('started') || oBoard.hasClass('complete') || oBoard.hasClass('paused')) return;
        var v = parseInt(value);
        if (v>=0 && v<=9) {
            setValueByCell(value, oSelectedCell);
        }
        this.validate();
    }

    var setValueByCell = function (value, cell, temp) {
        while(cell.hasChildNodes()) cell.removeChild(cell.lastChild);
        if (typeof value != 'undefined' && value != 0) {
            cell.appendChild(document.createTextNode(value));
        }
        if (!temp) {
            cell.value = value;
        }
    }
    this.getSolution = function () {
        var solution = '';
        for (var y=0; y<9; y++) {
            for (var x=0; x<9; x++) {
                if (cell[x][y].value > 0) solution += cell[x][y].value;
                else return false;
            }
        }
        return solution;
    }
    
    this.validate = function () {
        var count = 0;
	    for (var y=0; y<9; y++) {
    		var nx = [0,0,0,0,0,0,0,0,0];
		    var ny = [0,0,0,0,0,0,0,0,0];
	    	var nz = [0,0,0,0,0,0,0,0,0];
    		for (var x=0; x<9; x++) {
			    var X = (y%3)*3 + x%3;
                var Y = Math.floor(y/3)*3 + Math.floor(x/3);
                if (cell[x][y].value > 0) {
                    nx[cell[x][y].value-1]++;
                    count++;
                }
    			if (cell[y][x].value > 0) {
                    ny[cell[y][x].value-1]++;
                }
			    if (cell[X][Y].value > 0) {
                    nz[cell[X][Y].value-1]++;
                }
                if (nx.max() > 1 || ny.max() > 1 || nz.max() > 1) {
                    oBoard.addClass('error');
                    this.onError();
                    return false;
                }
		    }
	    }
        oBoard.deleteClass('error');
        if (count == 81) {
            stopTime();
            this.setSelectedCell(null);
            oBoard.addClass('complete');
            oBoard.deleteClass('started');
            this.onComplete();
        }
	    return count;
    }
    this.onStart = function () {}
    this.onComplete = function () {}
    this.onError = function () {}

    this.initialize = function (values) {
        for (var i=0; i<values.length; i++) {
            var x = i%9;
            var y = Math.floor(i/9);
            if (values[i] != '') {
                cell[x][y].addClass('given');
            }
            else {
                cell[x][y].deleteClass('given');
            }
            setValueByCell(values[i], cell[x][y]);
        }
        oBoard.className = 'board';
        resetTime();
    }

    this.start = function () {
        oBoard.addClass('started');
        startTime();
        this.onStart();
    }
    this.pause = function () {
        if (!oBoard.hasClass('started')) return;
        stopTime();
        for (var y=0; y<9; y++) {
            for (var x=0; x<9; x++) {
                if (cell[x][y].value != '') {
                    setValueByCell('\u00D7', cell[x][y], true);
                }
            }
        }
        oBoard.addClass('paused');
    }
    this.resume = function () {
        if (!oBoard.hasClass('paused')) return;
        for (var y=0; y<9; y++) {
            for (var x=0; x<9; x++) {
                setValueByCell(cell[x][y].value, cell[x][y]);
            }
        }
        oBoard.deleteClass('paused');
        startTime();
    }
}

