// Change ITEMS for amount of bars.
let ITEMS = 50;

// Grab canvas and context.
let canvas = document.getElementById('display');
let context = canvas.getContext('2d');

// Declare array to be ITEMS long.
let list = null;
let history = null;
let showing = 0;
let comparison_count = 0;
let swap_count = 0;

// adapted from https://en.wikipedia.org/wiki/Comb_sort
let gap = ITEMS
let shrink = 1.3

// Flags used to determine if bg or bars need redrawn.
let updateBG = true;
let updateBars = true;

let paused = false;

// Delay between comparasins.
let sortDelay = 100;
let sortDelay_interval = null;

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

function keydownEvent(ev) {
    var k = ev.key;
    if (k == ' ') {
	paused = !paused;
    }
    else if (k == 'ArrowUp' && ! paused) { // up
	sortDelay /= 2;
	if (sortDelay < 1) sortDelay = 1
	clearInterval(sortDelay_interval);
	sortDelay_interval = setInterval(sortLoop, sortDelay);
    }
    else if (k == 'ArrowDown' && ! paused) { // down
	sortDelay *= 2;
	if (sortDelay < 1) sortDelay = 1
	clearInterval(sortDelay_interval);
	sortDelay_interval = setInterval(sortLoop, sortDelay);
    }
    else if (k == 'ArrowLeft') {
	if (showing > 0) showing--;
    }
    else if (k == 'ArrowRight') {
	if (showing+1 < history.length)
	    showing++;
    }
    updateBG = true;
    updateBars = true;
}

// Initializes the list of 100 random numbers.
function initList() {
    list = new Array(ITEMS);
    history = new Array(0);
    for (let i = 0; i < ITEMS; i++) {
	list[i] = Math.round(Math.random() * 100);
    }
    comparison_count = 0;
    swap_count = 0;
    gap = ITEMS
    history = new Array(0);
    history.push(list.slice(0));
    showing = 0;
}

// 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;
    // removed the following two lines to make printing text work properly (not inverted)
  //context.translate(0, canvas.height);
  //  context.scale(1, -1);
  updateBG = true;
  updateBars = true;
}

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

// Comparing indicators to draw in different colors.
// 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() {
    var list = history[showing];
    
    let length = list.length;
    let barWidth = canvas.width / length;
    let barScale = canvas.height / max();
    for (let i = 0; i < ITEMS; i++) {
	let x = i * barWidth;
	let h = list[i] * barScale;
	let y = canvas.height - h;
	
	context.save();
	if (i == cmp1 || i == cmp2)
	    context.fillStyle = 'white';
	else if (i == -cmp1 || i == -cmp2)
	    context.fillStyle = 'yellow';
	else
	    context.fillStyle = 'orange';
	context.fillRect(x+1, y, barWidth-2, h);
	context.restore();
    }
}

// Draws the black background.
function drawBackground() {
  // 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 = 'grey';
    context.font = "30px Ariel";
    context.fillText("Comb Sort, " + ITEMS + " numbers", 30, 25);
    
    context.fillStyle = 'white';
    context.fillText('bars being compared - white', 30, 50);
    context.fillStyle = 'yellow';
    context.fillText('bars being swapped - yellow', 30, 75);
    
    context.fillStyle = 'grey';
    context.fillText('# comparisons so far - ' + comparison_count, 30, 100);
    context.fillText('# swaps so far - ' + swap_count, 30, 125);
    context.fillText('sortDelay - ' + sortDelay + 'ms', 30, 150);
    context.fillText('showing - ' + showing, 30, 175);

    context.fillText('space - pause, up/down - speed, left/right - history while paused',
		     400, 25);
}

// 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;

// Delay between sorting simulations.
let restartDelay = 2000;

let sortIndex = 0;


function sortLoop() {
    if (! paused) {
    
	// If left > right, swap
	comparison_count = comparison_count + 1;
	
	cmp1 = sortIndex
	cmp2 = sortIndex + gap;
	
	if (cmp2 < ITEMS &&
	    list[cmp1] > list[cmp2]) {
	    swap_count += 1;
            // Keep setting finished just to be safe.
	    finished = false;
	    
	    // ol-switcharoo
	    tmp = list[cmp2];
	    list[cmp2] = list[cmp1];
	    list[cmp1] = tmp;
	    cmp1 *= -1; // flags that a swap is being done
	    cmp2 *= -1; // flags that a swap is being done
	    
	}
	// Flag that the background and bars need updating.
	updateBG = true;
	updateBars = true;

	history.push(list.slice(0));
	showing = history.length - 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 + gap >= ITEMS) {
	    if (finished) {
		sortFinished = true;
		clearInterval(sortLoop);
	    } 
	    sortIndex = 0;
	    gap = Math.floor(gap / shrink)
	    if (gap <= 1) {
		gap = 1;
		finished = true;
	    }
	}
    }
    // Repeat once every <sortDelay>.
}


// Sorting function. This example uses bubble sort.
function sortStart() {
  // Finished is used to mark when no swaps were done in a sort loop.
  // Think of it as finishing one sorting loop.
  let finished = false; // note - set to false except for when gap is 1

  // tmp is used as a temporary value when swapping.
  let tmp;
    
  // Flip the sortStarted flag.
  sortStarted = true;

    gap = Math.floor(gap / shrink)
    if (gap <= 1) {
	// for gap=1, keep track of if there were any swaps
	gap = 1
	finished = true
    }
    
    // setInterval() runs once every <sortDelay>
    sortDelay_interval = setInterval(sortLoop, sortDelay);
}


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

// loopBlock is briefly enabled when sorting has finished to give a view of the finished sort.
let loopBlock = false;
function loop() {
  if (!loopBlock) {
    // If background needs updated, redraw background.
    if (updateBG) {
      updateBG = false; 
      drawBackground();
    }
    // If bars need updated, replot.
    if (updateBars) {
      updateBars = false;
      plotBars();
    }
    
    // Starts the sort if it isn't looping already.
    if (!sortStarted)
      sortStart();
    // Checks if sort has completed. Briefly blocks looping while re-initializing the list, sortStarted, and sortFinished variables.
    if (sortFinished) {
	loopBlock = true;
	clearInterval(sortDelay_interval); sortDelay_interval = null;
	setTimeout(function() {
            initList();
            sortStarted = false;
            sortFinished = false;
            loopBlock = false;
	}, restartDelay);
    }
  }
}

// Main function which runs the setup and holds the main infinite loop.
(function main() {
    var x = window.prompt('How many items?', '50')
    ITEMS = x;
    // window.alert()...
    // Setup the canvas and list.
    setup();
    (function mainLoop() {
	loop();
	// requestAnimationFrame calls mainLoop infinitely
	requestAnimationFrame(mainLoop);
    })();
})();