// CardMatchingGame JavaScript file.

var gridSize = 4;  // i.e. 4x4
var numberOfCards = gridSize * gridSize;
var numberOfCardPairs = Math.floor(numberOfCards / 2);
var cardImageTypes = [
  "&#8682;",  // upwards arrow from bar
  "&#8984;",  // place of interest sign
  "&#9728;",  // sun with rays
  "&#9729;",  // cloud
  "&#9730;",  // umbrella
  "&#9743;",  // telephone
  // "&#9749;",  // hot beverage
  "&#9786;",  // smiling face
  "&#9818;",  // black chess king
  "&#9819;",  // black chess queen
  "&#9820;",  // black chess rook
  "&#9821;",  // black chess bishop
  "&#9822;",  // black chess knight
  "&#9823;",  // black chess pawn
  "&#9824;",  // black spade suit
  "&#9827;",  // black club suit
  "&#9829;",  // black heart suit
  "&#9830;",  // black diamond suit
  "&#9832;",  // hot springs
  "&#9834;",  // eighth notes
  "&#9836;",  // beamed sixteenth notes
  // "&#9872;",  // white flag
  // "&#9875;",  // anchor
  // "&#9881;",  // gear
  // "&#9883;",  // atom symbol
  // "&#9884;",  // fleur-de-lis
  // "&#9888;",  // warning sign
  "&#9986;",  // scissors
  "&#9990;",  // telephone location sign
  "&#9991;",  // tape drive
  "&#9992;",  // airplane
  "&#9993;",  // envelope
  "&#9997;",  // writing hand
  "&#9998;",  // "lower right" pencil
  "&#10031;", // five-point pinwheel star
  "&#10043;", // tear-drop centred asterisk
  "&#10048;", // florette
  "&#10052;", // snowflake
  "&#10086;", // floral heart
  "&sect;",   // section sign
  "&para;"    // paragraph sign
];
var unshuffledCards = shuffledCards = null;
var numberOfAttempts = 0;
var numberOfMatches = 0;
var timeStarted = timeFinished = null;
var timeoutObj = null;
var firstCardPicked = secondCardPicked = null;
var matchedCardsQueue = new Array();

// resets the game
function resetGame()
{
  // pick the card images/symbols randomly from the available types
  selectUnshuffledCards();
  // shuffle the image pairs to fill up the grid
  shuffleCards();
  // draw the cards
  initGridTableBody();
  // reset counters and timers
  numberOfAttempts = 0;
  numberOfMatches = 0;
  timeStarted = null;
  timeFinished = null;
  // update status
  updateProgressArea();
}

// creates a set of cards from the various "image" types
function selectUnshuffledCards()
{
  unshuffledCards = new Array();
  var selected = new Array(numberOfCardPairs);
  for (var i=0; i<numberOfCardPairs; i++)
  {
    selected[i] = false;
  }
  var i = 0;
  while (i < numberOfCardPairs)
  {
    var selectedIndex = Math.floor(cardImageTypes.length * Math.random());
    if (!selected[selectedIndex])
    {
      selected[selectedIndex] = true;
      unshuffledCards.push(cardImageTypes[selectedIndex]);
      unshuffledCards.push(cardImageTypes[selectedIndex]);
      i++;
    }
  }
}

// shuffles the cards
function shuffleCards()
{
  shuffledCards = new Array();
  while (unshuffledCards.length > 0)
  {
    var drawnCardIndex = Math.floor(unshuffledCards.length * Math.random());
    shuffledCards.push(unshuffledCards[drawnCardIndex]);
    unshuffledCards.splice(drawnCardIndex, 1);
  }
}

// intialises the card grid
function initGridTableBody()
{
  var gridTableBody = document.getElementById("gridTableBody");
  while (gridTableBody.hasChildNodes())
  {
     gridTableBody.removeChild(gridTableBody.lastChild);
  }
  var row = null;
  for (var i=0; i<gridSize; i++)
  {
    row = document.createElement("tr");
    for (var j=0; j<gridSize; j++)
    {
      var cell = document.createElement("td");
      cell.className = "cardFaceDown";
      cell.hiddenContent = shuffledCards[i*gridSize+j];
      cell.innerHTML = "";
      cell.onclick = function() {pickCard(this);};
      row.appendChild(cell);
    }
    gridTableBody.appendChild(row);
  }
}

// called when a card is picked
function pickCard(obj)
{
  if (!timeStarted)
  {
    timeStarted = new Date();
    timeoutObj = setTimeout("updateProgressArea();", 500);
  }

  if (!firstCardPicked)
  {
    firstCardPicked = obj;
    toggleCard(obj);
  }
  else if (firstCardPicked && !secondCardPicked && (obj != firstCardPicked))
  {
    numberOfAttempts++;
    secondCardPicked = obj;
    toggleCard(obj);
    if (firstCardPicked.innerHTML === secondCardPicked.innerHTML)
    {
      matchedCardsQueue.push(firstCardPicked, secondCardPicked);
      setTimeout("delayedRemoveMatchedCards();", 1000);
      firstCardPicked = secondCardPicked = null;
      numberOfMatches++;
      if (numberOfMatches == numberOfCardPairs)
      {
        clearTimeout(timeoutObj);
        updateProgressArea();
      }
    }
    else
    {
      // turn picked cards face down after small delay
      setTimeout("delayedTogglePickedCards();", 500);
    }
  }
}

// called when a pair of mismatched cards need to be turned face down
function delayedTogglePickedCards()
{
  toggleCard(firstCardPicked);
  firstCardPicked = null;
  toggleCard(secondCardPicked);
  secondCardPicked = null;
}

// turns over an individual card
function toggleCard(obj)
{
  if (obj.className === "cardFaceDown")
  {
    obj.className = "cardFaceUp";
    obj.innerHTML = obj.hiddenContent;
  }
  else
  {
    obj.className = "cardFaceDown";
    obj.innerHTML = "";
  }
}

// called when a pair of matched cards need to be removed from the grid
function delayedRemoveMatchedCards()
{
  while (matchedCardsQueue.length > 0)
  {
    markCardAsMatched(matchedCardsQueue.pop());
  }
}

// set a card as being "matched"
function markCardAsMatched(obj)
{
  obj.className = "cardMatched";
  obj.onclick = null;
}

// update the elapsed time and game status
function updateProgressArea()
{
  var elapsedDisplayCell = document.getElementById("elapsedDisplayCell");
  if (timeStarted)
  {
    if (numberOfMatches == numberOfCardPairs)
    {
      if (!timeFinished) timeFinished = new Date();
      elapsedDisplayCell.innerHTML = "" + ((timeFinished - timeStarted) / 1000.0).toFixed(2);
    }
    else
    {
      elapsedDisplayCell.innerHTML = "" + Math.floor((new Date() - timeStarted) / 1000.0);
      timeoutObj = setTimeout("updateProgressArea();", 500);
    }
  }
  else
  {
    elapsedDisplayCell.innerHTML = "0";
  }
  elapsedDisplayCell.innerHTML += " seconds";
  var attemptsDisplayCell = document.getElementById("attemptsDisplayCell");
  attemptsDisplayCell.innerHTML = numberOfAttempts;
  if (numberOfAttempts > 0)
  {
    var percentage = (numberOfMatches / numberOfAttempts) * 100.0;
    attemptsDisplayCell.innerHTML += "&nbsp; (" + percentage.toFixed(2) + "%)";
  }
  var matchesDisplayCell = document.getElementById("matchesDisplayCell");
  if (timeStarted)
  {
    matchesDisplayCell.innerHTML = "Found " + numberOfMatches + " of " + numberOfCardPairs + " pairs.";
  }
  else
  {
    matchesDisplayCell.innerHTML = "Select a card to start.";
  }
  if (numberOfMatches == numberOfCardPairs)
  {
    matchesDisplayCell.innerHTML += "<br>Completed!";
    alert("Congratulations, all pairs matched! Time taken: " + ((timeFinished - timeStarted) / 1000.0).toFixed(2) + " seconds.");
  }
}
