//
//
//
//  by Pinkhas Nisanov
//
//
//



start();


function start () {
  loadImg();

  var maindv = document.createElement( 'div' );
  setStyle( maindv, { "position":"absolute", "top": "110px", "left":"30px" } );
  document.body.appendChild( maindv );

  var field = new Array();
  for ( var ii=0; ii<9; ++ii ) {
    field[ii] = new Array();
    for ( var jj=0; jj<9; ++jj ) {
      field[ii][jj] = new Cell( ii, jj );
      maindv.appendChild( field[ii][jj].ge );
      setStyle( field[ii][jj].ge, { "top":ii*42+"px", "left":jj*42+"px" } );
    }
  }

  drBord( maindv );

  procFld( field );
  field[0][0].setSel();


  var btndv = document.createElement( 'div' );
  setStyle( btndv, { "position":"absolute", "top": "60px", "left":"30px" } );
  document.body.appendChild( btndv );
  putButton( btndv, 'Solve', function () { solveHndl( field ) } );
  putButton( btndv, 'Next Step', function () { solveHndl( field, 1 ); } );
  putButton( btndv, 'Example', function () { fillEx( field ) } );

}


function solveHndl ( f, one ) {
  if ( ! checkEmpty( f ) ) {
    alert( "Enter numbers into cells" );
    return;
  }

  calcPosVals( f );
  var onlRes = setOnly( f, one );
  if ( onlRes && one ) return 1;
  calcPosVals( f );
  var opRes = setOp( f, one );
  if ( opRes == 2 ) return 2;
  if ( opRes && one ) return 1;

  if ( ! onlRes && ! opRes ) {
    var tsRes = trySolve( f, one );
  }

  if ( allDone( f ) ) {
    return 1;
  }
  else {
    return solveHndl( f, one );
  }
}



function trySolve ( f, one ) {

  for ( var ii=0; ii<9; ++ii ) {
    for ( var jj=0; jj<9; ++jj ) {
      if ( ! f[ii][jj].val ) {
	var strVls = copyFl( f );
	var nArr = cpArr( f[ii][jj].posVals );
	for ( var x=0; x<nArr.length; ++x ) {
	  for ( var kk=0; kk<9; ++kk ) {
	    for ( var mm=0; mm<9; ++mm ) {
	      f[kk][mm].setVal( strVls[kk][mm] );
	    }
	  }
	  f[ii][jj].setVal( nArr[x] );
	  var res = solveHndl( f, one );

	  if ( res != 2 ) {
	    return;
	  }
	}
	return;
      }
    }
  }
}



function copyFl ( f ) {
  var cpAr = new Array();
  for ( var ii=0; ii<9; ++ii ) {
    cpAr[ii] = new Array();
    for ( var jj=0; jj<9; ++jj ) {
      cpAr[ii][jj] = f[ii][jj].val ? f[ii][jj].val : 0;
    }
  }

  return cpAr;
}


function setOnly ( f, one ) {
  for ( var ii=0; ii<9; ++ii ) {
    for ( var jj=0; jj<9; ++jj ) {
      if ( ! f[ii][jj].val ) {
	for ( var x=0; x<f[ii][jj].posVals.length; ++x ) {
	  var fl = [ procVert, procHor, procSq ];
	  for ( var fi=0; fi<3; ++fi ) {
	    var pFn = function () {
	      var hasOther = 0;
	      fl[fi]( f, f[ii][jj], function ( cEl ) {
			  if ( hasOther ) return;
			  if ( cEl.ii == ii && cEl.jj == jj ) return;
			  for ( var pi=0; pi<cEl.posVals.length; ++pi ) {
			    if ( f[ii][jj].posVals[x] == cEl.posVals[pi] ) {
			      hasOther = 1;
			      return;
			    }
			  }
			} );
	      if ( ! hasOther ) {
		f[ii][jj].setVal( f[ii][jj].posVals[x] );
		fi=4;
		return 1;
	      }
	      return 0;
	    };
	    if ( pFn() ) {
	      return 1;
	    }
	  }
	}
      }
    }
  }
  return 0;
}


function setOp ( f, one ) {
  for ( var ii=0; ii<9; ++ii ) {
    for ( var jj=0; jj<9; ++jj ) {
      if ( f[ii][jj].posVals.length < 2 ) {
	if ( ! f[ii][jj].val && f[ii][jj].posVals.length == 0 ) {
	  return 2;
	}
	if ( f[ii][jj].val ) {
	  if ( f[ii][jj].posVals[0] && f[ii][jj].val != f[ii][jj].posVals[0] ) {
	    return 2;
	  }
	}
	else {
	  f[ii][jj].setVal( f[ii][jj].posVals[0] );
	  return 1;
	}
      }
    }
  }

  return 0;
}

var currX;
var currY;
var prevElem;
function procFld ( f ) {
  currX = 0;
  currY = 0;
  prevElem = f[0][0];

  document.onkeydown = function (en) {
    var evt=(en)?en:(window.event)?window.event:null;
    var key=(evt.charCode)?evt.charCode: ((evt.keyCode)?evt.keyCode:((evt.which)?evt.which:0));

    if ( key == 37 ) {
      currY = currY - 1;
      if ( currY < 0 ) currY = 8;
    }
    else if ( key == 38 ) {
      currX = currX - 1;
      if ( currX < 0 ) currX = 8;
    }
    else if ( key == 39 ) {
      currY = currY + 1;
      if ( currY > 8 ) currY = 0;
    }
    else if ( key == 40 ) {
      currX = currX + 1;
      if ( currX > 8 ) currX = 0;
    }
    else if ( key > 47 && key < 58 ) {
      var keyNum = key - 48;
      f[currX][currY].setVal( keyNum );
    }
    else if ( key == 8 || key == 46 ) {
      f[currX][currY].setVal( 0 );
    }
    prevElem.setReg();
    f[currX][currY].setSel();
    prevElem = f[currX][currY];
  };

}





function Cell ( ii, jj ) {
  var oEl = this;
  oEl.ii = ii;
  oEl.jj = jj;
  var ge = document.createElement( 'div' );
  oEl.ge = ge;
  var elImg = new Image();
  elImg.src = "0.gif";
  ge.appendChild( elImg );
  setStyle( ge, { "width":"40px", "height":"40px", "position":"absolute", "background":"#eeeeee", "border":"solid 1px #999999" } );

  oEl.val = 0;
  oEl.posVals = new Array();

  oEl.setSel = function () { setStyle( ge, { "background":"#cccccc", "border":"solid 1px green" } ); }
  oEl.setReg = function () { setStyle( ge, { "background":"#eeeeee", "border":"solid 1px #999999" } ); }
  oEl.setVal = function ( nVal ) { oEl.val = nVal; elImg.src = nVal+".gif";};


  oEl.ge.onclick = function () {
    if ( currX == ii && currY == jj ) return;
    oEl.setSel();
    prevElem.setReg();
    currX = ii;
    currY = jj;
    prevElem = oEl;
  };
}




function calcPosVals ( f ) {
  for ( var ii=0; ii<9; ++ii ) {
    for ( var jj=0; jj<9; ++jj ) {
      if ( ! f[ii][jj].val ) {
	var fn = function () {
	  var posVals = new Array();
	  for ( var pi=1; pi<10; ++pi ) { posVals[pi] = 1; };
	  var fl = [ procVert, procHor, procSq ];
	  for ( var fi=0; fi<3; ++fi ) fl[fi]( f, f[ii][jj], function ( cEl ) { if ( cEl.val ) posVals[ cEl.val ] = 0; } );
	  f[ii][jj].posVals = new Array();
	  for ( var ti=1; ti<10; ++ti ) { if ( posVals[ti] == 1 ) f[ii][jj].posVals.push( ti ) };
	};
	fn();
      }
    }
  }
}



function allDone ( f ) {
  var done = 1;
  for ( var ii=0; ii<9; ++ii ) {
    for ( var jj=0; jj<9; ++jj ) {
      if ( ! f[ii][jj].val ) {
	done = 0;
      }
    }
  }
  return done;
}

function checkEmpty ( f ) {
  for ( var ii=0; ii<9; ++ii ) {
    for ( var jj=0; jj<9; ++jj ) {
      if ( f[ii][jj].val ) {
	return 1;
      }
    }
  }
  return 0;
}


function procVert ( f, cEl, fnHdl ) {
  var jj = cEl.jj;
  for ( var x=0; x<9; ++x ) {
    fnHdl( f[x][jj] );
  }
}

function procHor ( f, cEl, fnHdl ) {
  var ii = cEl.ii;
  for ( var x=0; x<9; ++x ) {
    fnHdl( f[ii][x] );
  }
}

function procSq ( f, cEl, fnHdl ) {
  var ii = cEl.ii;
  var jj = cEl.jj;

  var sidx = [
	      [0,1,2],
	      [0,1,2],
	      [0,1,2],
	      [3,4,5],
	      [3,4,5],
	      [3,4,5],
	      [6,7,8],
	      [6,7,8],
	      [6,7,8]
	      ];

  var vixs = sidx[jj];
  var hixs = sidx[ii];
  for ( var x = 0; x < 3; ++x ) {
    for ( var y = 0; y < 3; ++y ) {
      fnHdl( f[ hixs[x] ][ vixs[y] ] );
    }
  }
}




function loadImg () {
  for ( var x=0; x<10; ++x ) {
    var bfn = function () {
      var dImg = new Image();
      dImg.src = x+".gif";
      setStyle( dImg, { "display":"none" } );
      document.body.appendChild( dImg );
    };
    bfn();
  }
}


function fillEx ( f ) {

  // easy
//   f[0][1].setVal( 6 );
//   f[0][2].setVal( 3 );
//   f[0][6].setVal( 8 );
//   f[0][8].setVal( 7 );

//   f[1][0].setVal( 5 );
//   f[1][5].setVal( 3 );
//   f[1][7].setVal( 2 );
//   f[1][8].setVal( 6 );

//   f[2][1].setVal( 1 );
//   f[2][4].setVal( 7 );
//   f[2][7].setVal( 3 );

//   f[3][6].setVal( 5 );
//   f[3][7].setVal( 7 );
//   f[3][8].setVal( 3 );

//   f[4][3].setVal( 2 );
//   f[4][5].setVal( 5 );

//   f[5][0].setVal( 1 );
//   f[5][1].setVal( 3 );
//   f[5][2].setVal( 5 );

//   f[6][1].setVal( 5 );
//   f[6][4].setVal( 4 );
//   f[6][7].setVal( 8 );

//   f[7][0].setVal( 4 );
//   f[7][1].setVal( 7 );
//   f[7][3].setVal( 8 );
//   f[7][8].setVal( 1 );

//   f[8][0].setVal( 6 );
//   f[8][2].setVal( 2 );
//   f[8][6].setVal( 7 );
//   f[8][7].setVal( 5 );


  ///// very 
//   f[0][2].setVal( 6 );
//   f[0][6].setVal( 5 );

//   f[1][1].setVal( 1 );
//   f[1][3].setVal( 8 );
//   f[1][5].setVal( 4 );
//   f[1][7].setVal( 2 );

//   f[2][0].setVal( 8 );
//   f[2][8].setVal( 6 );

//   f[3][1].setVal( 7 );
//   f[3][4].setVal( 3 );
//   f[3][7].setVal( 4 );

//   f[4][3].setVal( 6 );
//   f[4][5].setVal( 5 );

//   f[5][1].setVal( 5 );
//   f[5][4].setVal( 1 );
//   f[5][7].setVal( 3 );

//   f[6][0].setVal( 9 );
//   f[6][8].setVal( 2 );

//   f[7][1].setVal( 6 );
//   f[7][3].setVal( 7 );
//   f[7][5].setVal( 1 );
//   f[7][7].setVal( 8 );

//   f[8][2].setVal( 4 );
//   f[8][6].setVal( 9 );


////////////////////////////////
  f[0][0].setVal( 8 );
  f[0][2].setVal( 1 );
  f[0][5].setVal( 4 );

  f[1][4].setVal( 6 );
  f[1][7].setVal( 5 );

  f[2][0].setVal( 3 );
  f[2][2].setVal( 2 );
  f[2][3].setVal( 7 );
  f[2][8].setVal( 6 );

  f[3][1].setVal( 9 );
  f[3][4].setVal( 4 );
  f[3][8].setVal( 7 );

  f[4][0].setVal( 2 );
  f[4][3].setVal( 3 );
  f[4][5].setVal( 9 );
  f[4][8].setVal( 5 );

  f[5][0].setVal( 1 );
  f[5][4].setVal( 5 );
  f[5][7].setVal( 2 );

  f[6][0].setVal( 6 );
  f[6][5].setVal( 7 );
  f[6][6].setVal( 4 );
  f[6][8].setVal( 2 );

  f[7][1].setVal( 7 );
  f[7][4].setVal( 8 );

  f[8][3].setVal( 6 );
  f[8][6].setVal( 3 );
  f[8][8].setVal( 9 );




}


function drBord ( maindv ) {
  for ( var ii=0; ii<4; ++ii ) {
    var ge = document.createElement( 'img' );
    ge.src = "black.png";
    var lft = ii*126-1;
    setStyle( ge, { "width":"2px", "height":"378px", "position":"absolute", "background":"black", "left":lft+"px", "top":"0px" } );
    maindv.appendChild( ge );
  }

  for ( var jj=0; jj<4; ++jj ) {
    var ge = document.createElement( 'img' );
    ge.src = "black.png";
    var tp = jj*126-1;
    setStyle( ge, { "width":"378px", "height":"2px", "position":"absolute", "background":"black", "left":0, "top":tp+"px" } );
    maindv.appendChild( ge );
  }
}


function putButton ( parent, text, clickHdl ) {
  var btn = window.document.createElement( "button" );
  var buttext = document.createTextNode( text );
  btn.appendChild( buttext );
  parent.appendChild( btn );
  btn.onclick = clickHdl;
}


function setStyle ( obj, stLs ) {
  var obj = arguments[ 0 ];
  for( var ii=1; ii<arguments.length; ++ii ) {
    var newDefs = arguments[ ii ];
    for( var jj in newDefs ) {
      obj.style[ jj ] = newDefs[ jj ];
    }
  }
}


function cpArr ( sArr ) {
  var nr = new Array();
  for ( var x=0; x<sArr.length; ++x ) {
    nr.push( sArr[x] );
  }

  return nr;
}






