var sudoku = new Object();
sudoku.cfg = new Object();
sudoku.cfg.values = new Array();
sudoku.cfg.solution = new Array();
sudoku.cfg.inptPrefix = 'inpt';
sudoku.cfg.timeoutHighlight = 5000; // milliseconds
sudoku.cfg.timeoutCheckGame = 500; // milliseconds
sudoku.cfg.inptAutocheckGame = "inptAutocheckGame";
sudoku.cfg.inptInitValues = "inptInitValues";
sudoku.cfg.inptInitSolution = "inptInitSolution";
sudoku.cfg.gameFieldTable = "sudokutab";
sudoku.txt = new Object();
sudoku.txt.max3digits = 'Je možno vložit max 3 varianty do jednoho políčka';
sudoku.txt.errorRow = 'Máte chybu v řádce {NUMBER}. Vícekrát se opakuje číslo {ERR_NUMBER}.';
sudoku.txt.errorColumn = 'Máte chybu ve sloupci {NUMBER}. Vícekrát se opakuje číslo {ERR_NUMBER}.';
sudoku.txt.errorBox = 'Máte chybu v boxu {NUMBER}. Vícekrát se opakuje číslo {ERR_NUMBER}.';
sudoku.txt.gameVerified = 'Hrací pole je vyplněno v pořádku';
sudoku.txt.gameReseted = 'Hra byla vyresetována';
sudoku.txt.gameFinished = 'Gratulujeme! Sudoku bylo vyřešeno! Celkový čas {TIME}';
sudoku.txt.gameCorrectness = 'Nalezeno {ERRORS} chyb oproti správnému řešení';
sudoku.txt.confirmNewGame = 'Opravdu začít novou hru?';
sudoku.txt.confirmSolveGame = 'Opravdu vyřešit hru?';
sudoku.memory = new Object();
sudoku.memory.abandon = false;
sudoku.memory.finished = false;

// -----------------------------------------------
// fce spousti hru
sudoku.initGame = function()
{
	var inptValuesStr = this.geid(this.cfg.inptInitValues);
	var inptSoluStr = this.geid(this.cfg.inptInitSolution);
	if(inptValuesStr == null || inptSoluStr == null)
	{
		this.alert('Došlo k chybě při načítání hry. Hra nebude funkční')
		return;
	}
	
	this.cfg.values = new Array(81);
	this.cfg.solution = new Array(81);
	
	for(var x = 0; x < inptValuesStr.value.length; x++)
	{
		this.cfg.values[x] = inptValuesStr.value.charAt(x);
	}
	
	for(var x = 0; x < inptSoluStr.value.length; x++)
	{
		this.cfg.solution[x] = inptSoluStr.value.charAt(x);
	}
	
}

// -----------------------------------------------
// fce spousti hru
sudoku.newGame = function(confirmIt)
{
	if(confirmIt == true  &&  !confirm(this.txt.confirmNewGame))
		return;
	
	if(confirmIt == true)
		this.alert(this.txt.gameReseted);
	
	this.areaHighlight(false);
	this.elemHighlight(this.geid(this.cfg.gameFieldTable), false);
	timer.start();
	this.memory.abandon = false;
	this.memory.finished = false;
	
	var row = 0;
	var cell = 0;
	
	// vyplnit hodnoty
	for(var x = 0; x < this.cfg.values.length; x++)
	{
		var elem = this.getInpt(cell, row);
		
		
		if(this.cfg.values[x] > 0)
		{
			elem.value = this.cfg.values[x];
			elem.readOnly = true;
			elem.className = 'orig';
		}
		else
		{
			elem.value = '';
			elem.readOnly = false;
		}
		
		cell++;
		if(cell == 9) { row++; cell = 0; }
	}
	
}


// -----------------------------------------------
// fce vyresi hru
sudoku.solveGame = function()
{
	if(this.memory.finished)
		return;
	
	if(!confirm(this.txt.confirmSolveGame))
		return;
	
	this.areaHighlight(false);
	this.memory.abandon = true;
	this.memory.finished = true;
	timer.stop();
	
	var row = 0;
	var cell = 0;

	// vyplnit hodnoty
	for(var x = 0; x < this.cfg.solution.length; x++)
	{
		this.setInptVal(cell, row, this.cfg.solution[x]);
		
		cell++;
		if(cell == 9) { row++; cell = 0; }
	}
}


// ------------------------------------------------
// porovna naklikany vysledek s pozadovanym
sudoku.checkGameCorrectness = function(doAlert)
{
	if(this.memory.finished)
		return;
	
	var row = 0;
	var cell = 0;
	var errors = 0;
	
	this.areaHighlight(false);
	
	// projit vysledne hodnoty
	for(var x = 0; x < this.cfg.solution.length; x++)
	{
		var realval = this.getInptVal(cell, row);
		var solval = this.cfg.solution[x];

		if(realval != null)
		{
			if(parseInt(realval) != solval)
			{
				if(doAlert == true) this.inptHighlight(true, cell, row);
				errors++;
			}
		}
		
		cell++;
		if(cell == 9) { row++; cell = 0; }
	}
	
	if(doAlert == true)
		this.alert(this.txt.gameCorrectness.replace('{ERRORS}', errors));
	
	return (errors == 0) ? true : false;
}


// ------------------------------------------------
// prozkouset herni pole
sudoku.autocheckGame = function()
{
	if(this.geid(this.cfg.inptAutocheckGame) == null  ||  !(this.geid(this.cfg.inptAutocheckGame).checked))
		return;
	
	clearTimeout(this.memory.intvl_2);
	
	this.memory.intvl_2 = setTimeout("sudoku.checkGame();", this.cfg.timeoutCheckGame);
	
}


// ------------------------------------------------
// automaticky kontroluje, jestli hra uz skoncila
sudoku.checkGameEnd = function()
{
	var stopSearching = false;
	var completed = true; // zda jsou vsechna pole vyplnena
	
	// overit kazdou radku
	for(var y = 0; y < 9; y++)
	{
		if(stopSearching) break;
		for(var x = 0; x < 9; x++)
		{
			var val = this.getInptVal(x, y);
			if(val == null)
			{
				completed = false;
				stopSearching = true;
				break;
			}
		}
	}
	
	if(!completed)
		return;
	
	if(this.checkGameCorrectness(false))
	{
		this.gameFinished();
	}
	
}


// ------------------------------------------------
// ukonci hru a oznami
sudoku.gameFinished = function()
{
	this.memory.finished = true;
	this.elemHighlight(this.geid(this.cfg.gameFieldTable), true);
	timer.stop();
	this.alert(this.txt.gameFinished.replace('{TIME}', timer.getTime()));
}


// ------------------------------------------------
// prozkouset herni pole
sudoku.checkGame = function(userAction)
{
	if(this.memory.finished)
		return;
	
	var stopSearching = false;
	// nejdrive schovat pripadne zvyrazneni
	this.areaHighlight(false);
	
	
	// overit kazdou radku
	for(var y = 0; y < 9; y++)
	{
		if(stopSearching) break;
		var filled = new Array(9);
		for(var x = 0; x < 9; x++)
		{
			var val = this.getInptVal(x, y);
			if(val == null) continue;
			
			if(filled[val] == true)
			{
				this.alert(this.txt.errorRow.replace('{NUMBER}', y+1).replace('{ERR_NUMBER}', val));
				this.areaHighlight(true, 'row', null, y);
				stopSearching = true;
				break;
			}
			filled[val] = true;
		}
	}
	if(stopSearching) return false;
	
	// overit sloupce
	for(var x = 0; x < 9; x++)
	{
		if(stopSearching) break;
		var filled = new Array(9);
		for(var y = 0; y < 9; y++)
		{
			var val = this.getInptVal(x, y);
			if(val == null) continue;
			
			if(filled[val] == true)
			{
				this.alert(this.txt.errorColumn.replace('{NUMBER}', x+1).replace('{ERR_NUMBER}', val));
				stopSearching = true;
				this.areaHighlight(true, 'column', x, null);
				break;
			}
			filled[val] = true;
		}
	}
	if(stopSearching) return false;
	
	// overit jednotlive boxy 3x3
	// 3x boxy v Y ose
	for(var by = 0; by < 3; by++)
	{
		if(stopSearching) break;
		// 3x boxy v X ose
		for(var bx = 0; bx < 3; bx++)
		{
			if(stopSearching) break;
			var filled = new Array(9);
			// radky v boxu XY
			for(var y = 0; y < 3; y++)
			{
				if(stopSearching) break;
				// sloupce v boxu XY
				for(var x = 0; x < 3; x++)
				{
					var cellnumber = x + (y * 3);
					var cx = (bx * 3) + x;
					var cy = (by * 3) + y;
					var val = this.getInptVal(cx, cy);
					if(val == null) continue;
					
					if(filled[val] == true)
					{
						this.alert(this.txt.errorBox.replace('{NUMBER}', (bx + 1)+'x'+(by + 1)).replace('{ERR_NUMBER}', val));
						stopSearching = true;
						this.areaHighlight(true, 'box', bx, by);
						break;
					}
					filled[val] = true;
				}
			}
		}
	}
	if(stopSearching) return false;
	
	if(userAction == true)
		this.alert(this.txt.gameVerified);
	
	return !stopSearching;
	
}



// ------------------------------------------------
// vrati odkaz na element policka
sudoku.getInpt = function(x, y)
{
	return this.geid(this.cfg.inptPrefix + x + y);
}

// real boolean oznacuje, jestli se ma predat skutecna hodnota nebo upravena
sudoku.getInptVal = function(x, y, real)
{
	var inpt = this.getInpt(x, y);
	if(inpt == null)
		return null;
	
	//  pokud je vyplneno vice hodnot -> ignorovat
	return (real != true  &&  inpt.value.length != 1) ? null : inpt.value;
}

// nastavi hodnotu policka
sudoku.setInptVal = function(x, y, val)
{
	var inpt = this.getInpt(x, y);
	if(inpt == null)
		return false;
	
	inpt.value = val;
	
	if(inpt.readOnly)
		return;
	
	this.inptChangeStyle(x, y);
	return true;
}


// fce se spousti pri psani v policku a odfiltrovava chybne znaky
sudoku.inptTyping = function(inpt, x, y)
{
	if(this.memory.finished  ||  (event.keyCode < 48 || event.keyCode > 57)
		 && event.keyCode != 44 && event.keyCode != 13)
	{
		event.returnValue = false;
	}
	else
	{
		if(inpt.value.length >= 5)
		{
			event.returnValue = false;
			this.alert(this.txt.max3digits);
		}
			
		this.inptChangeStyle(x, y);
	}
}

// fce vsechny spatne znaky a graficky preformatuje
sudoku.inptFormat = function(inpt, x, y)
{
	if(inpt.readOnly)
		return;
	
	var val = this.getInptVal(x, y, true);
	if(val != null)
	{
		var newval = '';
		var countDigits = 0;
		for(var z = 0; z < val.length; z++)
		{
			if(isNaN(val.charAt(z)))
				continue;
			if(newval != '') newval += ',';
			newval += parseInt(val.charAt(z));
			countDigits++;
			if(countDigits >= 3) break;
		}
		val = newval;
	}
	
	this.setInptVal(x, y, val);
}

// fce podle poctu moznosti v policku zmeni barvu a velikost pisma
sudoku.inptChangeStyle = function(x, y)
{
	var val = this.getInptVal(x, y, true);
	var inpt = this.getInpt(x, y);
	
	inpt.className = inpt.className.replace(' s1', '').replace(' s2', '').replace(' s3', '');
	
	if(val == null)
	{
		inpt.className += ' s1';
		return true;
	}
	
	if(val.length <= 1)
		inpt.className += ' s1';
	else if(val.length <= 3)
		inpt.className += ' s2';
	else /*if(val.length <= 5)*/
		inpt.className += ' s3';
	
	return true;
}

// fce zvyrazni urcitou oblast (radku, sloupec, box 3x3)
sudoku.areaHighlight = function(highlight, area, coordX, coordY)
{
	clearTimeout(this.memory.intvl_1);
	
	switch(area)
	{
		case 'box':
			for(var y = 0; y < 3; y++)
			{
				// sloupce v boxu XY
				for(var x = 0; x < 3; x++)
				{
					var cx = (coordX * 3) + x;
					var cy = (coordY * 3) + y;
					this.inptHighlight(highlight, cx, cy);
				}
			}
		break;
		case 'row':
			for(var x = 0; x < 9; x++)
				this.inptHighlight(highlight, x, coordY);
		break;
		
		case 'column':
			for(var y = 0; y < 9; y++)
				this.inptHighlight(highlight, coordX, y);
		break;
		
		// vsechna policka
		default:
			for(var y = 0; y < 9; y++)
			{
				for(var x = 0; x < 9; x++)
					this.inptHighlight(highlight, x, y);
			}
		break;
	}
	
	// zapnout casovac na zhasnuti
	if(highlight == true)
	{
		this.memory.intvl_1 = setTimeout("sudoku.areaHighlight(false);", this.cfg.timeoutHighlight);
	}

}

// fce zvyrazni urcitou bunku
sudoku.inptHighlight = function(highlight, x, y)
{
	var elem = this.getInpt(x, y);
	if(elem == null)
		return false;
	
	elem.style.visibility = 'hidden';
	if(highlight == true)
		elem.className += ' highlight';
	else
		elem.className = elem.className.replace(' highlight', '');
	elem.style.visibility = 'visible';
	
	return true;
}

// fce zvyrazni urcity element
sudoku.elemHighlight = function(elem, highlight)
{
	if(elem == null)
		return false;
	
	elem.style.visibility = 'hidden';
	elem.className = elem.className.replace(' highlight', '');
	if(highlight == true)
		elem.className += ' highlight';
	else
		elem.className = elem.className.replace(' highlight', '');
	elem.style.visibility = 'visible';
	
	return true;
}

sudoku.alert = function(msg)
{
	var elem = this.geid('errorFrame');
	if(elem == null)
		alert(msg);
	else
	{
		if(msg != null)
		{
			elem.style.display = 'block';
			elem.innerHTML = msg;
		}
		else
			elem.style.display = 'none';
	}
}

sudoku.geid = function(oid)
{
	return document.getElementById(oid);
}





var timer = new Object();
timer.cfg = new Object();
timer.cfg.divShowTime = 'divTimer';
timer.timeStart = 0;
timer.timeEnd = 0;
timer.intvlRun = null;
timer.running = false;

timer.start = function()
{
	this.reset();
	this.running = true;
	this.timeStart = parseInt((new Date().getTime()) / 1000);
	this.intvlRun = setInterval("timer.iteration();", 1000);
	this.showTime();
	//alert(this.timeStart.getHours());
}

timer.stop = function()
{
	this.running = false;
	clearInterval(this.intvlRun);
}

timer.reset = function()
{
	this.stop();
	this.timeStart = new Date();
	this.timeEnd = 0;
}

timer.iteration = function()
{
	this.timeEnd++;
	//alert(this.timeEnd.getTime());this.stop();
	this.showTime();
}

timer.getTime = function()
{
	var timef = '';
	var hrs = (this.timeEnd >= 3600) ? (Math.floor(this.timeEnd / 3600)) : 0;
	var min = (this.timeEnd >= 60) ? (Math.floor((this.timeEnd % 3600) / 60)) : 0;
	var sec = (this.timeEnd >= 0) ? (Math.floor(this.timeEnd % 60)) : 0;
	if(hrs < 10) timef += '0';
	timef += hrs+':';
	if(min < 10) timef += '0';
	timef += min+':';
	if(sec < 10) timef += '0';
	timef += sec;
	
	return timef;
}

timer.showTime = function()
{
	var divel = document.getElementById(this.cfg.divShowTime);
	if(divel == null)
		return;
	
	var timef = this.getTime();

	divel.innerHTML = timef;
}
