
var xmlDoc; // the crossword document
var basefile;

var labels;
var clues;
var grid;
var crosssize; // crossword size

var ce_a_key
var ce_a_arr
var ce_d_key
var ce_d_arr


var xpos; // current cursor position
var ypos; // ditto
var align = true; // across

var timer; // used to keep track of text size changes
var known_xpos; // track the position to know when change occurred
var known_ypos; // ditto

////////////////////////////////////////
// Initialize Crossword
function initialize(file)
{
  if (window.location.search.substring(1))
  {
    basefile = window.location.search.substring(1);
  }
  else if (file)
  {
    basefile = file;
  }
  else
  {
    alert('No puzzle specified');
  }

  // Internet Explorer
  if (window.ActiveXObject)
  {
    xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
    xmlDoc.onreadystatechange = function()
    {
      if (xmlDoc.readyState == 4) generateCrossword();
    }
  }

  // Mozilla et al
  else if (document.implementation &&
           document.implementation.createDocument)
  {
    xmlDoc = document.implementation.createDocument("", "", null);
    xmlDoc.onload = generateCrossword;
  }

  // Other
  else
  {
    alert("Your browser can't handle this script");
    return;
  }

  xmlDoc.load(basefile);
}


////////////////////////////////////////
// Generate Crossword
function generateCrossword()
{
  // Initialize crossword
  clues = new Array();
  grid = new Array();
  labels = new Array();

  // Root element
  var x = xmlDoc.getElementsByTagName('crossword');

  if (x.length == 0)
  {
    alert('Error loading crossword puzzle "' + basefile + '"');
    return;
  }

  // Get Title
  var e = document.getElementById('title');
  e.innerHTML = x.item(0).getAttribute('title');

  // Get crossword size
  crosssize = x.item(0).getAttribute('size');

  e = document.getElementById('crossword');
  e.style.width = (crosssize * 2) + 'em';
  e.style.height = (crosssize * 2) + 'em';

  var ce = document.getElementById('clues');
  ce.style.left = (crosssize * 2 + 2) + 'em';
  ce.style.height = (crosssize * 2) + 'em';

  ce_a_key = new Array();
  ce_a_arr = new Array();

  ce_d_key = new Array();
  ce_d_arr = new Array();

  var cookiedata = readCookie(basefile);
  if (cookiedata)
  {
    cookiedata = cookiedata.split('');
  }

  // Initialize grid
  for (var i = 0; i < crosssize; i++)
  {
    grid[i] = new Array();
    for (var j = 0; j < crosssize; j++)
    {
      grid[i][j] = new Array();

      var dc = document.createElement('DIV');
      dc.style.top = (j * 2) + 'em';
      dc.style.left = (i * 2) + 'em';
      e.appendChild(dc);

      var d = document.createElement('INPUT');
      d.maxLength = 1;
      d.gridx = i;
      d.gridy = j;
      d.setAttribute('autocomplete', 'off');
      d.readOnly = 1;
      if (!cookiedata)
      {
        d.value = '\u2022';
      }
      else
      {
        if (cookiedata[i * crosssize + j] != '.')
        {
          d.value = cookiedata[i * crosssize + j];
        }
        else
        {
          d.value = '';
        }
      }
      d.style.cursor = 'default';
      dc.style.backgroundColor = 'black';

      dc.appendChild(d);

      grid[i][j]['input'] = d;
    }
  }

  // Get clues
  var x = xmlDoc.getElementsByTagName('clue');
  for (var i = 0; i < x.length; i++)
  {
    var node = x.item(i);

    // Parse input
    var x_posx = parseInt(node.getAttribute('posx'));
    var x_posy = parseInt(node.getAttribute('posy'));
    var x_len = parseInt(node.getAttribute('length'));
    var x_align = node.getAttribute('align');
    var x_num = parseInt(node.getAttribute('number'));
    var x_val = node.firstChild.nodeValue;
    var x_ans = node.getAttribute('answer');

    // Save clue
    clue = new Array(x_posx, x_posy, x_len, x_align, x_num, x_val, x_ans);
    clues[clues.length] = clue;

    // Create input fields
    if (x_align == 'across')
    {
      ce_a_key[ce_a_key.length] = x_num;
      ce_a_arr[x_num] = clue;

      for (var j = x_posx; j < x_posx + x_len; j++)
      {
        var box = grid[j][x_posy]['input'];

        box.onclick = focusBox;
        box.onfocus = focusBox;
        box.onkeyup = inputBox;
        if (box.value == '\u2022')
        {
          box.value = '';
        }
        box.readOnly = 0;
        box.style.cursor = 'text';

        box.parentNode.style.backgroundColor = 'white';

        grid[j][x_posy]['across'] = clue;
      }
    }
    else
    {
      ce_d_key[ce_d_key.length] = x_num;
      ce_d_arr[x_num] = clue;

      for (var j = x_posy; j < x_posy + x_len; j++)
      {
        var box = grid[x_posx][j]['input'];

        box.onclick = focusBox;
        box.onfocus = focusBox;
        box.onkeyup = inputBox;
        if (box.value == '\u2022')
        {
          box.value = '';
        }
        box.readOnly = 0;
        box.style.cursor = 'text';

        box.parentNode.style.backgroundColor = 'white';

        grid[x_posx][j]['down'] = clue;
      }
    }

    // Set up text label
    if (!grid[x_posx][x_posy]['label'])
    {
      var parent = grid[x_posx][x_posy]['input'].parentNode
      var o_posx = parent.offsetLeft + 3;
      var o_posy = parent.offsetTop;

      var dc = document.createElement('SPAN');
      dc.style.fontSize = '60%';
      dc.style.fontFamily = 'arial, helvetica, sans-serif';
      dc.style.verticalAlign = 'top';
      dc.innerHTML = x_num;
      dc.style.zindex = -20;
      dc.style.top = o_posy + 'px';
      dc.style.left = o_posx + 'px';
      dc.style.position = 'absolute';
      e.appendChild(dc);

      grid[x_posx][x_posy]['label'] = dc;

      labels[labels.length] = new Array(x_posx, x_posy, dc);
    }
  }


  // set up the clues
  var first;
  var ce_a = document.getElementById('clues_across_list');
  ce_a_key.sort(function(a,b){return a-b});
  for (var i = 0; i < ce_a_key.length; i++)
  {
    var key = ce_a_key[i];
    var e = document.createElement('DIV');
    e.id = 'clue_' + ce_a_arr[key][3] + '_' + ce_a_key[i];
    e.clue = ce_a_arr[key];
    e.onclick = selectClue;
    e.onmouseover = hoverClue;
    e.onmouseout = unhoverClue;
    e.innerHTML = ce_a_arr[key][4] + '. ' + ce_a_arr[key][5];
    ce_a.appendChild(e);

    if (!first)
    {
      first = grid[ce_a_arr[key][0]][ce_a_arr[key][1]]['input'];
    }
  }

  var ce_d = document.getElementById('clues_down_list');
  ce_d_key.sort(function(a,b){return a-b});
  for (var i = 0; i < ce_d_key.length; i++)
  {
    var key = ce_d_key[i];
    var e = document.createElement('DIV');
    e.id = 'clue_' + ce_d_arr[key][3] + '_' + ce_d_key[i];
    e.clue = ce_d_arr[key];
    e.onclick = selectClue;
    e.onmouseover = hoverClue;
    e.onmouseout = unhoverClue;
    e.innerHTML = ce_d_arr[key][4] + '. ' + ce_d_arr[key][5];
    ce_d.appendChild(e);
  }


  // Display the crossword
  document.getElementById('loading').style.display = "none";
  document.getElementById('wrapper').style.visibility = "visible";
  if (first) first.focus();

  var lastparent = grid[crosssize - 1][crosssize - 1]['input'].parentNode;
  known_xpos = lastparent.offsetLeft;
  known_ypos = lastparent.offsetTop;

  timer = window.setInterval(checkFormatting, 100);
}

function checkGrid()
{
  var empty = 0;
  for (var i = 0; i < clues.length; i++)
  {
    var clue = clues[i];
    var letters = clue[6].split('');
    if (clue[3] == 'across')
    {
      for (var j = 0; j < clue[2]; j++)
      {
        var val = grid[j + clue[0]][clue[1]]['input'].value;
        val = val.replace(/^\s*|\s*$/g,"").toUpperCase();
	if (val == '')
	{
          empty++;
        }
        else if (val != letters[j].toUpperCase())
        {
          alert("You've made a mistake somewhere");
          return;
        }
      }
    }
  }

  if (empty == 0)
  {
    uncolorBox();
    alert("Congrats! You've completed the puzzle.");
  }
  else
  {
    alert("Congrats! There's no mistakes.");
  }
}

function revealGrid()
{
  uncolorBox();

  // should be able to just fill in all the across's
  for (var i = 0; i < clues.length; i++)
  {
    var clue = clues[i];
    var letters = clue[6].split('');
    if (clue[3] == 'across')
    {
      for (var j = 0; j < clue[2]; j++)
      {
        grid[clue[0] + j][clue[1]]['input'].value = letters[j];
      }
    }
  }
}

function clearGrid()
{
  for (var i = 0; i < crosssize; i++)
  {
    for (var j = 0; j < crosssize; j++)
    {
      if (!grid[i][j]['input'].readOnly)
      {
        grid[i][j]['input'].value = '';
      }
    }
  }

  if (xpos != undefined && ypos != undefined)
  {
    uncolorBox();
    action = -1;
    grid[xpos][ypos]['input'].focus();
    colorBox();
  }

  eraseCookie(basefile);
}

function checkFormatting()
{
  var lastparent = grid[crosssize - 1][crosssize - 1]['input'].parentNode;

  if (known_xpos != lastparent.offsetLeft ||
      known_ypos != lastparent.offsetTop)
  {
    for (var i = 0; i < labels.length; i++)
    {
      var gx = labels[i][0];
      var gy = labels[i][1];
      var dc = labels[i][2];

      var parent = grid[gx][gy]['input'].parentNode;
      var ox = parent.offsetLeft + 3;
      var oy = parent.offsetTop;

      dc.style.top = oy + 'px';
      dc.style.left = ox + 'px';
    }

    known_xpos = lastparent.offsetLeft;
    known_ypos = lastparent.offsetTop;
  }
}

function inputBox(evt)
{
  var e = (window.event) ? window.event : evt;
  var charCode = (e.charCode) ? e.charCode :
                  ((e.keyCode) ? e.keyCode :
                   ((e.which) ? e.which : 0));

  var x = parseInt(this.gridx);
  var y = parseInt(this.gridy);

  // Left key
  if (charCode == 37)
  {
    x--;
    while (x > 0 && grid[x][y]['input'].readOnly)
    {
      x--;
    }

    if (x >= 0)
    {
      grid[x][y]['input'].focus();
    }
  }

  // Right key
  else if (charCode == 39 && x < crosssize)
  {
    x++;
    while (x < crosssize && grid[x][y]['input'].readOnly)
    {
      x++;
    }

    if (x < crosssize)
    {
      grid[x][y]['input'].focus();
    }
  }

  // Up key
  else if (charCode == 38 && y > 0)
  {
    y--;
    while (y > 0 && grid[x][y]['input'].readOnly)
    {
      y--;
    }

    if (y >= 0)
    {
      grid[x][y]['input'].focus();
    }
  }

  // Down key
  else if (charCode == 40 && y < crosssize)
  {
    y++;
    while (y < crosssize && grid[x][y]['input'].readOnly)
    {
      y++;
    }

    if (y < crosssize)
    {
      grid[x][y]['input'].focus();
    }
  }

  // Enter key
  else if (charCode == 13)
  {
    // do something
    return false;
  }

  // Space key
  else if (charCode == 32)
  {
    return false;
  }

  // Alphanum keys
  else if ((charCode >= 65 && charCode <= 90) ||
           (charCode >= 97 && charCode <= 122) ||
           (charCode >= 48 && charCode <= 57))
  {
    function selectNext(clue)
    {
      var found = 0;
      if (clue[3] == 'across')
      {
        for (var i = 0; i < ce_a_key.length; i++)
        {
          var ce = ce_a_arr[ce_a_key[i]];
          if (found)
          {
            grid[ce[0]][ce[1]]['input'].focus();
            return;
          }
          if (ce == clue)
          {
            found = 1;
          }
        }
      }
      else
      {
        for (var i = 0; i < ce_d_key.length; i++)
        {
          var ce = ce_d_arr[ce_d_key[i]];
          if (found)
          {
            grid[ce[0]][ce[1]]['input'].focus();
            return;
          }
          if (ce == clue)
          {
            found = 1;
          }
        }
      }
    }

    if (align)
    {
      var clue = grid[x][y]['across'];
      if (clue[2] == x - clue[0] + 1)
      {
        // go to next across
        selectNext(clue);
      }
      else
      {
        // next letter
        grid[x + 1][y]['input'].focus();
      }
    }
    else
    {
      var clue = grid[x][y]['down'];
      if (clue[2] == y - clue[1] + 1)
      {
        // go to next down
        selectNext(clue);
      }
      else
      {
        // next letter
        grid[x][y + 1]['input'].focus();
      }
    }
    
    // save results into cookie
    var data = '';
    for (var i = 0; i < crosssize; i++)
    {
      for (var j = 0; j < crosssize; j++)
      {
        if (grid[i][j]['input'].value != '' &&
            grid[i][j]['input'].value != ' ')
        {
          data += grid[i][j]['input'].value;
        }
        else
        {
          data += '.';
        }
      }
    }
    createCookie(basefile, data, 7);
  }
}

var selectedClueAcross;
var selectedClueDown;
function hoverClue()
{
  if (this == selectedClueAcross || this == selectedClueDown) return;
  this.style.backgroundColor = '#ccc';
}

function unhoverClue()
{
  if (this == selectedClueAcross || this == selectedClueDown) return;
  this.style.backgroundColor = 'white';
}

function selectClue()
{
  if (this.parentNode.id == 'clues_across_list')
  {
    if (selectedClueAcross)
    {
      selectedClueAcross.style.backgroundColor = 'white';
    }
    selectedClueAcross = this;
    this.style.backgroundColor = '#fcc';
    align = 1;
  }
  else
  {
    if (selectedClueDown)
    {
      selectedClueDown.style.backgroundColor = 'white';
    }
    selectedClueDown = this;
    this.style.backgroundColor = '#fcc';
    align = 0;
  }

  var clue = this.clue;
  grid[clue[0]][clue[1]]['input'].focus();
}

var action = 0;
function focusBox()
{
  // horrible kludge for multi-action until i figure something else
  var timestamp = new Date();
  if (action == -1 ||
      ((timestamp.getTime() - action) < 250 &&
       xpos == this.gridx && ypos == this.gridy))
  {
    action = timestamp.getTime();
    return;
  }
  action = timestamp.getTime();

  // de-highlight selected word
  if (xpos != undefined && ypos != undefined)
  {
    uncolorBox();
  }

  // if double clicked, change alignment
  if (xpos == this.gridx && ypos == this.gridy)
  {
    align = !align;
  }

  // select input box contents
  this.select();

  // set our current position
  xpos = this.gridx;
  ypos = this.gridy;
  
  // highlight the selected word
  colorBox();

  // scroll clue text across
  if (grid[xpos][ypos]['across'])
  {
    var scid = 'clue_across_' + grid[xpos][ypos]['across'][4];
    var scel = document.getElementById(scid);
    if (scel != selectedClueAcross)
    {
      if (selectedClueAcross)
      {
        selectedClueAcross.style.backgroundColor = 'white';
      }

      selectedClueAcross = scel;
      selectedClueAcross.style.backgroundColor = '#fcc';
      selectedClueAcross.scrollIntoView();
    }
  }

  // scroll clue text down
  if (grid[xpos][ypos]['down'])
  {
    var scid = 'clue_down_' + grid[xpos][ypos]['down'][4];
    var scel = document.getElementById(scid);
    if (scel != selectedClueDown)
    {
      if (selectedClueDown)
      {
        selectedClueDown.style.backgroundColor = 'white';
      }
      selectedClueDown = scel;
      selectedClueDown.style.backgroundColor = '#fcc';
      selectedClueDown.scrollIntoView();
    }
  }
}

function colorBox()
{
  var gridpos = grid[xpos][ypos];
  var color = '#bbb';
  var hilight = '#b88';

  if (align && gridpos['across'])
  {
    var clue = gridpos['across'];
    for (var i = clue[0]; i < clue[0] + clue[2]; i++)
    {
      var box = grid[i][clue[1]]['input'].parentNode;
      box.style.backgroundColor = color;
    }
  }
  else if (gridpos['down'])
  {
    var clue = gridpos['down'];
    for (var i = clue[1]; i < clue[1] + clue[2]; i++)
    {
      var box = grid[clue[0]][i]['input'].parentNode;
      box.style.backgroundColor = color;
    }
  }

  var box = gridpos['input'].parentNode;
  box.style.backgroundColor = hilight;
}

function uncolorBox()
{
  var gridpos = grid[xpos][ypos];
  var color = 'white';

  if (gridpos['across'])
  {
    var clue = gridpos['across'];
    for (var i = clue[0]; i < clue[0] + clue[2]; i++)
    {
      var box = grid[i][clue[1]]['input'].parentNode;
      box.style.backgroundColor = color;
    }
  }
  if (gridpos['down'])
  {
    var clue = gridpos['down'];
    for (var i = clue[1]; i < clue[1] + clue[2]; i++)
    {
      var box = grid[clue[0]][i]['input'].parentNode;
      box.style.backgroundColor = color;
    }
  }
}


function createCookie(name,value,days)
{
  var expires = "";
  if (days)
  {
    var date = new Date();
    date.setTime(date.getTime() + (days * 24*60*60*1000));
    expires = "; expires=" + date.toGMTString();
  }

  document.cookie = name + "=" + value + expires + "; path=/";
}

function readCookie(name)
{
  var nameEQ = name + "=";
  var ca = document.cookie.split(';');
  for(var i=0; i < ca.length; i++)
  {
    var c = ca[i];
    while (c.charAt(0) == ' ')
    {
      c = c.substring(1, c.length);
    }

    if (c.indexOf(nameEQ) == 0)
    {
      return c.substring(nameEQ.length, c.length);
    }
  }
  return null;
}

function eraseCookie(name)
{
  createCookie(name, "", -1);
}
