// Grab canvas and context.
let canvas = document.getElementById('display');
let context = canvas.getContext('2d');
// Declare array to be quite large.
let list = new Array(1024);
// Flags used to determine if bg or bars need redrawn.
let updateBG = true;
let updateBars = true;

// loopBlock is used to block the program.
// It is enabled when simulation is paused or finished.
let loopBlock = false;

// Setup event listeners for window.
function setupCanvas() {
    // Setup event listeners for load and window resize.
  window.addEventListener('load', canvasResize);
  window.addEventListener('resize', canvasResize);
}

// Initializes the list of 100 random numbers.
function initList(len) {
  for (let i = 0; i < len; i++)
    list[i] = Math.round(Math.random() * 100);
}

// This function grabs the width and height of the window
// and resizes the canvas  in the event of loading or resizing.
function canvasResize() {
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
  updateBG = true;
  updateBars = true;
}

// Finds the largest number in the list.
function max(len) {
  let maximum = list[0];
  for (let i = 1; i < len; i++)
    maximum = list[i] > maximum ? list[i] : maximum;
  return maximum;
}

// cmp1 and cmp2 are two indices being compared, make them
// negative to mean a switch is happening
let cmp1 = 0;
let cmp2 = 0;
// Plot each index of list[].
function plotBars(len) {
  let barWidth = canvas.width/len;
  let barScale = canvas.height/max(len);
  for (let i = 0; i < len; i++) {
    let x = i * barWidth;
    let h = list[i] * barScale;
    let y = canvas.height - h;
    
    context.save();
    if (i == cmp1 || i == cmp2)
      context.fillStyle = '#eafbea';
    else if (i == -cmp1 || i == -cmp2)
      context.fillStyle = '#ea5e5e';
    else
      context.fillStyle = '#1f6650';
    context.fillRect(x+1, y, barWidth-2, h);
    
    context.fillStyle = 'white';
    context.font = "12px Ariel";
    context.fillText(list[i], x+4, y+12);
    
    context.restore();
  }
}

// Draws the black background.
function drawBackground(len) {
  // Save context to stack.
  context.save();
  context.fillStyle = 'black';
  context.fillRect(0, 0, canvas.width, canvas.height);
  // Pops most recently saved canvas state off stack.
  context.restore();
  
  context.fillStyle = '#1f6650';
  context.font = "30px Ariel";
  context.fillText("Bubble Sort, " + len + " numbers", 15, 25);
  context.fillStyle = 'white';
  context.fillText('Comparing - white', 15, 50);
  context.fillStyle = 'red';
  context.fillText('Swapping - red', 15, 75);
  
  
  // Testing buttons:
  context.fillStyle = 'white';
  context.fillRect(15, 90, 30, 30);
  context.fillRect(50, 90, 30, 30);
  context.fillStyle = 'black';
  context.font = "20px Ariel";
  context.fillText("<<", 17, 110);
  context.fillText(">>", 53, 110);
}

// These are some variables that flag and alter various parts 
// of the sort. You may need to add/remove/modify your own 
// depending on the  complexity of your sorting method.
let sortStarted = false;
let sortFinished = false;
let sortIndex = 1;

// Sorting function. This example uses bubble sort.
function sortStart(len, speed) {
  // Finished is used to mark when no swaps were done in a sort loop.
  // Think of it as finishing one sorting loop.
  let finished = true;
  // tmp is used as a temporary value when swapping.
  let tmp;  
  // Flip the sortStarted flag.
  sortStarted = true;
  
  // setInterval() runs once every <sortDelay>
  let sortLoop = setInterval(function() {
    // Checks for Pause button and sets loopBlock
    document.body.onkeyup = function(e){
      if(e.keyCode == 32) loopBlock = !loopBlock;
    }
    
    if (loopBlock) {
      // Paused
      context.fillStyle = 'red';
      context.font = "30px Ariel";
      context.fillText("PAUSED", 90, 115);  
    } else {
      cmp1 = sortIndex;
      cmp2 = sortIndex+1;
      // If left > right, swap
      if (list[sortIndex] < list[sortIndex - 1]) {
        // Keep setting finished just to be safe.
        finished = false;
      
        // ol-switcharoo
        tmp = list[sortIndex - 1];
        list[sortIndex - 1] = list[sortIndex];
        list[sortIndex] = tmp;
        cmp1 *= -1;
        cmp2 *= -1;
      }
      
      sortIndex++;
    
      // Reached end of list.
      // If list has been scanned once with no swaps, 
      // sortFinish will be set and sort will end
      // Otherwise start over from 1.
      if (sortIndex == len) {
        sortIndex = 1;
        if (finished) {
          sortFinished = true;
          clearInterval(sortLoop);
        } else {
          finished = true;
        }
      }
    }
    // Flag that bg and bars need redrawn
    updateBG = true;
    updateBars = true;
    // Repeat once every speed ms
  }, speed);
}

// Setup initializes the canvas as well as the list of random nums.
function setup(len) {
  setupCanvas();
  initList(len);
}

function loop(len, sortSpeed) {
  if (!loopBlock) {
    // If background needs updated, redraw background.
    if (updateBG) {
      updateBG = false; 
      drawBackground(len);
    }
    // If bars need updated, replot.
    if (updateBars) {
      updateBars = false;
      plotBars(len);
    }
    
    // Starts the sort if it isn't looping already.
    if (!sortStarted)
      sortStart(len, sortSpeed);
    // Checks if sort has completed. Briefly blocks looping while re-initializing the list, sortStarted, and sortFinished variables.
    if (sortFinished) {
      loopBlock = true;
      setTimeout(function() {
        initList(len);
        sortStarted = false;
        sortFinished = false;
        loopBlock = false;
      }, 5000);
    }
  }
}

// Main function which runs the setup and holds the main infinite loop.
(function main() {
  // Setup the canvas and list.
  var numItems = window.prompt("Enter number of items to sort:", "50");
  var speed = window.prompt("Delay (in ms) between comparasins:", "10")
  setup(numItems);
  (function mainLoop() {
    loop(numItems, speed);
    // requestAnimationFrame calls mainLoop infinitely
    requestAnimationFrame(mainLoop);
  })();
})();