var DEFAULT_RESOLUTION = 5;

Heatmap.prototype.installInputHandlers = function(canvas, resolutionSlider) {
  var self = this;

  canvas.mousemove(function(event) {
    var mouseX = event.pageX - canvas.position().left;
    var mouseY = event.pageY - canvas.position().top;
    self.draw();
    var traces = self.findTracesAt(mouseX, mouseY);
    for (var i = 0; i < traces.length; ++i)
      self.drawTrace(traces[i], 0.5);
  });

  canvas.click(function(event) {
    var mouseX = event.pageX - canvas.position().left;
    var mouseY = event.pageY - canvas.position().top;
    var traces = self.findTracesAt(mouseX, mouseY);
    for (var i = 0; i < traces.length; ++i)
      self.selectTrace(traces[i]);
    self.draw();
  });

  resolutionSlider.val(DEFAULT_RESOLUTION);
  resolutionSlider.change(function() {
    self.resolution = self.h / this.value;
    self.calculateHeatmap();
    self.draw();
  });
  resolutionSlider.change();
};

Heatmap.prototype.selectTrace = function(trace) {
  this.drawTraces[trace] = !this.drawTraces[trace];
};

Heatmap.prototype.getTime = function(x) {
  var time = Math.floor(mapRange(x, 0, this.w, 0, this.revisions.length));
  return constrain(time, 0, this.revisions.length - 1);
};

Heatmap.prototype.getBucket = function(y) {
  var bucket = Math.floor(mapRange(y, this.h, 0, 0, this.resolution));
  return constrain(bucket, 0, this.resolution - 1);
};

Heatmap.prototype.findTracesAt = function(x, y) {
  var minX = x - 5, minY = y + 5;
  var maxX = x + 5, maxY = y - 5;

  var minTime = this.getTime(minX), minY = this.getBucket(minY);
  var maxTime = this.getTime(maxX), maxY = this.getBucket(maxY);

  var traces = {};  // Use an object to avoid duplicates.
  for (var time = minTime; time <= maxTime; ++time) {
    for (var bucket = minY; bucket <= maxY; ++bucket) {
      var revision = this.revisions[time];
      if (!this.data[revision])
        continue;
      if (!this.data[revision][bucket])
        continue;

      for (var i = 0; i < this.data[revision][bucket].length; ++i)
        traces[this.data[revision][bucket][i]] = true;
    }
  }

  return Object.keys(traces);
};