/*
	Copyright 2008 Egmont Serieforlaget AS

	Developed by Escio AS - http://www.escio.no/
*/
function eSudoku(ePuzzle, puzzleId) {
	this.ePuzzle			= ePuzzle;

	this.eSudokuId			= puzzleId;
	this.baseUrl			= '';

	this.state				= 'UNINITIALIZED';
	this.stateMessage		= '';

	this.saveState			= 'SAVED';
	this.forceSave			= false;

	this.sudokuStart		= 0;
	this.sudokuTimer		= 0;

	this.selectedCell		= undefined;

	this.conflictCells		= new Array();

	this.initPainter = function() {
		this.painter.initSudokuElement();
	}

	this.getError = function() {
		if(this.sudoku.error) return this.sudoku.error;

		return false;
	}

	this.buildPuzzle = function() {
		this.ePuzzle.setPuzzleType(this.sudoku.type);
		this.ePuzzle.setPuzzleCodename(this.sudoku.codename);
		this.ePuzzle.setPuzzleLevel(this.sudoku.level);
		this.ePuzzle.setPuzzleStatus(this.sudoku.status);
		this.ePuzzle.setPuzzleTitle(this.sudoku.name);
		this.ePuzzle.setAuthor(this.sudoku.author);
		this.ePuzzle.setDateTs(this.sudoku.availableFromTs);

		this.painter.buildSudoku();

		this.addButtons();
		this.setAdditionalInfo();

		if(this.sudoku.startTs > 0) {
			this.ePuzzle.setStatus('Påbegynt: ' + tsToDate(this.sudoku.startTs));

			if(this.sudoku.storedData != undefined) this.loadStoredData(this.sudoku.storedData);
		}
	}

	this.setAdditionalInfo = function() {
		var additionalInfo = '';
		if (this.sudoku.userId > 0) {
			if(this.sudoku.actions.buy) {
				additionalInfo = 'Denne oppgaven er en konkurranseoppgave. Ved å kjøpe denne oppgaven får du mulighet til å delta i konkurransen.';
			} else {
				if(this.ePuzzle.puzzleContext == 'puzzle') {
					additionalInfo = 'Løs oppgaven for å samle poeng og delta i konkurranser.';
				} else {
					if(this.sudoku.actions.solve) {
						additionalInfo = 'Løs oppgaven for å samle poeng og delta i konkurransen.';
					} else {
						additionalInfo = 'Du må være medlem for å kunne løse denne oppgaven og delta i konkurransen.<br /><a class="register" href="/medlemskap/">Les mer om medlemskap her</a>';
					}
				}
			}
		} else {
			additionalInfo = 'Du må være registrert bruker for å kunne sende inn løsning på oppgaven.<br /><a class="register" href="/medlemskap/">Les mer om medlemskap her</a>';
		}

		if($('puzzleHelptext') && $('puzzleHelpLinktext') && $('puzzleHelptext').innerHTML != '') {
				additionalInfo += '<br /><br /><a onclick="openHelpText(); return false;">'+$('puzzleHelpLinktext').innerHTML+'</a>';
		}
		this.ePuzzle.setAdditionalInfo(additionalInfo);
	}

	this.puzzleBought = function() {
		this.ePuzzle.removeButtons();
		this.sudoku.actions.buy = false;
		this.setAdditionalInfo();
		this.addButtons();
	}

	this.addButtons = function() {
		if(this.sudoku.actions.buy) {
			this.ePuzzle.addButton(this.ePuzzle, 'Gå til kjøp av oppgaven', 'buyPuzzleButton', true);
		} else {
			this.ePuzzle.addButton(this, 'Send inn'		, 'submitButton'		, this.sudoku.actions.submit);
			this.ePuzzle.addButton(this, 'Lagre'		, 'saveButton'			, false);
			this.ePuzzle.addButton(this, 'Tøm'			, 'resetButton'			, true);
			this.ePuzzle.addButton(this, 'Se fasit'		, 'solveButton'			, this.sudoku.actions.solve);
			this.ePuzzle.addButton(this, 'Se tall'		, 'solveCellButton'		, false);
		}
	}

	this.setState = function(newState) {
		// Update internal state
		this.state = newState;

		// Tell painter-object to update
		// any visual elements affected
		// by state.
		this.painter.newState();
	}

	this.setSaveState = function(state) {
		if(state == 'SAVED') {
			if(this.saveState != 'SAVED') {
				// TODO: Oppdater lagringsstatus
			}

			this.saveState = 'SAVED';
		}

		if(state == 'CHANGED') {
			this.saveState = 'CHANGED';
		}
	}

	this.selectCell = function(targetElement) {
		this.painter.deSelectCell();
		this.selectedCell = targetElement;

		this.updateBoard();
		targetElement.focus();
	}

	this.updateBoard = function() {
		for(var i = 0; i < (this.sudoku.grid[0]*this.sudoku.grid[0]); i++) {
			if(this.conflictCells[i]) {
				this.painter.highLightCell(i);
			} else {
				this.painter.dimCell(i);
			}
		}

		this.painter.selectCell(this.conflictCells[this.getCurrentCellNumber()]);
	}

	this.goToCell = function(cellNumber) {
		var baseId = this.ePuzzle.puzzleContainer;

		var clue = $(baseId + '_cell_' + cellNumber + '_clue');

		if(clue) {
			return 'CELL_IS_CLUE';
		}

		var input = $(baseId + '_cell_' + cellNumber + '_input');
		if(input) {
			this.selectCell(input);
			return true;
		}

		return false;
	}

	this.goLeft = function() {
		if(this.selectedCell) {
			var increment = 1;
			while(this.goToCell(this.getCurrentCellNumber()-increment) == 'CELL_IS_CLUE') {
				increment++;
			}
		}
	}

	this.goRight = function() {
		if(this.selectedCell) {
			var increment = 1;
			while(this.goToCell(parseInt(this.getCurrentCellNumber())+increment) == 'CELL_IS_CLUE') {
				increment++;
			}
		}
	}

	this.goUp = function() {
		if(this.selectedCell) {
			var currentCell = parseInt(this.getCurrentCellNumber());
			var increment = 1;

			if(currentCell-parseInt(this.sudoku.grid[0]*increment) < 0) {
				currentCell = currentCell-1+(this.sudoku.grid[0]*this.sudoku.grid[1]);
			}

			while(this.goToCell(currentCell-parseInt(this.sudoku.grid[0]*increment)) == 'CELL_IS_CLUE') {
				increment++;
			}
		}
	}

	this.goDown = function() {
		if(this.selectedCell) {
			var currentCell = parseInt(this.getCurrentCellNumber());
			var increment = 1;

			if(currentCell+parseInt(this.sudoku.grid[0]*increment) > (this.sudoku.grid[0]*this.sudoku.grid[1])) {
				currentCell = currentCell+1-(this.sudoku.grid[0]*this.sudoku.grid[1]);
			}

			while(this.goToCell(currentCell+parseInt(this.sudoku.grid[0]*increment)) == 'CELL_IS_CLUE') {
				increment++;
			}
		}
	}

	this.checkBoard = function() {
		this.setSaveState('CHANGED');

		// Reset conflict cells
		this.conflictCells = new Array();

		for(var i = 0; i < (this.sudoku.grid[0]*this.sudoku.grid[0]); i++) {
			this.checkCell(i);
		}

		this.updateBoard();
	}

	this.checkCell = function(cellNumber) {
		var grid	= this.sudoku.grid;
		var cluster	= this.sudoku.cluster;

		var currentColumn	= cellNumber%grid[0];
		var currentRow		= Math.floor(cellNumber/grid[0]);
		var currentCluster	= Math.floor(currentColumn/cluster[0]) + (Math.floor(currentRow/cluster[1])*cluster[1]);

		var currentValue	= this.painter.getValue(cellNumber);

		if(currentValue > 0) {
			this.checkRow(cellNumber,currentRow,currentValue);
			this.checkColumn(cellNumber,currentColumn,currentValue);
			this.checkCluster(cellNumber,currentCluster,currentValue);
		}
	}

	this.checkRow = function(cellNumber,row,value) {
		for(var i = 0; i < this.sudoku.grid[1]; i++) {
			var checkCellNumber = (row*this.sudoku.grid[0])+i;

			if(checkCellNumber != cellNumber) {
				if(this.painter.getValue(checkCellNumber) == value) {
					this.conflictCells[cellNumber] = true;
					this.conflictCells[cellNumber] = true;
				}
			}
		}
	}

	this.checkColumn = function(cellNumber,column,value) {
		for(var i = 0; i < this.sudoku.grid[0]; i++) {
			var checkCellNumber = (column)+(i*this.sudoku.grid[1]);

			if(checkCellNumber != cellNumber) {
				if(this.painter.getValue(checkCellNumber) == value) {
					this.conflictCells[cellNumber] = true;
					this.conflictCells[cellNumber] = true;
				}
			}
		}
	}

	this.checkCluster = function(cellNumber,clusterNumber,value) {
		var grid	= this.sudoku.grid;
		var gridWidth = grid[0];
		var gridHeight = grid[1];

		var cluster	= this.sudoku.cluster;
		var clusterWidth = cluster[0];
		var clusterHeight = cluster[1];
		var clusterSize = clusterWidth * clusterHeight;

		var clustersAcross = grid[0]/clusterWidth;

		for(var i = 0; i < clusterSize; i++) {
			var checkCellNumber =
				(Math.floor(clusterNumber/clustersAcross)*gridWidth*clusterHeight)
				+((clusterNumber%clustersAcross)*clusterWidth)
				+(Math.floor(i/clusterWidth)*gridWidth)
				+(i%clusterHeight);

			if(checkCellNumber != cellNumber) {
				if(this.painter.getValue(checkCellNumber) == value) {
					this.conflictCells[cellNumber] = true;
					this.conflictCells[cellNumber] = true;
				}
			}
		}
	}

	this.getCurrentCellNumber = function() {
		var currentCell = this.selectedCell;

		if(!currentCell) return false;

		var currentCellId = currentCell.up().id;

		// Return end of string
		// Example: puzzle_1_cell_10 => 10
		var baseId = this.ePuzzle.puzzleContainer;

		return currentCellId.substring(baseId.length + 6);
	}

	this.loadStoredData = function(storedData) {
		for(var i = 0; i < this.sudoku.cells.length; i++) {
			if(this.sudoku.cells[i][3] != 'true') {
				this.painter.setValue(i,stringPart(storedData,i,1));
			}
		}

		this.setSaveState('SAVED');
	}

	this.checkStatus = function() {
		// Update SudokuTimer
		if(this.sudokuStart == 0) {
			if(this.sudoku.startTs) {
				this.sudokuStart = (this.sudoku.startTs*1000);
			} else {
				this.sudokuStart = new Date().getTime();
			}
		}
		this.sudokuTimer = new Date().getTime() - this.sudokuStart;

		var hours	= 0;
		var minutes = 0;
		var timeString = '';

		var totalTime = Math.floor(this.sudokuTimer/1000);
		if(totalTime > 3600) {
			hours = Math.floor(totalTime/3600);
			totalTime = totalTime - (hours*3600);
			if(hours > 1) {
				timeString = hours + ' timer ';
			} else {
				timeString = hours + ' time ';
			}
		}

		if(totalTime > 60) {
			minutes = Math.floor(totalTime/60);
			totalTime = totalTime - (minutes*60);
			timeString += minutes + ' min ';
		}

		timeString += totalTime + ' sek';

		if(((totalTime%60) == 0) || (this.forceSave)) {
			this.forceSave = false;
			this.sudokuCheckSum = '';
			for(var i = 0; i < (this.sudoku.grid[0]*this.sudoku.grid[0]); i++) {
				var cellValue = parseInt(this.painter.getValue(i));
				if((cellValue >= 1) && (cellValue <= 9)) {
					this.sudokuCheckSum += this.painter.getValue(i);
				} else {
					this.sudokuCheckSum += ' ';
				}
			}

			URL = this.baseUrl+puzzleLoader.puzzleIdentifier+'&apimode=statusUpdate';
			postBody = 'sVer=3.0&sudokuCheckSum=' + this.sudokuCheckSum;

			if(URL != '') {
				this.setSaveState('SAVED');
				new Ajax.Request(URL, {
					method: 'POST',
					postBody: postBody,
					onSuccess: function(response) {
						try {
							result = JSON.parse(response.responseText);
						} catch(e) {
							switch(e.name) {
								case 'TypeError':
									puzzleLoader.puzzle.errorMessage = e.message;
								break;

								default:
									puzzleLoader.puzzle.errorMessage = 'Feil. ' + e.name + ' ' + e.message;
							}

							puzzleLoader.puzzle.setState('ERROR');
						}
					}
				});
			}
		}

		puzzleLoader.puzzle.stateMessage = 'Tidsforbruk: ' + timeString;
		puzzleLoader.puzzle.stateMessage = '';
		puzzleLoader.puzzle.setState('IDLE');
	}

	this.performRequest = function(URL, postBody) {
		if(URL != '') {
			new Ajax.Request(URL, {
				method: 'POST',
				postBody: postBody,
				onSuccess: function(response) {
					var eSudoku = puzzleLoader.puzzle; 
	
					try {
						result = JSON.parse(response.responseText);
	
						if(result.result == 'solution') {
							eSudoku.puzzleMode = 'SOLVED';
							eSudoku.sudoku.endTs = result.endTs;
							eSudoku.sudoku.score = result.newScore;
	
							result.solution.each(function(value, index) {
								eSudoku.painter.setValue(index, value);
							});
						}
	
						if(result.result == 'submitted') {
							eSudoku.puzzleMode = 'SOLVED';
							eSudoku.sudoku.endTs = result.endTs;
							eSudoku.sudoku.score = result.score;
	
							openPuzzleResult(result.score);
						}
	
						if(result.result == 'cell') {
							var cell		= result.cell;
							var cellNumber	= parseInt(result.cId);
	
							eSudoku.painter.setValue(cellNumber, cell[2]);
							eSudoku.checkBoard();
						}
					} catch(JSONError) {
						switch(JSONError.name) {
							case 'TypeError':
								puzzleLoader.puzzle.errorMessage = JSONError.message;
							break;
	
							default:
								puzzleLoader.puzzle.errorMessage = 'Feil. ' + JSONError.name + ' ' + JSONError.message;
						}
	
						puzzleLoader.puzzle.setState('ERROR');
					}
				}
			});
		} else {
			alert('Ingen url');
		}
	}
	
	this.controlListener = function(event) {
		var targetElement = Event.element(event);

		switch(event.type) {
			case 'click':
				var URL = '';
				var postBody = '';

				sudokuCheckSum = '';
				for(var i = 0; i < (this.sudoku.grid[0]*this.sudoku.grid[0]); i++) {
					sudokuCheckSum += this.painter.getValue(i);
					sudokuCheckSum = hex_md5(sudokuCheckSum);
				}

				if(stringPart(targetElement.id,-12) == 'submitButton') {
					this.ePuzzle.confirm("Er du sikker på at du vil sende inn din besvarelse?\n", function(confirmed) {
						if(!confirmed) return;

						URL = puzzleLoader.puzzle.baseUrl+puzzleLoader.puzzleIdentifier+'&apimode=submitPuzzle';
						postBody = 'sVer=3.0&sudokuCheckSum=' + sudokuCheckSum;

						puzzleLoader.puzzle.performRequest(URL,postBody);
					});
				}

				if(stringPart(targetElement.id,-10) == 'saveButton') {
					this.forceSave = true;
					return;
				}

				if(stringPart(targetElement.id,-11) == 'resetButton') {
					this.sudoku.cells.each(function(value, index) {
						if(value[3] !== 'true') puzzleLoader.puzzleDisplay.setValue(index, '');
					});

					this.checkBoard();
					return;
				}

				if(stringPart(targetElement.id,-11) == 'solveButton') {
					this.ePuzzle.confirm("Er du sikker på at du vil se fasiten?\n\nDersom du velger å vise fasiten vil du ikke\nlenger kunne få poeng for oppgaven.", function(confirmed) {
						if(!confirmed) return;

						URL = puzzleLoader.puzzle.baseUrl+puzzleLoader.puzzleIdentifier+'&apimode=solvePuzzle';
						postBody = 'sVer=3.0&solveMode=puzzle&sudokuCheckSum=' + sudokuCheckSum;

						puzzleLoader.puzzle.performRequest(URL,postBody);
					});
				}

				if(stringPart(targetElement.id,-15) == 'solveCellButton') {
					this.ePuzzle.confirm("Er du sikker på at du vil se fasit for dette tallet?\n\nDersom du velger å vise fasiten vil du miste 1 poeng.", function(confirmed) {
						if(!confirmed) return;

						var baseId = puzzleLoader.ePuzzle.puzzleContainer;

						var cellNumber = stringPart(puzzleLoader.puzzle.selectedCell.id , (baseId+'_cell_').length, puzzleLoader.puzzle.selectedCell.id.length-6-(baseId+'_cell_').length);
						var value = puzzleLoader.puzzleDisplay.getValue(cellNumber);

						URL = puzzleLoader.puzzle.baseUrl+puzzleLoader.puzzleIdentifier+'&apimode=solvePuzzle';
						postBody = 'sVer=3.0&solveMode=cell&cId=' + cellNumber + '&cV=' + value;

						puzzleLoader.puzzle.performRequest(URL,postBody);
					});
				}

			break;
		}
	}

	this.eventListener = function(event) {
		var targetElement = Event.element(event);

		switch(event.type) {
			case 'click':
				this.selectCell(targetElement);
			break;

			case 'keydown':
			case 'keypress':
				switch(event.keyCode) {
					case Event.KEY_LEFT:
					case Event.KEY_RIGHT:
					case Event.KEY_UP:
					case Event.KEY_DOWN:
					break;

					case Event.KEY_BACKSPACE:
					case Event.KEY_DELETE:
					case Event.KEY_RETURN:
					case Event.KEY_TAB:
					break;

					default:
						var clusterSize = (this.sudoku.cluster[0]*this.sudoku.cluster[1]);
						var charCode = event.charCode ? event.charCode : event.keyCode;

						// Hack to address IE numeric
						// keypad values
						if(charCode >= 96 && charCode <= 105) {
							charCode -= 48;
						}

						var number	 = String.fromCharCode(charCode);

						if((number >= 1) && (number <= clusterSize)) {
							targetElement.value = number;

							this.checkBoard();
						} else {
							targetElement.value = '';
						}

						Event.stop(event);
					break;
				}
			break;

			case 'keyup':
				switch(event.keyCode) {
					case Event.KEY_RIGHT:
						this.goRight();
					break;

					case Event.KEY_LEFT:
						this.goLeft();
					break;

					case Event.KEY_DOWN:
						this.goDown();
					break;

					case Event.KEY_UP:
						this.goUp();
					break;

					case Event.KEY_TAB:
					case Event.KEY_RETURN:
					break;

					case Event.KEY_DELETE:
					case Event.KEY_BACKSPACE:
						this.selectedCell.value = '';
						this.checkBoard();
					break;

					default:
						Event.stop(event);
					break;
				}
			break;
		}
	}
}