// COPYRIGHT 2005 Orangeminds Software
// Written by Dominic Penfold
// Email us at myname@gmail.com if you'd like to discuss licensing.

function cellStateObject()
{
 this.mType = typeInitial;
 this.mValue = 0;
 this.mOptionCode = 0;
}

function cellIndexObject()
{
 this.mIndex = new Array(9);
}

var optionValue = new Array(9);
var cellState = new Array(81);
var regionIndex = new Array(9);
var rowIndex = new Array(9);
var columnIndex = new Array(9);
var maxBoards = 100;
var numBoards = 0;
var boards = new Array(maxBoards);
var typeInitial=1;
var typeUser=0;
var regionLookup = new Array(81);
var startTime;
var baseImage = new Array(9);
var userImage = new Array(9);
var highImage = new Array(9);

for (i=0; i<9; i++)
{
 baseImage[i] = new Image(44, 44);
 baseImage[i].src = 'base'+i+'.gif'
 userImage[i] = new Image(44, 44);
 userImage[i].src = 'user'+i+'.gif'
 highImage[i] = new Image(44, 44);
 highImage[i].src = 'high'+i+'.gif'
 optionValue[i] = (1<<i);
}

for (cell=0; cell<81; cell++)
{
 cellState[cell] = new cellStateObject();
 cellState[cell].mType = typeUser;
 cellState[cell].mValue = 0;
 for (option=0; option<9; option++)
 {
  cellState[cell].mOptionCode = 0; // all bits set true
 }
}

for (region=0; region<9; region++)
{
 regionIndex[region] = new cellIndexObject();
 regionIndex[region].mIndex[0] = region%3 * 3 + Math.floor(region/3) * 27;
 regionIndex[region].mIndex[1] = region%3 * 3 + Math.floor(region/3) * 27 + 1;
 regionIndex[region].mIndex[2] = region%3 * 3 + Math.floor(region/3) * 27 + 2;
 regionIndex[region].mIndex[3] = region%3 * 3 + Math.floor(region/3) * 27 + 9;
 regionIndex[region].mIndex[4] = region%3 * 3 + Math.floor(region/3) * 27 + 10;
 regionIndex[region].mIndex[5] = region%3 * 3 + Math.floor(region/3) * 27 + 11;
 regionIndex[region].mIndex[6] = region%3 * 3 + Math.floor(region/3) * 27 + 18;
 regionIndex[region].mIndex[7] = region%3 * 3 + Math.floor(region/3) * 27 + 19;
 regionIndex[region].mIndex[8] = region%3 * 3 + Math.floor(region/3) * 27 + 20;
}

for (row=0; row<9; row++)
{
 rowIndex[row] = new cellIndexObject();
 for (i=0; i<9; i++)
 {
  rowIndex[row].mIndex[i] = row*9 + i;
 }
}

for (col=0; col<9; col++)
{
 columnIndex[col] = new cellIndexObject();
 for (i=0; i<9; i++)
 {
  columnIndex[col].mIndex[i] = i*9 + col;
 }
}

function createTable()
{
 document.writeln('<TABLE class="board" border=0 cellspacing=0 cellpadding=0>');
 for (j=0; j<3; j++)
 {
  document.writeln('<TR>');
  document.writeln('<TD>');
  createRegion(j*27);
  document.writeln('</TD>');
  document.writeln('<TD>');
  createRegion(j*27+3);
  document.writeln('</TD>');
  document.writeln('<TD>');
  createRegion(j*27+6);
  document.writeln('</TD>');
  document.writeln('</TR>');
 }
  document.writeln('</TABLE>');
}

function createRegion(celloffset)
{
 document.writeln('<TABLE class="region" border=1 cellspacing=0 cellpadding=0>');
 for (i=0; i<3; i++)
 {
  document.writeln('<TR>');
  createCell(celloffset + i*9);
  createCell(celloffset + i*9 + 1);
  createCell(celloffset + i*9 + 2);
  document.writeln('</TR>');
 }
 document.writeln('</TABLE>');
}

/*******************************
 BEGIN Capture mouse position
 *******************************/
var IE = document.all?true:false
if (!IE) document.captureEvents(Event.MOUSEMOVE)
document.onmousemove = getMousePosition;
var mouseX, mouseY;

function getMousePosition(e)
{
 if (IE)
 {
    mouseX = event.clientX + document.body.scrollLeft
    mouseY = event.clientY + document.body.scrollTop
 }
 else
 {
    mouseX = e.pageX
    mouseY = e.pageY
    if (mouseX<0) mouseX = 0;
    if (mouseY<0) mouseY = 0;
 }
 return true;
}
/*******************************
 END Capture mouse position
 *******************************/

function getKeyPress(e)
{
 if (!e && window.event)
  e = window.event
 
 var keyChar;
 if(typeof(e.which)=='number')
 {
  keyChar = e.which;
 }
 else if(typeof(e.keyCode)=='number')
 {
  keyChar = e.keyCode;
 }
 
 if (selectionMenuActive)
 {
  if (keyChar>=48 && keyChar<=58)
  {
    keyChar-=48;
   hit(keyChar);
  }
 }
 else
 {
  if (currentCell!=-1)
  {
   if (keyChar>=49 && keyChar<=58)
   {
    keyChar-=48;
    if (((cellState[currentCell].mOptionCode)&(optionValue[keyChar-1]))==0)
     cellState[currentCell].mOptionCode |= optionValue[keyChar-1];
    else
     cellState[currentCell].mOptionCode &= ~optionValue[keyChar-1];
   }
   updateSingleCell(currentCell);
  }
 }
}
if (document.captureEvents)
 document.captureEvents(Event.KEYUP);
document.onkeyup = getKeyPress;

var currentCell=-1;

function cellMouseIn(cellIndex)
{
 currentCell = cellIndex;
 if (cellState[cellIndex].mType==typeUser)
 {
  eval('document.images.image'+cellIndex+'.src = "high"+cellState[cellIndex].mValue+".gif";');
  eval('document.sudoku.hint'+cellIndex+'.style.backgroundColor="#FFB949"');
 }
}

function cellMouseOut(cellIndex)
{
 if (currentCell==cellIndex)
 {
  currentCell = -1;
  if (cellState[cellIndex].mType==typeUser)
  {
   imageName = "user" + cellState[cellIndex].mValue + ".gif";
   eval('document.images.image'+cellIndex+'.src = imageName;');
   eval('document.sudoku.hint'+cellIndex+'.style.backgroundColor="#FFFFFF"');
  }
 }
}

var modifyIndex=1;

function clickedImage(cellIndex)
{
 if (cellState[cellIndex].mType==typeUser)
 {
  modifyIndex = cellIndex;
	moveSelectionMenu()
 }
}

function hit(num)
{
 cellState[modifyIndex].mValue = num;
 updateSingleCell(modifyIndex);
 removeSelectionMenu();
 if (getRemaining()==0)
 {
  if (isBoardValid())
  {
   var currentTime = new Date();
   var seconds = ((currentTime.getTime()-startTime.getTime())/1000);
   document.complete.submit();
  }
  else
  {
   alert("board not correct");
  }
 }
}

var selectionMenuActive=false;

function moveSelectionMenu()
{
 selectionMenuActive = true;
 var element = document.getElementById('selectionMenu');
 element.style.top=mouseY - 53;
 element.style.left=mouseX - 53;
}

function removeSelectionMenu()
{
 selectionMenuActive = false;
 var element = document.getElementById('selectionMenu');
 element.style.top=-200;
 element.style.left=-200;
}

function createCell(cellindex)
{
 document.write('<TD class="region"><IMG name="image'+cellindex+'" id="image'+cellindex+'" onmouseover="cellMouseIn('+cellindex+')" onmouseout="cellMouseOut('+cellindex+')" onClick="clickedImage('+cellindex+')" src="user0.gif" width=44 height=44 border=0>');
 document.write('<DIV id="hintlayer'+cellindex+'"style="position:absolute;">');
 createOptionTable(cellindex);
 document.writeln('</DIV></TD>');
}

function createOptionTable(cellindex)
{
 document.writeln('<input class="plain" onmouseover="cellMouseIn('+cellindex+')" onmouseout="cellMouseOut('+cellindex+')" onClick="this.blur();clickedImage('+cellindex+')" name="hint'+cellindex+'" type="text" size="5">');
}

function updateSingleCell(cell)
{
 var imageName;
 if (currentCell==cell && cellState[cell].mType==typeUser)
 {
  eval('document.images.image'+cell+'.src = "high"+cellState[cell].mValue+".gif";');
 }
 else
 {
  switch(cellState[cell].mType)
  {
  case typeUser :
   imageName = "user";
   break;
  case typeInitial :
   imageName = "base";
   break;
  default :
   alert("whoopsie");
  }
  imageName = imageName + cellState[cell].mValue + ".gif";
  eval('document.images.image'+cell+'.src = imageName;');
 }
 if (cellState[cell].mValue==0)
 {
  var hint = "";
  var option;
  for (option=0; option<9; option++)
  {
   if (((cellState[cell].mOptionCode)&(optionValue[option]))>0)
   {
    hint = hint + (option+1).toString();
   }
  }
  eval('document.sudoku.hint'+cell+'.value = hint;');
  var element = document.getElementById('hintlayer'+cell);
  var ypos = getAbsoluteY(document.getElementById('image'+cell));
  element.style.top=IE ? ypos+11 : ypos+15;
  var xpos = getAbsoluteX(document.getElementById('image'+cell));
  element.style.left=IE ? xpos+1 : xpos;
 }
 else
 {
  var element = document.getElementById('hintlayer'+cell);
  element.style.top=0;
  element.style.left=-800;
  eval('document.sudoku.hint'+cell+'.value = "";');
 }
}

function updateCellState()
{
 var cell;
 for (cell=0; cell<81; cell++)
 {
  updateSingleCell(cell);
 }
}

function getAbsoluteX(obj)
{
 var xPos = 0;
 if (obj.offsetParent)
 {
  while (obj.offsetParent)
  {
   xPos += obj.offsetLeft
   obj = obj.offsetParent;
  }
 }
 else if (obj.x)
  xPos += obj.x;
 return xPos;
}

function getAbsoluteY(obj)
{
 var yPos = 0;
 if (obj.offsetParent)
 {
  while (obj.offsetParent)
  {
   yPos += obj.offsetTop
   obj = obj.offsetParent;
  }
 }
 else if (obj.y)
  yPos += obj.y;
 return yPos;
}

function setState(idx, type, value)
{
 cellState[idx].mType = type;
 cellState[idx].mValue = value;
}

function initialiseState(statestring)
{
 for (cell=0; cell<81; cell++)
 {
  var value = statestring.charAt(cell);
  cellState[cell].mValue = value;
  cellState[cell].mType = value>0 ? typeInitial : typeUser;
 }
 startTime = new Date();
}

function addBoardLong(string)
{
 addBoard(string);
}

function addBoard(string)
{
 boards[numBoards++] = string;
}

function initialiseExample(boardstring)
{
 addBoardLong(boardstring);
 initialiseState(boards[numBoards-1]);
}

function getRow(cell)
{
 return Math.floor(cell/9);
}

function getColumn(cell)
{
 return cell%9;
}

function getRemaining()
{
 var numremaining=0;
 for (cell=0; cell<81; cell++)
 {
  if (cellState[cell].mValue==0)
  {
   numremaining++;
  }
 }
 return numremaining;
}

function resetBoard()
{
 var cell;
 for (cell=0; cell<81; cell++)
 {
  if (cellState[cell].mType==typeUser)
  {
   cellState[cell].mValue = 0;
   cellState[cell].mOptionCode = 0;
  }
 }
}

function isBoardValid()
{
 for (area=0; area<9; area++)
 {
  if (!isAreaValid(rowIndex[area].mIndex))
   return false;
  if (!isAreaValid(columnIndex[area].mIndex))
   return false;
  if (!isAreaValid(regionIndex[area].mIndex))
   return false;
 }

 return true;
}

function isAreaValid(areaIndex)
{
 var i, area;
 var areaCount = new Array(9);
 
 for (area=0; area<9; area++)
 {
  for (i=0; i<9; i++)
  {
   areaCount[i] = 0;
  }
  for (i=0; i<9; i++)
  {
   var cellIndex = areaIndex[i]
   var val = cellState[cellIndex].mValue;
   if (val>0)
   {
    areaCount[val-1]++;
    if (areaCount[val-1]>1)
    {
     return false;
    }
   }
  }
 }
 return true
}