// V12.02.2007

var _apnltimeout = 120000;

var BOXWIDTH = 3 ;
var GRIDWIDTH = BOXWIDTH * BOXWIDTH ;
var NUMCELLS = GRIDWIDTH  * GRIDWIDTH ; 
var clr_invalid = '#ff0000' ;
var clr_valid = '#000000';
var clr_cell  = '#666666'; 
var clr_cell_selected = 'blue';
var colors = new Array("#fffff0", "#00ff00", "#ff9900", "#ff0000", "#0000ff", 
"#ff9966", "#99ccff", "#66ff66", "#ffff66");

var _show_candidates = 0 ;
var _editable = 0 ;
var _givens_editable = 0 ;
var _mark_invalid_cells = 0 ;


var cells = new Array(NUMCELLS) ;
var candidates = new Array(NUMCELLS);
var candidates_removed = new Array(NUMCELLS);

var ds_rm = new Array(GRIDWIDTH);
var ds_cm = new Array(GRIDWIDTH);
var ds_bm = new Array(GRIDWIDTH);
var edit_candidates = -1;
var active_cell = -1 ;
var htext = 0 ;
var apnl_request = 0;


//-------------------------------------------

function sC(event) 
{
  if(!_editable)
    return 1;

  var d = 0 ;
  if(event && event.keyCode) { d = event.keyCode ; }
  if(!d && event && event.which) { d = event.which ;  }
  if(!d && window.event) { d = window.event.keyCode;  }
  if(!d) { return 1; }  


  if(d>=37 && d<=40)
    return false ;

  if(active_cell == -1)
    return true ;

  if(_show_candidates && d == 99 && grid[active_cell]==0) {
    edit_candidates = active_cell ;
    return false;
  }

  d-=48;    
  if(d == grid[active_cell]) 
    return false;

  if(cur_hint) { 
    hd2p=0;
    remove_hints(); 
  }

  if(givens[active_cell] && !_givens_editable) {
    return false ; }
  
  var odg = grid[active_cell];
  if(d>=1 && d<=9) 
  {
    if(edit_candidates == active_cell) {
      candidates_removed[active_cell] ^= (1<<(d-1));
      edit_candidates = -1 ;
    }
    else {
      grid[active_cell] = d ;
      cells[active_cell].innerHTML = (_mark_invalid_cells && solution[active_cell] != d) ? '<span class="idigit">'+d+'</span>' : "<span>"+d+"</span>";
    }

    calc_candidates();

    if(event || (event=window.event)) {
      event.returnValue=false;
      event.cancelBubble=true;
      if(event.preventDefault){  event.preventDefault() ;}
      if(event.stopPropagation){ event.stopPropagation();}
    }
    check_solved();
  }
  else if(grid[active_cell]){
    grid[active_cell] = 0 ;
    calc_candidates();
  }

  num_hints=0;

  return false ;
}

function saC(i) {
  if(!_editable)
    return 1;  

  if(active_cell == i) { return 0; }

  edit_candidates = -1 ;
  if(active_cell != -1) { cells[active_cell].style.borderColor = colors[0] ; cells[active_cell].blur(); }
  if((active_cell = i)==-1) { return 0; }
  if(givens[i] && !_givens_editable) { return 0; }

  cells[active_cell].style.borderColor = clr_cell_selected ;
  cells[active_cell].focus();
}

function uhl(c) {
  if(c) { c.style.borderColor = colors[0] ; }
}

function mC(event) {
  if(!_editable)
    return 1;

  var rinc = 0 ;
  var cinc = 0 ;
  
  var key = 0 ;
  if(event && event.keyCode) { key = event.keyCode ; }
  if(!key && event && event.which) { key = event.which ;  }
  if(!key && window.event) { key = window.event.keyCode;  }
  if(!key) { return 1; }  
  
  switch(key) {
    case 37: cinc=-1; break ;
    case 39: cinc= 1; break ;
    case 38: rinc=-1; break ;
    case 40: rinc= 1; break ;

    case 8:
      return sC(event);

    default:
    return 1;
    break ;
  }
  
  if(rinc || cinc)
  {
    if(active_cell != -1) {
    var r = parseInt(active_cell/GRIDWIDTH);
    var c = active_cell%GRIDWIDTH;
    for(var n=GRIDWIDTH-1; n>0; n--) 
    {
      r = (GRIDWIDTH + r + rinc)%GRIDWIDTH;
      c = (GRIDWIDTH + c + cinc)%GRIDWIDTH;

      var i = r*GRIDWIDTH+c;
      if(_givens_editable || givens[i]==0) 
      {
        saC(i);
        break ;
      }
    }}

    if(event || (event=window.event)) {
      event.returnValue=false;
      event.cancelBubble=true;
      if(event.preventDefault){  event.preventDefault() ;}
      if(event.stopPropagation){ event.stopPropagation();}
    }

    return false;
  }

  return 1;
}

function calc_candidates() 
{
  for(var j=0; j<GRIDWIDTH; j++) {
    ds_rm[j] = 0 ;
    ds_cm[j] = 0 ;
    ds_bm[j] = 0 ;
  }

  var i=0;
  for(var y=0; y<GRIDWIDTH; y++) 
  {
    var by = parseInt(y/BOXWIDTH)*BOXWIDTH ;
    for(var x=0; x<GRIDWIDTH; x++) 
    {
      var v = grid[i];
      if(v) {
        var n = 1<<(v - 1);
        var b = by + parseInt(x/BOXWIDTH);
        ds_rm[y] |= n;
        ds_cm[x] |= n;
        ds_bm[b] |= n;
      }

      i++;
  }}

  var m = (1<<GRIDWIDTH)-1;
  i=0;
  for(var y=0; y<GRIDWIDTH; y++) 
  {
    var by = parseInt(y/BOXWIDTH)*BOXWIDTH ;
    for(var x=0; x<GRIDWIDTH; x++) 
    {
      var b = by + parseInt(x/BOXWIDTH);
      var ca = !_show_candidates || grid[i] ? 0 : (~ds_rm[y]) & (~ds_cm[x]) & (~ds_bm[b]) & m & ~candidates_removed[i] ;    

      if(1){//ca != candidates[i]) {
        candidates[i] = ca ;
        if(grid[i]==0) {
          cells[i].innerHTML = cdT(ca,candidates_removed[i],0,0,0);
        }
      }

      i++;
  }}
}


function apnl_timer()
{
  var pnl = document.getElementById('apnl');
  if(!pnl)
    return ;
  
  round++;
  if(round>50)
    return ;

  if(!apnl_request) {
    try { apnl_request = new XMLHttpRequest(); } 
    catch(foo) {
      try { apnl_request = new ActiveXObject("Msxml2.XMLHTTP"); } 
      catch(bar) {
        try { apnl_request = new ActiveXObject("Microsoft.XMLHTTP"); } 
        catch(foobar) { apnl_request = false; }
      }
    }
  }

  if(!apnl_request)
    return ;

  apnl_request.open("GET", "/apnl.asp?round="+round, true);
  apnl_request.onreadystatechange = apnl_update;
  apnl_request.send(null);
  
  return ;
}

function apnl_update()
{
  if(!apnl_request || apnl_request.readyState != 4 || apnl_request.status != 200) 
    return ;

  var tbox = document.getElementById("apnl");
  if(!tbox)
    return ;

  tbox.innerHTML = apnl_request.responseText;
}




function init_grid(ea,mic,gea)
{
  _editable = ea ;
  _mark_invalid_cells = mic ;
  _givens_editable = gea ;
  htext = document.getElementById("hint"); 

  for(var i=0; i<NUMCELLS; i++) {
    cells[i] =document.getElementById("c"+i);
    candidates_removed[i] = 0 ;
  }

  if(_show_candidates) {
    calc_candidates() ;
  }

  mark_invalid_cells();

  window.setInterval('apnl_timer();',_apnltimeout);
}

function hlcell(i,h1,h2)
{
  cells[i].style.borderColor = colors[h2];
}

function hlrow(r, h1,h2,lbl)
{
  h1 = colors[h1];
  h2 = colors[h2];

  if(lbl) {
    var label = document.getElementById("rlabel"+r);
    if(label) { label.style.backgroundColor = colors[lbl]; }
  }

  r--;
  for(var c=0; c<GRIDWIDTH; c++) {
    if(grid[r*GRIDWIDTH+c]) {
      cells[r*GRIDWIDTH+c].style.borderColor = h1;
    }
    else {
      cells[r*GRIDWIDTH+c].style.borderColor = h2;
    }
  }
}

function hlcol(c, h1,h2,lbl)
{
  h1 = colors[h1];
  h2 = colors[h2];

  if(lbl) {
    var label = document.getElementById("clabel"+c);
    if(label) { label.style.backgroundColor = colors[lbl]; }
  }

  c--;
  for(var r=0; r<GRIDWIDTH; r++) {
    if(grid[r*GRIDWIDTH+c]) {
      cells[r*GRIDWIDTH+c].style.borderColor = h1;
    }
    else {
      cells[r*GRIDWIDTH+c].style.borderColor = h2;
    }
  }
}

function hlbox(b, h1, h2)
{
  b--;
  var rb = parseInt(b/BOXWIDTH)*BOXWIDTH ;
  var cb = (b%BOXWIDTH)*BOXWIDTH ;
  var rm = rb + BOXWIDTH ;
  var cm = cb + BOXWIDTH ;
  

  for(var r=rb; r<rm; r++) {
  for(var c=cb; c<cm; c++) {
    if(grid[r*GRIDWIDTH+c]) {
      cells[r*GRIDWIDTH+c].style.borderColor = colors[h1];
    }
    else {
      cells[r*GRIDWIDTH+c].style.borderColor = colors[h2];
    }
  }}
}

function hlcol_ch(c, h1, d, h2)
{
  c--;
  for(var r=0; r<GRIDWIDTH; r++) {
    if(!grid[r*GRIDWIDTH+c]) {
      for(var cc=0; cc<GRIDWIDTH; cc++) {
        if(cc != c && grid[r*GRIDWIDTH+cc] == d) {
          cells[r*GRIDWIDTH+cc].style.borderColor = colors[h2];
          break;
        }
      }
      cells[r*GRIDWIDTH+c].style.borderColor = colors[h2];
    }
    else {
      cells[r*GRIDWIDTH+c].style.borderColor = colors[h1];
    }
  }
}

function hlrow_ch(r, h1, d, h2)
{
  r--;
  for(var c=0; c<GRIDWIDTH; c++) {
    if(!cells[r*GRIDWIDTH+c].value) {
      for(var rr=0; rr<GRIDWIDTH; rr++) {
        if(rr != r && cells[rr*GRIDWIDTH+c].value == d) {
          cells[rr*GRIDWIDTH+c].style.borderColor = colors[h2];
          break;
        }
      }
      cells[r*GRIDWIDTH+c].style.borderColor = colors[h2];
    }
    else {
      cells[r*GRIDWIDTH+c].style.borderColor = colors[h1];
    }
  }
}

function hlca(i,hl,name,border) 
{
  var T = ""
  var f = 1 ;
  var d = 1 ;
  var ca = candidates[i];

  if(name) {
    T+="<div class=\"n"+name+"\">"+name+"</div>";
  }

  T += "<table class=\"cd\">";
  for(var r=0; r<BOXWIDTH; r++) {
    T += "<tr>";
    for(var c=0; c<BOXWIDTH; c++) {
      T += "<td>";
      if(ca&f) {
        T += hl[d]? "<span class=\"c"+hl[d]+"\">"+d+"</span>" : d;
      }

      T += "</td>";
      f+=f ;
      d++;
    }

    T += "</tr>";
  }
  
 T+="</table>";
 cells[i].innerHTML = T;

 if(border) {
  cells[i].style.borderColor = colors[border];
 }
 
// candidates[i] = 0 ;
}

function hlns(rs,cs,border)
{
  border = colors[border];
  var bd = 0 ;
  var i = 0;
  var rb = parseInt(rs/BOXWIDTH)*BOXWIDTH ;
  var cb = parseInt(cs/BOXWIDTH)*BOXWIDTH
  var rm = rb + BOXWIDTH ;
  var cm = cb + BOXWIDTH ;
  for(var r=rb; r<rm; r++) {
  for(var c=cb; c<cm; c++) {
    i = r*GRIDWIDTH+c;
    if(grid[i] && !(bd&(1<<(grid[i]-1)))) {
      cells[i].style.borderColor = border;
      bd|=1<<(grid[i]-1);
    }
  }}  

  i = rs * GRIDWIDTH;
  for(var c=0; c<GRIDWIDTH; c++) {
    if(grid[i] && !(bd&(1<<(grid[i]-1)))) {
      cells[i].style.borderColor = border;
      bd|=1<<(grid[i]-1);
    }
    i++;
  }

  i = cs ;
  for(var r=0; r<GRIDWIDTH; r++) {
    if(grid[i] && !(bd&(1<<(grid[i]-1)))) {
      cells[i].style.borderColor = border;
      bd|=1<<(grid[i]-1);
    }

    i+=GRIDWIDTH;
  }
}

function enable_candidates(show)
{
  _show_candidates = show ;
  calc_candidates() ;

  var cb = document.getElementById("cb_candidates");
  var howto = document.getElementById("cedit_howto");
  if(cb) { cb.checked = show ? 1 : 0 ; }
  if(howto) { howto.style.display = show ? "block" : "none"; }
}

function toggle_candidates() {
  enable_candidates(!_show_candidates) ;
}

function remove_hints()
{ 
  var hc = colors[0] ;
  for(var i=0; i<NUMCELLS; i++) {
    if(cells[i].style.borderColor != hc || cells[i].style.backgroundColor != hc)
    {
      cells[i].style.borderColor = hc ;
      cells[i].style.backgroundColor = hc ;
    }
  }

  for(var j=1; j<=GRIDWIDTH; j++) {
    var rlabel = document.getElementById("rlabel"+j);
    if(rlabel && rlabel.style.backgroundColor) { rlabel.style.backgroundColor = ""; }
    var clabel = document.getElementById("clabel"+j);
    if(clabel && clabel.style.backgroundColor) { clabel.style.backgroundColor = ""; }
  }

  htext.innerHTML = "";

  if(active_cell != -1)
    cells[active_cell].style.borderColor = clr_cell_selected ;
}

function rmc(i,crm,hl,clrs)
{
  var ca = candidates[i];
  var r = ca & crm;


  if(r || clrs) {
    cells[i].innerHTML = cdT(ca,candidates_removed[i],r,"dgr",0,clrs);

    if(r){
      candidates_removed[i] |= crm ;
    }
  }

}

function check_solved()
{
  for(var i=0; i<NUMCELLS; i++) { 
    if(grid[i] != solution[i]) {
      return 0 ;
    }
  }

  for(var i=0; i<NUMCELLS; i++) { 
    cells[i].style.borderColor = "#00ff00";
  }

  var tmnow = unixtime(); 
  var minutes = parseInt((tmnow-tmstarted)/60);
  var seconds = (tmnow-tmstarted)%60;
  if(seconds <10) {seconds = "0"+seconds;}

  //htext.innerHTML = "<h3>Well done!</h3><p>Your Time: "+minutes+"."+seconds+" minutes</p>";
  htext.innerHTML = "<h3>Well done!</h3>";
  return 1;
}

function fetch_state1(sb,hm)
{
  var st="";
  var cr="";
  var empty=0;
  var filled=0;

  for(var i=0; i<NUMCELLS; i++) { 
    if(grid[i]) {
      filled++;
      st += grid[i].toString(10) ;
    }
    else {
      empty++;
      st += "0";
    }
  }

  if(empty>0 && filled>0) {
    var f = sb.form ;
    
    f.state1.value = st; 
    if(f.hintmode) { f.hintmode.value = hm; }
    if(f.candidates) { f.candidates.value = _show_candidates; }
    if(f.invalidcells) { f.invalidcells.value = _mark_invalid_cells; }
    
    f.submit();
    return 1;
  }

  return 0 ;
}

function show_solution() {
  for(var i=0; i<NUMCELLS; i++) { 
    if(!grid[i]) {
      cells[i].innerHTML = '<span>'+solution[i]+'</span>';
      grid[i] = solution[i];
    }
  }

  remove_hints();
  num_hints=0;
  _editable = 0;

  var btnep = document.getElementById("btnep");
  if(btnep) {
    btnep.value="Edit puzzle";
  }
}

function mark_cells2() {
  for(var j=0; j<GRIDWIDTH; j++) {
    ds_rm[j] = 0 ;
    ds_cm[j] = 0 ;
    ds_bm[j] = 0 ;
  }

  var i=0;
  for(var y=0; y<GRIDWIDTH; y++) 
  {
    var by = parseInt(y/BOXWIDTH)*BOXWIDTH ;
    for(var x=0; x<GRIDWIDTH; x++) 
    {
      var v = grid[i];
      if(v) {
        var n = 1<<(v - 1);
        var b = by + parseInt(x/BOXWIDTH);

        if(ds_rm[y]&n) {
          var cv = "<span class=\"idigit\">"+v+"</span>";
          for(var k=0; k<GRIDWIDTH; k++) { if(grid[y*GRIDWIDTH+k] == v) { cells[y*GRIDWIDTH+k].innerHTML = cv; } }
        }

        if(ds_cm[x]&n) {
          var cv = "<span class=\"idigit\">"+v+"</span>";
          for(var k=0; k<GRIDWIDTH; k++) { if(grid[k*GRIDWIDTH+x] == v) { cells[k*GRIDWIDTH+x].innerHTML = cv; } }
        }

        if(ds_bm[b]&n) {
          var cv = "<span class=\"idigit\">"+v+"</span>";
          var rb = parseInt(b/BOXWIDTH)*BOXWIDTH ;
          var cb = (b%BOXWIDTH)*BOXWIDTH ;
          var rm = rb + BOXWIDTH ;
          var cm = cb + BOXWIDTH ;
          for(var r=rb; r<rm; r++) {
          for(var c=cb; c<cm; c++) {
            if(grid[r*GRIDWIDTH+c] == v) { cells[r*GRIDWIDTH+c].innerHTML = cv; }
          }}
        }

        ds_rm[y] |= n;
        ds_cm[x] |= n;
        ds_bm[b] |= n;
      }

      i++;
  }}
  
}

function small_hint(sb) 
{
  if(hint_invalid_digits()) {
    return 0;
  }

  if(!next_hint(0)) {
    fetch_state1(sb,1);
  }

  return 0;
}


function full_hint(sb) {
  if(hint_invalid_digits()) {
    return 0;
  }

  if(!next_hint(1)) {
    fetch_state1(sb,2);
  }

  return 0;
}


function hint_invalid_digits() {
 saC(-1);
 var ncells = mark_invalid_cells();
 if(ncells) {
  var ht = "<p>Currently "+ncells;
  ht += ncells>1 ? " cells contain" : " cell contains";
  ht += " the wrong digit.</p><p><input type=\"button\" onclick=\"clear_invalid_cells();\" value=\"Click here to remove ";
  ht += ncells>1 ? "them" : "it";
  ht +="\"></p>";
  htext.innerHTML = ht ;
  return 1;
 }

 
 return 0 ;
}

function clear_invalid_cells()
{
  for(var i=0; i<NUMCELLS; i++) {
    if(grid[i] && (grid[i] != solution[i])) {
      cells[i].innerHTML = "";
      cells[i].style.color = clr_valid ;
      grid[i] = 0 ;
    }
  }

  calc_candidates();
  return 0;
}

function mark_invalid_cells()
{
  var n = 0 ;
  for(var i=0; i<NUMCELLS; i++) {
    if(grid[i] && (grid[i] != solution[i]) && solution[i]) {
      cells[i].style.color = clr_invalid ;
      n++;
    }
  }

  return n ;
}

function unixtime() {
  return parseInt((new Date()).getTime()/1000); 
}

function cdT(ca,cr,ch,hl,bc,clrs) 
{
  var T = ""
  var f = 1 ;
  var d = 1 ;

  if(bc) { T+="<div class=\""+bc+"\">"; }

  T += "<table class=\"cd\">";
  for(var r=0; r<BOXWIDTH; r++) {
    T += "<tr>";
    for(var c=0; c<BOXWIDTH; c++) {
      T += "<td>";
      if(ca&f) 
      {
        if(clrs && clrs[d]) {
          T += "<span class=\"c"+clrs[d]+"\">"+d+"</span>";
        }
        else if(ch&f) {
          T+="<span class=\""+hl+"\">"+d+"</span>";
        }
        else {
          T += d ;
        }
      }
      else if(cr&f) {
        T += "<span class=\"cdr\">"+d+"</span>";
      }

      

      T += "</td>";
      f+=f ;
      d++;
    }

    T += "</tr>";
  }
  
  T+="</table>";
  if(bc) { T+="</div>"; }
  return T;
}

function edit_grid(sb) {
  if(_editable) {
    fetch_state1(sb,2);
  }
  else {
    _editable = 1;
    _mark_invalid_cells=0;
    _givens_editable =1;
    for(var i=0; i<NUMCELLS; i++) {
      grid[i] = givens[i];
      cells[i].innerHTML = givens[i] ? "<span>"+givens[i]+"</span>" : "<span></span>";
    }

    sb.value="Start solver" ;
  }
}