var _ = require('lodash');
var $ = require('preconditions').singleton();

function Lock() {
  this.tasks = {};
};

Lock.prototype._release = function(token, task) {
  if (!task.running) return;
  task.running = false;
  this.tasks[token] = _.without(this.tasks[token], task);
  this._runOne(token);
};

Lock.prototype._runOne = function(token) {
  var self = this;

  if (_.any(self.tasks[token], {
    running: true
  })) return;

  var task = _.first(self.tasks[token]);
  if (!task) return;

  task.running = true;

  if (task.timeout > 0) {
    setTimeout(function() {
      self._release(token, task);
    }, task.timeout);
  }

  task.fn(null, function() {
    self._release(token, task);
  });
};

Lock.prototype.locked = function(token, wait, max, userTask) {
  var self = this;

  if (_.isUndefined(self.tasks[token])) {
    self.tasks[token] = [];
  }

  var task = {
    timeout: max,
    running: false,
    fn: userTask,
  };
  self.tasks[token].push(task);

  if (wait > 0) {
    setTimeout(function() {
      if (task.running || !_.contains(self.tasks[token], task)) return;
      self.tasks[token] = _.without(self.tasks[token], task);
      task.fn(new Error('Could not acquire lock ' + token));
    }, wait);
  }

  self._runOne(token);
};

module.exports = Lock;