/****
**
**  Sudoku (sudoku.js)
**
**  2006 Guido Stammel
**
****/


//
//
//
function value_of( col, row ) {

  return cache[ col * size + row ];

}


//
//
//
function real_value_of( col, row ) {

  var j = game[ col * size + row ];
  var input = document.getElementById( 'input_' + row + '_' + col );
  if( input ) {
    j = input.value;
    if( j == ' ' ) j = '.';
    if( j == '' ) j = '.';
  }

  cache[ col * size + row ] = (new String( j )).toUpperCase( );

  return cache[ col * size + row ];

}


//
//
//
function check_v_constraints( row ) {

  var f = new Array( );
  var failed = false;

  for( var col = 0; col < size; col++ ) {
    var j = value_of( col, row );
    if( j == '.' ) continue;
    if( f[ j ] ) {
      failed = true;
      break;
    }
    f[ j ] = true;
  }

  if( failed ) {
    for( var col = 0; col < size; col++ ) {
      var cell = document.getElementById( 'cell_' + row + '_' + col );
      cell.style.background = '#FF4040';
    }
  }

  return failed;

}


//
//
//
function check_h_constraints( col ) {

  var f = new Array( );
  var failed = false;

  for( var row = 0; row < size; row++ ) {
    var j = value_of( col, row );
    if( j == '.' ) continue;
    if( f[ j ] ) {
      failed = true;
      break;
    }
    f[ j ] = true;
  }

  if( failed ) {
    for( var row = 0; row < size; row++ ) {
      var cell = document.getElementById( 'cell_' + row + '_' + col );
      cell.style.background = '#FF4040';
    }
  }

  return failed;

}


//
//
//
function check_f_constraints( area ) {

  var f = new Array( );
  var failed = false;

  area = String.fromCharCode( area + 65 );
  for( var row = 0; row < size; row++ ) {
    for( var col = 0; col < size; col++ ) {
      if( form[ col * size + row ] != area ) continue;
      var j = value_of( col, row );
      if( j == '.' ) continue;
      if( f[ j ] ) {
        failed = true;
        break;
      }
      f[ j ] = true;
    }
  }

  if( failed ) {
    for( var row = 0; row < size; row++ ) {
      for( var col = 0; col < size; col++ ) {
        if( form[ col * size + row ] != area ) continue;
        var cell = document.getElementById( 'cell_' + row + '_' + col );
        cell.style.background = '#FF4040';
      }
    }
  }

  return failed;

}


//
//
//
function find_simple_v_hint( col ) {

  var empty = 0, last_row;

  for( var row = 0; row < size; row++ ) {
    var j = value_of( col, row );
    if( j != '.' ) continue;
    empty++;
    last_row = row;
  }

  if( empty == 1 ) {
    var cell = document.getElementById( 'cell_' + last_row + '_' + col );
    cell.style.background = '#B0F0B0';
  }

  return false;

}


//
//
//
function find_simple_h_hint( row ) {

  var empty = 0, last_col;

  for( var col = 0; col < size; col++ ) {
    var j = value_of( col, row );
    if( j != '.' ) continue;
    empty++;
    last_col = col;
  }

  if( empty == 1 ) {
    var cell = document.getElementById( 'cell_' + row + '_' + last_col );
    cell.style.background = '#B0F0B0';
  }

  return false;

}


//
//
//
function find_simple_f_hint( area ) {

  var empty = 0, last_col, last_row;

  area = String.fromCharCode( area + 65 );
  for( var row = 0; row < size; row++ ) {
    for( var col = 0; col < size; col++ ) {
      if( form[ col * size + row ] != area ) continue;
      var j = value_of( col, row );
      if( j != '.' ) continue;
      empty++;
      last_col = col;
      last_row = row;
    }
  }

  if( empty == 1 ) {
    var cell = document.getElementById( 'cell_' + last_row + '_' + last_col );
    cell.style.background = '#B0F0B0';
  }

  return false;

}


//
//
//
function find_simple_hint( ) {

  var found = false;

  for( var i = 0; i < size; i++ ) {
    if( find_simple_h_hint( i ) ) found |= true;
    if( find_simple_v_hint( i ) ) found |= true;
    if( find_simple_f_hint( i ) ) found |= true;
  }

  return found;

}


//
//
//
function helper_create_field( ) {

  var field = new Array( );

  for( var col = 0; col < size; col++ ) {
    for( var row = 0; row < size; row++ ) {
      field[ col + row * size ] = new Array( );
      for( symbol = 0; symbol < size; symbol++ ) {
        field[ col + row * size ][ symbol ] = true;
      }
      field[ col + row * size ][ size ] = size;
    }
  }

  return field;

}


//
//
//
function helper_process_row_flags( field, row ) {

  for( var col = 0; col < size; col++ ) {
    var j = value_of( col, row );
    if( j == '.' ) continue;
    var symbol = (new String( j )).charCodeAt( 0 ) - baseCharCode;
    for( var col_i = 0; col_i < size; col_i++ ) {
      if( col_i == col ) continue;
      if( field[ col_i + row * size ][ symbol ] == true ) {
        field[ col_i + row * size ][ symbol ] = false;
        field[ col_i + row * size ][ size ]--;
      }
    }
    field[ col + row * size ][ size ] = 0;
  }

}


//
//
//
function helper_process_col_flags( field, col ) {

  for( var row = 0; row < size; row++ ) {
    var j = value_of( col, row );
    if( j == '.' ) continue;
    var symbol = (new String( j )).charCodeAt( 0 ) - baseCharCode;
    for( var row_i = 0; row_i < size; row_i++ ) {
      if( row_i == row ) continue;
      if( field[ col + row_i * size ][ symbol ] == true ) {
        field[ col + row_i * size ][ symbol ] = false;
        field[ col + row_i * size ][ size ]--;
      }
    }
    field[ col + row * size ][ size ] = 0;
  }

}


//
//
//
function helper_process_area_flags( field, area ) {

  area = String.fromCharCode( area + 65 );
  for( var row = 0; row < size; row++ ) {
    for( var col = 0; col < size; col++ ) {
      if( form[ col * size + row ] != area ) continue;
      var j = value_of( col, row );
      if( j == '.' ) continue;
      var symbol = (new String( j )).charCodeAt( 0 ) - baseCharCode;
      for( var row_i = 0; row_i < size; row_i++ ) {
        for( var col_i = 0; col_i < size; col_i++ ) {
          if( form[ col_i * size + row_i ] != area ) continue;
          if( (row_i == row) && (col_i == col) ) continue;
          if( field[ col_i + row_i * size ][ symbol ] == true ) {
            field[ col_i + row_i * size ][ symbol ] = false;
            field[ col_i + row_i * size ][ size ]--;
          }
        }
      }
      field[ col + row * size ][ size ] = 0;
    }
  }

}


//
//
//
function helper_find_col_hint( field, col ) {

  var found = false;

  for( var symbol = 0; symbol < size; symbol++ ) {
    var count = 0, last_row = 0;
    for( var row = 0; row < size; row++ ) {
      var j = value_of( col, row );
      if( (new String( j )).charCodeAt( 0 ) == symbol + baseCharCode ) count = size;
      if( j != '.' ) continue;
      if( field[ col + row * size ][ symbol ] ) {
        last_row = row;
        count++;
      }
    }
    if( count == 1 ) {
      var cell = document.getElementById( 'cell_' + last_row + '_' + col );
      cell.style.background = '#B0F0B0';
      found |= true;
    }
  }

  return found;

}


//
//
//
function helper_find_area_hint( field, area ) {

  var found = false;

  area = String.fromCharCode( area + 65 );
  for( var symbol = 0; symbol < size; symbol++ ) {
    var count = 0, last_row = 0, last_col = 0;
    for( var row = 0; row < size; row++ ) {
      for( var col = 0; col < size; col++ ) {
        if( form[ col * size + row ] != area ) continue;
        var j = value_of( col, row );
        if( (new String( j )).charCodeAt( 0 ) == symbol + baseCharCode ) count = size;
        if( j != '.' ) continue;
        if( field[ col + row * size ][ symbol ] ) {
          last_row = row;
          last_col = col;
          last_symbol = symbol;
          count++;
        }
      }
    }
    if( count == 1 ) {
      var cell = document.getElementById( 'cell_' + last_row + '_' + last_col );
      cell.style.background = '#B0F0B0';
      found |= true;
    }
  }

  return found;

}


//
//
//
function helper_find_row_hint( field, row ) {

  var found = false;

  for( var symbol = 0; symbol < size; symbol++ ) {
    var count = 0, last_col = 0;
    for( var col = 0; col < size; col++ ) {
      var j = value_of( col, row );
      if( (new String( j )).charCodeAt( 0 ) == symbol + baseCharCode ) count = size;
      if( j != '.' ) continue;
      if( field[ col + row * size ][ symbol ] ) {
        last_col = col;
        count++;
      }
    }
    if( count == 1 ) {
      var cell = document.getElementById( 'cell_' + row + '_' + last_col );
      cell.style.background = '#B0F0B0';
      found |= true;
    }
  }

  return found;

}


//
//
//
function helper_find_field_hint( field ) {

  var found = false;

  for( var i = 0; i < size; i++ ) {
    helper_find_row_hint( field, i );
    helper_find_col_hint( field, i );
    helper_find_area_hint( field, i );
  }

  for( var col = 0; col < size; col++ ) {
    for( var row = 0; row < size; row++ ) {
      if( field[ col + row * size ][ size ] != 1 ) continue;
      var cell = document.getElementById( 'cell_' + row + '_' + col );
      cell.style.background = '#B0F0B0';
      found |= true;
    }
  }

  return found;

}


//
//
//
function find_easy_hint( ) {

  var found = false;
  var field = helper_create_field( );

  for( var col = 0; col < size; col++ ) {
    var field2 = field;
    helper_process_col_flags( field2, col );
    for( var row = 0; row < size; row++ ) {
      var field3 = field2;
      helper_process_row_flags( field3, row );
      if( helper_find_field_hint( field3 ) ) found |= true;
    }
  }

  for( var area = 0; area < size; area++ ) {
    var field2 = field;
    helper_process_area_flags( field2, area );

    for( var col = 0; col < size; col++ ) {
      var field3 = field2;
      helper_process_col_flags( field3, col );
      if( helper_find_field_hint( field3 ) ) found |= true;
    }

    for( var row = 0; row < size; row++ ) {
      var field3 = field2;
      helper_process_row_flags( field3, row );
      if( helper_find_field_hint( field3 ) ) found |= true;
    }
  }

  return found;

}


//
//
//
function find_hard_hint( ) {

  var found = false;
  var field = helper_create_field( );

  for( var col = 0; col < size; col++ ) {
    var field2 = field;
    helper_process_col_flags( field2, col );
    for( var row = 0; row < size; row++ ) {
      var field3 = field2;
      helper_process_row_flags( field3, row );
      for( var area = 0; area < size; area++ ) {
        var field4 = field3;
        helper_process_row_flags( field4, area );
        if( helper_find_field_hint( field4 ) ) found |= true;
      }
    }
  }

  return found;

}


//
//
//
function find_complex_hint( ) {

  var found = false;
  var field = helper_create_field( );

  for( var col = 0; col < size; col++ ) {
    helper_process_col_flags( field, col );
  }

  for( var row = 0; row < size; row++ ) {
    helper_process_row_flags( field, row );
  }

  for( var area = 0; area < size; area++ ) {
    helper_process_area_flags( field, area );
  }

  if( helper_find_field_hint( field ) ) found |= true;

  return found;

}


//
//
//
function find_hint( ) {

//  if( find_simple_hint( ) ) return true;
//  if( find_easy_hint( ) ) return true;
//  if( find_hard_hint( ) ) return true;
  if( find_complex_hint( ) ) return true;

  return false;

}


//
//
//
function check_constraints( ) {

  var failed = false, ready = true;

  for( var row = 0; row < size; row++ ) {
    for( var col = 0; col < size; col++ ) {
      var cell = document.getElementById( 'cell_' + row + '_' + col );
      var v = real_value_of( col, row );
      var this_failed = false;
      if( v.length != 1 ) this_failed = true;
      if( v != '.' ) {
        if( v.charCodeAt( 0 ) < baseCharCode ) this_failed = true;
        if( v.charCodeAt( 0 ) >= baseCharCode + size ) this_failed = true;
      } else {
        ready = false;
      }
      cell.style.background = (this_failed ? '#FF4040' : '#FFFFFF');
      failed |= this_failed;
      ready &= (failed == false);
    }
  }

  for( var i = 0; i < size; i++ ) {
    failed |= check_v_constraints( i );
    failed |= check_h_constraints( i );
    failed |= check_f_constraints( i );
  }

  ready &= (failed == false);
  if( ready == true ) {
    for( var row = 0; row < size; row++ ) {
      for( var col = 0; col < size; col++ ) {
        var cell = document.getElementById( 'cell_' + row + '_' + col );
        cell.style.background = '#40FF40';
      }
    }
  } else if( (failed == false) && (show_hints == true) ) {
    find_hint( );
  }

  return failed;

}


//
//
//
function prepare_game( game_string ) {

  game_string = String( game_string );
  size = Math.floor( Math.sqrt( (game_string.length - 1) / 2 ) );
  game = new Array( );
  form = new Array( );
  cache = new Array( );

  for( var i = 0, j = 0; i < size * size; i++, j++ ) {
    if( game_string.charAt( j ) == '.' ) {
      game[ i ] = '.';
    } else {
      game[ i ] = String.fromCharCode( game_string.charCodeAt( j ) - 65 + baseCharCode );
    }
    form[ i ] = game_string.charAt( size * (size + 1) + j );
    if( (i + 1) % size == 0 ) j++;
  }

}


//
//
//
function render_game( table ) {

  for( var j = 0; j < size; j++ ) {
    var row = document.createElement( 'tr' );
    table.appendChild( row  );
    for( var i = 0; i < size; i++ ) {
      var cell = document.createElement( 'td' );
      cell.id = 'cell_' + i + '_' + j;
      if( game[ i + j * size ] == '.' ) {
        var cell_input = document.createElement( 'input' );
        cell_input.id = 'input_' + i + '_' + j;
        cell_input.setAttribute( 'type', 'text' );
        cell_input.setAttribute( 'size', '1' );
        cell_input.setAttribute( 'maxlength', '1' );
        cell_input.setAttribute( 'value', '' );
        cell_input.onkeyup = function( ) { check_constraints( ) };
        cell.appendChild( cell_input );
      } else {
        cell_content = document.createElement( 'div' );
        cell_content_data = document.createTextNode( game[ i + j * size ] );
        cell_content.appendChild( cell_content_data );
        cell.appendChild( cell_content );
      }
      if( (i == 0) || (form[ i + j * size ] != form[ i - 1 + j * size ]) ) cell.style.borderLeft = '3px solid #000000';
      if( (i == size - 1) || (form[ i + j * size ] != form[ i + 1 + j * size ]) ) cell.style.borderRight = '3px solid #000000';
      if( (j == 0) || (form[ i + j * size ] != form[ i + (j - 1) * size ]) ) cell.style.borderTop = '3px solid #000000';
      if( (j == size - 1) || (form[ i + j * size ] != form[ i + (j + 1) * size ]) ) cell.style.borderBottom = '3px solid #000000';
      row.appendChild( cell );
    }
  }

}


//
//
//
function fetch_game( url ) {

  var request;

  var a = document.getElementById( 'game_table' );
  do {
    var b = a.firstChild;
    if( b == null ) break;
    a.removeChild( b );
  } while( true );

  try {
    request = new ActiveXObject( 'Microsoft.XMLHTTP' );
  } catch( err ) {
    request = new XMLHttpRequest( );
  }

  request.onreadystatechange = function( ) {
    if( request.readyState == 4 ) {
      var result = request.responseXML.getElementsByTagName( 'response' )[ 0 ];
      if( result.getAttribute( 'state' ) == 'success' ) {
        var game_string = result.getElementsByTagName( 'game' )[ 0 ].firstChild.nodeValue;
        prepare_game( game_string );
        render_game( document.getElementById( 'game_table' ) );
        check_constraints( );
      }
    }
  }

  request.open( 'GET', url, true );
  request.send( null );

}


/****
**
**  End Of File (sudoku.js)
**
****/
