var RedditVotes = (function() {
  
  var urls2colors = null;
  var animation = ANIMATION;
  var animationIndex = 0;
  var stop = false;
  var snapshotLength = -1;

  var startButton;
  var stepButton;
  var resetButton;
  var msgSpan;

  const SLEEP = 70;
  const PERIOD = 5;

  function getColor(url) {
    var res = urls2colors[url];
    return res ? res : '#fffccc';
  }

  /**
   * Creates the initial map from urls to colors
   */
  function createUrls2colors() {
    var res = {}
    var urls2rankss = animation;
    var urls2ranks = urls2rankss[0];
    var len = 0;
    for (var i in urls2ranks) len++;
    snapshotLength = len;
    note('createUrls2colors: len=' + len);
    var inc = Math.round(0xff/(len-1))-2;
    var red = 0xff-inc;
    for (var url in urls2ranks) {
      var color = '#' + red.toString(16) + '0000';
      res[url] = color;
      red -= inc;
    }
    return res;
  }

  /**
   * Creates the bar divs for the given snapshot index and returns the
   * number of bars.
   */
  function createSnapshot(index) {
    note('createSnapshot: index=' + index);
    var urls2rankss = animation;
    var urls2ranks = urls2rankss[index];
    var bars = document.getElementById('bars');
    bars.innerHTML = '';
    var res = 0;
    for (var url in urls2ranks) {
      var rank = urls2ranks[url];
      var color = getColor(url);
      var title = url;
      var div = document.createElement('div');
      div.alt = title;
      div.title = title;
      div.className = 'bar';
      if (res == 0) {
	div.className += ' top';
      }
      div.style.background = color;
      div.onclick = (function() {
	var _url = url;
	return function(e) {
	  window.open(_url);
	  return true;
	};
      })();
      var a = document.createElement('a');
      a.target = '_';
      a.href = '#' + url;
      a.innerHTML = (rank == -1 ? '--' : rank);
      div.appendChild(a);
      bars.appendChild(div);
      res++;
    }
    return res;
  }

  function note(msg) {
    try {
      console.log(msg);
    } catch (e) {}
  }

  /*
   * -> Boolean
   *
   * Returns whether we could take one step in the animation.  If so, we
   * paint the new bars.
   */
  function doStepAnimation() {
    note('stepAnimation: animationIndex=' + animationIndex +
	 ' animation.length=' + animation.length);
    if (animationIndex >= animation.length-1) {
      return false;
    }
    status(formatTime(animationIndex*PERIOD));
    createSnapshot(++animationIndex);
    return true;
  }

  function pad(v) {
    if (v < 10) {
      return '0' + v;
    }
    return String(v);
  }

  function formatTime(minutes) {
    var hours, mins, hoursStr, minsStr;
    if (minutes >= 60) {
      hours = Math.floor(minutes / 60);
      mins = minutes % 60;
    } else {
      hours = 0;
      mins = minutes;
    }
    hoursStr = pad(hours);
    minsStr = pad(mins);
    return hoursStr + ':' + minsStr;
  }

  /**
   * -> Int
   *
   * Returns number of total minutes for all snapshots
   */
  function numMinutes() {
    return animation.length * PERIOD;
  }

  function status(msg) {
    msgSpan.innerHTML = msg;
  }

  /**
   * f can be null
   */
  function setButton(button,name,f) {
    f = f || function(e) {};
    button.innerHTML = name;
    button.onclick = f;
  }

  /**
   * Resets all the buttons to defaults.  If skipStart is true we don't
   * set the start button, because we will set it for ourselves after.
   */
  function resetButtons(skipStart) {
    if (!skipStart) {
      setButton(startButton,'Start',startAnimation);
    }
    setButton(stepButton,'Step',stepAnimation);
    setButton(resetButton,'Reset',resetAnimation);
  }

  // ----------------------------------------------------------------------
  // Event listener for the buttons
  // ----------------------------------------------------------------------

  /**
   * Event listener for the Start button when it shows 'Continue'
   */
  function continueAnimation() {
    if (stop) {
      stop = false;
      resetButtons(true);
      setButton(startButton,'Continue',continueAnimation);
      return;
    }
    var done = !doStepAnimation();
    if (done) {
      status('Done at ' + formatTime(numMinutes()));
      resetButtons(true);
      setButton(startButton,'Restart',startAnimation);
      return;
    }
    setButton(startButton,'Stop',stopAnimation);
    setTimeout(continueAnimation,SLEEP);
  }

  /**
   * Event listener for the Reset button
   */
  function resetAnimation() {
    status('Have ' + formatTime(numMinutes()));
    createSnapshot(0);
    animationIndex = 0;
    resetButtons();
  }

  /**
   * Event listener for the Start button when it shows 'Start'
   */
  function startAnimation() {
    status('Starting...');
    resetAnimation();
    setButton(startButton,'Stop',stopAnimation);
    setButton(stepButton,'Step');
    setButton(resetButton,'Reset');
    continueAnimation();
  }

  /**
   * Event listener for the Start button when it shows 'Stop'
   */
  function stopAnimation() {
    status('Stopping...');
    stop = true;
    resetButtons(true);
    setButton(startButton,'Continue',continueAnimation);
  }

  /**
   * Event listener for the Step button
   */ 
  function stepAnimation() {
    if (doStepAnimation()) {
      setButton(startButton,'Continue',continueAnimation);
    } else {
      alert('Already at the end');
    }
  }

  function onLoad() {
    urls2colors = createUrls2colors();
    startButton = document.getElementById('startButton');
    stepButton = document.getElementById('stepButton');
    resetButton = document.getElementById('resetButton');
    msgSpan = document.getElementById('msg');
    resetAnimation();
    resetButtons();
    status('Click \'Start\' to begin');
  }

  return {
    onLoad: onLoad
  };

})();
