
var dimension = 0;
var dim2 = 0;
var selected = -1;
var imagePath = "";
var imageType = "";
var images;
var imageAlts;
var cellArray;

function loadinit() {
	dimension = document.getElementById("sudoku_dimension").value;
	imagePath = document.getElementById("sudoku_image_path").value;
	imageType = document.getElementById("sudoku_image_type").value;
	images = document.getElementById("sudoku_images").value.split(";");
	imageAlts = document.getElementById("sudoku_image_alts").value.split(";");

	dim2 = dimension * dimension;

	LoadCellArray(dim2, document.getElementById("sudoku_data").value);
	buildMenuTable();
	buildGridTable();
}

function colToMenuTdId(col) {
	return "sudoku_menu_td_" + col;
}

function setAsSelected(col) {
	var selMenu;
	if (selected >= 0) {
		selMenu = document.getElementById(colToMenuTdId(selected));
		selMenu.className = "sudoku_menu_normal";
	}

	selected = col;
	selMenu = document.getElementById(colToMenuTdId(col));
	selMenu.className = "sudoku_menu_selected";
}


function buildMenuTable() {
	createTable("sudoku_menu_div", "sudoku_menu_table", "sudoku_menu", 1, dim2, GenMenuTd);
	setAsSelected(0);
}

function buildGridTable() {
	createTable("sudoku_grid_div", "sudoku_grid_table", "sudoku_grid", dim2, dim2, GenGridTd);
}

function setGridValue(row, col) {
	cellArray[row][col].CellClicked();
}


function gridTdID(row, col) {
	return "sudoku_grid_td_R" + row + "C" + col;
}

function gridAID(row, col) {
	return "sudoku_grid_a_R" + row + "C" + col;
}

function LoadCellArray(dim, data) {
	var idx;
	var row;
	var col;

	idx = 1;	// skip over first data element

	cellArray = new Array(dim);
	for (row = 0; row < dim; row++) {
	   cellArray[row] = new Array(dim);
	   for (col = 0; col < dim; col++) {
		   cellArray[row][col] = new SudokuCell(row, col, data.charAt(idx));
		   idx++;
	   }
	}
}

function ResetPuzzle() {
	var row;
	var col;
	for (row = 0; row < cellArray.length; row++) {
		for (col = 0; col < cellArray[row].length; col++) {
			cellArray[row][col].ResetCell();
		}
	}
}

// return number of empty cells
function CheckEmpty() {
	var row;
	var col;
	var emptyCount = 0;
	for (row = 0; row < cellArray.length; row++) {
		for (col = 0; col < cellArray[row].length; col++) {
			emptyCount += cellArray[row][col].FlagEmpty();
		}
	}
	return emptyCount;
}

function ClearError() {
	var row;
	var col;
	for (row = 0; row < cellArray.length; row++) {
		for (col = 0; col < cellArray[row].length; col++) {
			cellArray[row][col].ResetTdClass();
		}
	}
}

function CheckPuzzle() {
	var emptyCount = CheckEmpty();
	if (emptyCount != 0) {
		alert(emptyCount + " empty cells remain");
		ClearError();
	} else if (!CheckRows()) {
		alert("Duplicate entries in a row");
		ClearError();
	} else if (!CheckColumns()) {
		alert("Duplicate entries in a col");
		ClearError();
	} else if (!CheckBlocks()) {
		alert("Duplicate entries in a block");
		ClearError();
	} else if (!CheckValues()) {
		alert("Solution not unique??????");
		ClearError();
	} else {
		alert("You solved the puzzle. Congratuations.");
	}
}

function CheckRows() {
	var row;
	var ok = true;
	for (row = 0; ok && row < dim2; row++) {
		ok = CheckRow(row);
	}
	return ok;
}

function CheckRow(row) {
	var col;
	var ok = true;
	var vals = new Array(dim2);
	// initialize vals to all true meaning empty
	for (col = 0; col < dim2; col++) {
		vals[col] = true;
	}

	for (col = 0; ok && col < dim2; col++) {
		var testVal = cellArray[row][col].Shown();
		// assume testVal >= 0 && testVal < dim2
		// should we actually check this?
		if (vals[testVal]) {
			vals[testVal] = false;	// found a spot
		} else {
			var c;
			for (c = 0; c < dim2; c++) {
				if (cellArray[row][c].Shown() == testVal) {
					cellArray[row][c].FlagError();
				}
			}
			ok = false;
		}
	}
	// should we check to see if we found all the values?
	return ok;
}

function CheckColumns() {
	var col;
	var ok = true;
	for (col = 0; ok && col < dim2; col++) {
		ok = CheckColumn(col);
	}
	return ok;
}

function CheckColumn(column) {
	var row;
	var ok = true;
	var vals = new Array(dim2);
	// initialize vals to all true meaning empty
	for (row = 0; row < dim2; row++) {
		vals[row] = true;
	}

	for (row = 0; ok && row < dim2; row++) {
		var testVal = cellArray[row][column].Shown();
		// assume testVal >= 0 && testVal < dim2
		// should we actually check this?
		if (vals[testVal]) {
			vals[testVal] = false;	// found a spot
		} else {
			var r;
			for (r = 0; r < dim2; r++) {
				if (cellArray[r][column].Shown() == testVal) {
					cellArray[r][column].FlagError();
				}
			}
			ok = false;
		}
	}
	// should we check to see if we found all the values?
	return ok;
}

function CheckBlocks() {
	var row;
	var col;
	var ok = true;
	for (row = 0; ok && row < dimension; row++) {
		for (col = 0; ok && col < dimension; col++) {
			ok = CheckBlock(row * dimension, col * dimension);
		}
	}
	return ok
}

function CheckBlock(rowOffset, colOffset) {
	var row;
	var col;
	var idx;
	var ok = true;
	var vals = new Array(dim2);

	// initialize vals to all true meaning empty
	for (idx = 0; idx < dim2; idx++) {
		vals[idx] = true;
	}

	for (row = 0; ok && row < dimension; row++) {
		for (col = 0; ok && col < dimension; col++) {
			var testVal = cellArray[rowOffset + row][colOffset + col].Shown();

			if (vals[testVal]) {
				vals[testVal] = false;	// found a spot
			} else {
				var r;
				for (r = 0; r < dimension; r++) {
					var c;
					for (c = 0; c < dimension; c++) {
						if (cellArray[rowOffset + r][colOffset + c].Shown() == testVal) {
							cellArray[rowOffset + r][colOffset + c].FlagError();
						}
					}
				}
				ok = false;
			}
		}
	}
	return ok;
}

function CheckValues() {
	var row;
	var col;
	var ok = true;
	for (row = 0; ok && row < dim2; row++) {
		for (col = 0; ok && col < dim2; col++) {
			if (!cellArray[row][col].readonly && cellArray[row][col].code != cellArray[row][col].user) {
				cellArray[row][col].FlagError();
				ok = false;
			}
		}
	}
	return ok;
}


function GenMenuTd(row, col) {
	var td = document.createElement("td");
	td.setAttribute("id", "sudoku_menu_td_" + col);
	td.className = "sudoku_menu_normal";

	var a = document.createElement("a");
	a.setAttribute("href", "javascript:setAsSelected(" + col + ");");
	a.className = "sudoku_menu";

	var img = document.createElement("img");
	img.setAttribute("alt", imageAlts[col]);
	img.setAttribute("src", imagePath + images[col] + imageType);

	a.appendChild(img);
	td.appendChild(a);
	return td;
}


// constructor for the cell object
function SudokuCell(r, c, l) {
	this.row = r;
	this.col = c;
	this.ltr = l;
	var aStr = "A";
	var rawCode = l.charCodeAt(0) - (aStr.charCodeAt(0) + 10);
	if (rawCode < 0) {
		// this is a set cell
		this.code = -rawCode - 1;
		this.user = -1;
		this.readonly = true;
	} else {
		// user cell
		this.code = rawCode - 1;
		this.user = -1;
		this.readonly = false;
	}
}

SudokuCell.prototype.TdId = function () {
	return "sudoku_grid_td_R" + this.row + "C" + this.col;
}

SudokuCell.prototype.AId = function () {
	return "sudoku_grid_a_R" + this.row + "C" + this.col;
}

SudokuCell.prototype.CellClicked = function () {
	if (!this.readonly) {
		if (selected == this.user) {
			this.user = -1;
		} else {
			this.user = selected;
		}
		var a = document.getElementById(this.AId());
		a.replaceChild(this.GenAChild(), a.childNodes[0]);
	}
}

SudokuCell.prototype.ResetCell = function () {
	this.user = -1;
	var a = document.getElementById(this.AId());
	a.replaceChild(this.GenAChild(), a.childNodes[0]);
}

SudokuCell.prototype.CellClass = function () {
	// determine if white or gray
	// take the row and col and divide by dimension
	// this converts 0-8 to 0 to 2 (or 0-15 to 0-3) and so on
	// now add the divided row to the divided column
	// if the sum is odd (modulo 2) then it is white else it is gray
	var celltype = this.readonly ? "given" : "normal";

	if (((Math.floor(this.row / dimension) + Math.floor(this.col / dimension)) % 2) > 0) {
		return "sudoku_grid_td_" + celltype + "_w";
	} else {
		return "sudoku_grid_td_" + celltype + "_g";
	}
}

SudokuCell.prototype.Shown = function () {
	if (this.readonly) {
		return this.code;
	} else {
		return this.user;
	}
}

SudokuCell.prototype.GenAChild = function () {
	var idx = this.user >= 0 ? this.user : this.code;
	if (this.readonly || this.user >= 0) {
		var img = document.createElement("img");
		img.setAttribute("alt", imageAlts[idx]);
		img.setAttribute("src", imagePath + images[idx] + imageType);
		return img;
	} else {
		return document.createTextNode(" ");
	}
}

SudokuCell.prototype.TableTd = function () {
	var td = document.createElement("td");
	td.setAttribute("id", this.TdId());
	td.className = this.CellClass();

	var a = document.createElement("a");
	a.setAttribute("id", this.AId());
	a.setAttribute("href", "javascript:setGridValue(" + this.row + ", " + this.col + ");");
	a.className = "sudoku_grid";

	a.appendChild(this.GenAChild());
	td.appendChild(a);
	return td;
}

SudokuCell.prototype.ResetTdClass = function () {
	var td = document.getElementById(this.TdId());
	td.className = this.CellClass();
}

SudokuCell.prototype.FlagError = function () {
	var td = document.getElementById(this.TdId());
	td.className = "sudoku_grid_td_error";
}

SudokuCell.prototype.FlagEmpty = function () {
	var retVal = 0;
	if (!this.readonly && this.user < 0) {
		this.FlagError();
		retVal = 1;
	}
	return retVal;
}


function GenGridTd(row, col) {
	return cellArray[row][col].TableTd();
}


function createTable(parentId, tableId, tableClass, rows, columns, cellNode) {
	var div = document.getElementById(parentId);
	var tbl = document.createElement("table");
	tbl.setAttribute("id", tableId);
	tbl.className = tableClass;

	var tb = document.createElement("tbody");
	for (var row = 0; row < rows; row++)
	{
		tr = document.createElement("tr");

		for (var col = 0; col < columns; col++)
		{
			var td = cellNode(row, col);
			tr.appendChild(td);
		}
		tb.appendChild(tr);
	}
	tbl.appendChild(tb);
	div.appendChild(tbl);
}
