"use strict";

let exp = module.exports;

/**
 * Count down to zero or timeout and invoke cb finally.
 * 
 * @class
 */
let CountDownLatch = function(count, opts, cb) {
  this.count = count;
  this.cb = cb;
  let self = this;
  if (opts.timeout) {
    this.timerId = setTimeout(function() {
      self.cb(true);
    }, opts.timeout);
  }
};

/**
 * Call when a task finish to count down.
 *
 * @api public
 */
CountDownLatch.prototype.done = function() {
  if(this.count <= 0) {
    throw new Error('illegal state.');
  }

  this.count--;
  if (this.count === 0) {
    if (this.timerId) {
      clearTimeout(this.timerId);
    }
    this.cb();
  }
};

/**
 * Create a count down latch
 *
 * @param {Integer} count
 * @param {Object} opts, opts.timeout indicates timeout, optional param
 * @param {Function} cb, cb(isTimeout)
 *
 * @api public
 */
exp.createCountDownLatch = function(count, opts, cb) {
  if(!count || count <= 0) {
    throw new Error('count should be positive.');
  }

  if (!cb && typeof opts === 'function') {
    cb = opts;
    opts = {};
  }

  if(typeof cb !== 'function') {
    throw new Error('cb should be a function.');
  }

  return new CountDownLatch(count, opts, cb);
};