window.onload = function () {
  removeNoJavaScriptNotice();
  buildLifeTable();
  // continuouslyLoopForLife();
}

/*
 * Wish I could thread. Leave for future possibilities.
 */
function continuouslyLoopForLife() {

  while (1) {
    
    if (isLifeRunning) {
      stepLife();
    }

    pause(250);
  }
}

/*
 *
 */
function removeNoJavaScriptNotice() {
  document.getElementById('no_javascript').style.display = 'none';
}

var rows = 32;
var cols = 60;

// The current life array
var lifeArray = 0;

/*
 * The next life array. When calculating board, don't want to change
 * the current array since it will influence subsequent calculations.
 *
 * In other words, while calculating the board, don't change the values
 * being used to determine what will be alive on the next step!
 *
 * After the calculation is complete, lifeArray will point to nextLifeArray.
 */
var nextLifeArray = 0;

// The age. When reset, should be 0.
var age = 0;

/*
 *
 */
function buildLifeTable() {
  var lifeTableWrapper = document.getElementById('life_table_wrapper');

  // Create the table and add to page
  var lifeTable = document.createElement("table");
  lifeTable.setAttribute("id", "life_table");
  lifeTableWrapper.appendChild(lifeTable);
  
  // Add a tbody so won't run into problems with MSIE
  var lifeTableBody = document.createElement("tbody");
  lifeTable.appendChild(lifeTableBody);

  // Generate the rows and cells with usable ids
  var row;
  var col;
  for (row = 0; row < rows; row++) {

    var nextRow = document.createElement("tr");
    nextRow.setAttribute("id","life_row_"+row);
    lifeTableBody.appendChild(nextRow);


    for (col = 0; col < cols; col++) {
      var nextCell = document.createElement("td");
      nextCell.setAttribute("id","life_cell_"+row+"_"+col);
      nextCell.onclick = nextCellOnClick;

      nextRow.appendChild(nextCell);
    } // For each generated cell
  } // For each generated row

  initializeTable();
} // buildLifeTable

/*
 *
 */
function clearLife() {
  var confirmClear = confirm('Really genocide life from the board?');
  
  if (confirmClear) {
    initializeTable();
  }
  return false;
}

/*
 *
 */
function stepLife() {
  var row;
  var col;

  // Copy over array to next so can modify it with affecting 
  // the current calculations
  nextLifeArray = createDeepCopyOfMultidimensionalArray(lifeArray);

  for (row = 0; row < rows; row++) {
    for (col = 0; col < cols; col++) {
      transitionCell(row,col);
    }
  }

  // Move over the reference, then set nextLifeArray back to null
  lifeArray = nextLifeArray;
  nextLifeArray = 0;

  // Set the age
  age++;
  printAgeToPage();

  return false;
}

/*
 *
 */
function printAgeToPage() {
  var ageSpan = document.getElementById('age');
  ageSpan.innerHTML = age;
}

var isLifeRunning = 0;

/*
 *
 */
function runOrPauseLife() {
  var runLifeButton = document.getElementById('run_life_button');

  if (isLifeRunning) {
    runLifeButton.innerHTML = 'Run life';
    isLifeRunning = 0;
  } else {
    runLifeButton.innerHTML = 'Pause life';
    isLifeRunning = 1;
  }

  runLifeNumberOfSteps(9);

  return false;
}

/*
 *
 */
function warpLife() {

  var steps = 0;
  try {
    var stepsInput = document.getElementById('steps');
    steps = parseInt(stepsInput.value);

    if (isNaN(steps)) {
      throw stepsInput.value+" is not a number. Please input a whole number, such as 10";
    }
  } catch (err) {
    alert('Cannot warp due to error:\n\n'+err);
  }

  runLifeNumberOfSteps(steps);

  return false;
}

/*
 *
 */
function runLifeNumberOfSteps(numSteps) {
  var i;
  for (i=0; i<numSteps; i++) {
    stepLife();
  }

  return false;
}

/*
 * Rules of life:
 * 1. If unoccupied and exactly three neighbors are occupied, occupy
 * 2. If occupied and the number of occupied neighbors is not two or three, unoccupy
 */
function transitionCell(row,col) {
  var occupiedNeighbors = getNumberOccupiedNeighbors(row,col);
  //
  if (lifeArray[row][col] == 0) {
    if (occupiedNeighbors == 3) {
      setOccupied(row,col);
    } else {
      // Do nothing, already unoccupied
    }
  } else {
    if (occupiedNeighbors == 2 || occupiedNeighbors == 3) {
      // Do nothing, already occupied
    } else {
      setUnoccupied(row,col);
    }
  }
}

/*
 * There are eight neighbors.
 */
function getNumberOccupiedNeighbors(row,col) {
  var upIndex = row - 1;
  var downIndex = row + 1;
  var leftIndex = col - 1;
  var rightIndex = col + 1;

  // Check for over/underflow, and adjust. Yes,
  // we're playing "Around the World"
  if (upIndex < 0) {
    upIndex = rows - 1;
  } 
  if (downIndex > rows - 1) {
    downIndex = 0;
  }
  if (leftIndex < 0) {
    leftIndex = cols - 1;
  }
  if (rightIndex > cols - 1) {
    rightIndex = 0;
  }

  var num = 0;

  // Check top neighbor, then go counter clockwise
  if (lifeArray[upIndex][col]) {
    num++;
  }
  if (lifeArray[upIndex][leftIndex]) {
    num++;
  }
  if (lifeArray[row][leftIndex]) {
    num++;
  }
  if (lifeArray[downIndex][leftIndex]) {
    num++;
  }
  if (lifeArray[downIndex][col]) {
    num++;
  }
  if (lifeArray[downIndex][rightIndex]) {
    num++;
  }
  if (lifeArray[row][rightIndex]) {
    num++;
  }
  if (lifeArray[upIndex][rightIndex]) {
    num++;
  }

  return num;
}

/*
 *
 */
function initializeTable() {

  isLifeRunning = 0;

  age = 0;
  printAgeToPage();

  // Initialize the life array
  lifeArray = createMultidimensionalArray(rows,cols);

  // Set all cells to be unoccupied
  var row;
  var col;
  for (row = 0; row < rows; row++) {
    for (col = 0; col < cols; col++) {
      setUnoccupied(row,col);
    }
  }
}

/*
 *
 */
function clearAllChildNodes(domObj) {
  if (domObj.hasChildNodes()) {
    while (domObj.childNodes.length >= 1) {
      domObj.removeChild(domObj.firstChild);
    }
  }
}

/*
 *
 */
function nextCellOnClick() {

  //
  clearAllChildNodes(this);

  var row = getRowForCell(this);
  var col = getColForCell(this);

  //
  if (lifeArray[row][col] == 0) {
    setOccupied(row,col);
  } else {
    setUnoccupied(row,col);
  }
} 

/*
 *
 */
function setOccupied(row,col) {
  var cell = document.getElementById("life_cell_"+row+"_"+col);
  
//
  clearAllChildNodes(cell);
      
  //
  var img = document.createElement('img');
  img.setAttribute("src","images/occupied.gif");
  cell.appendChild(img);

  // If there is a nextLifeArray, use that. Otherwise, use current lifeArray.
  if (nextLifeArray) {
    nextLifeArray[row][col] = 1;
  } else {
    lifeArray[row][col] = 1;
  }
}

/*
 *
 */
function setUnoccupied(row,col) {
  var cell = document.getElementById("life_cell_"+row+"_"+col);

  //
  clearAllChildNodes(cell);

  //
  var img = document.createElement('img');
  img.setAttribute("src","images/unoccupied.gif");
  cell.appendChild(img);

  // If there is a nextLifeArray, use that. Otherwise, use current lifeArray.
  if (nextLifeArray) {
    nextLifeArray[row][col] = 0;
  } else {
    lifeArray[row][col] = 0;
  }
}

/*
 *
 */
function getRowForCell(cell) {
  var tokens = new Array();
  tokens = cell.id.split('_');
  var row = tokens[2];
  return parseInt(row);
}

/*
 *
 */
function getColForCell(cell) {
  var tokens = new Array();
  tokens = cell.id.split('_');
  var col = tokens[3];
  return parseInt(col);
}

/*
 *
 */
function createMultidimensionalArray(rowCount,colCount) {
  var i;
  var j;
  var a = new Array(rowCount);
  for (i=0; i < rowCount; i++) {
    a[i] = new Array(colCount);
    for (j=0; j < colCount; j++){
      a[i][j] = 0;
    }
  }
  return(a);
}

/*
 *
 */
function createDeepCopyOfMultidimensionalArray(array) {
  var i;
  var j;
  var copy = new Array(array.length);
  for (i=0; i<array.length; i++) {
    copy[i] = new Array(array[i].length);
    for (j=0; j<array[i].length; j++) {
      copy[i][j] = array[i][j];
    }
  }
  return copy;
}

/*
 * http://www.sean.co.uk/a/webdesign/javascriptdelay.shtm
 */
function pause(millis) {
  var date = new Date();
  var curDate = null;

  do { curDate = new Date(); }
    while(curDate-date < millis);
}

function setGlider() {
  var confirmAction = confirm('If setting a pattern, clears current population. Continue?');

  if (confirmAction) {
    initializeTable();
    setOccupied(2,cols-5);
    setOccupied(2,cols-4);
    setOccupied(3,cols-6);
    setOccupied(3,cols-5);
    setOccupied(4,cols-4);
  }

  return false;
}

function setPuffer() {
  var confirmAction = confirm('If setting a pattern, clears current population. Continue?');

  if (confirmAction) {
    initializeTable();
    setOccupied(7,rows-18);
    setOccupied(8,rows-18);
    setOccupied(9,rows-18);
    setOccupied(13,rows-18);
    setOccupied(14,rows-18);
    setOccupied(15,rows-18);
    setOccupied(7,rows-17);
    setOccupied(10,rows-17);
    setOccupied(12,rows-17);
    setOccupied(15,rows-17);
    setOccupied(7,rows-16);
    setOccupied(15,rows-16);
    setOccupied(7,rows-15);
    setOccupied(15,rows-15);
    setOccupied(8,rows-14);
    setOccupied(10,rows-14);
    setOccupied(12,rows-14);
    setOccupied(14,rows-14);
    setOccupied(11,rows-12);
    setOccupied(10,rows-11);
    setOccupied(11,rows-11);
    setOccupied(12,rows-11);
    setOccupied(10,rows-10);
    setOccupied(11,rows-10);
    setOccupied(12,rows-10);
  }

  return false;
}

function setRandom() {
  var confirmAction = confirm('If setting a pattern, clears current population. Continue?');

  if (confirmAction) {
    initializeTable();
    var i;
    var j;
    for (i=0; i<rows; i++) {
      for (j=0; j<cols; j++) {
        if (Math.round(Math.random())) {
          setOccupied(i,j);
        }
      }
    }
  }

  return false;
}