'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _regenerator = require('babel-runtime/regenerator');

var _regenerator2 = _interopRequireDefault(_regenerator);

var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');

var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);

var _typeof2 = require('babel-runtime/helpers/typeof');

var _typeof3 = _interopRequireDefault(_typeof2);

var _extends2 = require('babel-runtime/helpers/extends');

var _extends3 = _interopRequireDefault(_extends2);

var _createClass2 = require('babel-runtime/helpers/createClass');

var _createClass3 = _interopRequireDefault(_createClass2);

var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');

var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);

var _inherits2 = require('babel-runtime/helpers/inherits');

var _inherits3 = _interopRequireDefault(_inherits2);

var _eventemitter = require('eventemitter3');

var _eventemitter2 = _interopRequireDefault(_eventemitter);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var empty = function empty() {}; /**
                                  * @文件说明: 定义Promise同步执行队列
                                  * @创建人: pjtools@vip.qq.com
                                  * @创建时间: 2020-03-11 17:55:22
                                  */

var pFinally = function pFinally(promise, onFinally) {
  onFinally = onFinally || function () {};

  return promise.then(function (val) {
    return new Promise(function (resolve) {
      resolve(onFinally());
    }).then(function () {
      return val;
    });
  }, function (err) {
    return new Promise(function (resolve) {
      resolve(onFinally());
    }).then(function () {
      throw err;
    });
  });
};

var TimeoutError = function (_Error) {
  (0, _inherits3['default'])(TimeoutError, _Error);

  function TimeoutError(message) {
    (0, _classCallCheck3['default'])(this, TimeoutError);

    var _this = (0, _possibleConstructorReturn3['default'])(this, (TimeoutError.__proto__ || Object.getPrototypeOf(TimeoutError)).call(this, message));

    _this.name = 'TimeoutError';
    return _this;
  }

  return TimeoutError;
}(Error);

var pTimeout = function pTimeout(promise, milliseconds, fallback) {
  return new Promise(function (resolve, reject) {
    if (typeof milliseconds !== 'number' || milliseconds < 0) {
      throw new TypeError('Expected `milliseconds` to be a positive number');
    }

    if (milliseconds === Infinity) {
      resolve(promise);
      return;
    }

    var timer = setTimeout(function () {
      if (typeof fallback === 'function') {
        try {
          resolve(fallback());
        } catch (error) {
          reject(error);
        }
        return;
      }

      var message = typeof fallback === 'string' ? fallback : 'Promise timed out after ' + milliseconds + ' milliseconds';
      var timeoutError = fallback instanceof Error ? fallback : new TimeoutError(message);

      if (typeof promise.cancel === 'function') {
        promise.cancel();
      }

      reject(timeoutError);
    }, milliseconds);

    pFinally(promise.then(resolve, reject), function () {
      clearTimeout(timer);
    });
  });
};

function lowerBound(array, value, comparator) {
  var first = 0;
  var count = array.length;
  while (count > 0) {
    var step = count / 2 | 0;
    var it = first + step;
    if (comparator(array[it], value) <= 0) {
      first = ++it;
      count -= step + 1;
    } else {
      count = step;
    }
  }
  return first;
}

var PriorityQueue = function () {
  function PriorityQueue() {
    (0, _classCallCheck3['default'])(this, PriorityQueue);

    this._queue = [];
  }

  (0, _createClass3['default'])(PriorityQueue, [{
    key: 'enqueue',
    value: function enqueue(run, options) {
      options = (0, _extends3['default'])({ priority: 0 }, options);
      var element = {
        priority: options.priority,
        run: run
      };
      if (this.size && this._queue[this.size - 1].priority >= options.priority) {
        this._queue.push(element);
        return;
      }
      var index = lowerBound(this._queue, element, function (a, b) {
        return b.priority - a.priority;
      });
      this._queue.splice(index, 0, element);
    }
  }, {
    key: 'dequeue',
    value: function dequeue() {
      var item = this._queue.shift();
      return item && item.run;
    }
  }, {
    key: 'filter',
    value: function filter(options) {
      return this._queue.filter(function (element) {
        return element.priority === options.priority;
      }).map(function (element) {
        return element.run;
      });
    }
  }, {
    key: 'size',
    get: function get() {
      return this._queue.length;
    }
  }]);
  return PriorityQueue;
}();

var PromiseQueue = function (_EventEmitter) {
  (0, _inherits3['default'])(PromiseQueue, _EventEmitter);

  function PromiseQueue(options) {
    (0, _classCallCheck3['default'])(this, PromiseQueue);

    var _this2 = (0, _possibleConstructorReturn3['default'])(this, (PromiseQueue.__proto__ || Object.getPrototypeOf(PromiseQueue)).call(this));

    _this2._carryoverConcurrencyCount = void 0;
    _this2._isIntervalIgnored = void 0;
    _this2._intervalCount = 0;
    _this2._intervalCap = void 0;
    _this2._interval = void 0;
    _this2._intervalEnd = 0;
    _this2._intervalId = void 0;
    _this2._timeoutId = void 0;
    _this2._queue = void 0;
    _this2._queueClass = void 0;
    _this2._pendingCount = 0;
    _this2._concurrency = void 0;
    _this2._isPaused = void 0;
    _this2._resolveEmpty = empty;
    _this2._resolveIdle = empty;
    _this2._timeout = void 0;
    _this2._throwOnTimeout = void 0;

    options = (0, _extends3['default'])({
      carryoverConcurrencyCount: false,
      intervalCap: Infinity,
      interval: 0,
      concurrency: Infinity,
      autoStart: true,
      queueClass: PriorityQueue
    }, options);
    if (!(typeof options.intervalCap === 'number' && options.intervalCap >= 1)) {
      throw new TypeError('Expected `intervalCap` to be a number from 1 and up, got `' + options.intervalCap + '` (' + (0, _typeof3['default'])(options.intervalCap) + ')');
    }
    if (options.interval === undefined || !(Number.isFinite(options.interval) && options.interval >= 0)) {
      throw new TypeError('Expected `interval` to be a finite number >= 0, got `' + options.interval + '` (' + (0, _typeof3['default'])(options.interval) + ')');
    }
    _this2._carryoverConcurrencyCount = options.carryoverConcurrencyCount;
    _this2._isIntervalIgnored = options.intervalCap === Infinity || options.interval === 0;
    _this2._intervalCap = options.intervalCap;
    _this2._interval = options.interval;
    _this2._queue = new options.queueClass();
    _this2._queueClass = options.queueClass;
    _this2.concurrency = options.concurrency;
    _this2._timeout = options.timeout;
    _this2._throwOnTimeout = options.throwOnTimeout === true;
    _this2._isPaused = options.autoStart === false;
    return _this2;
  }

  (0, _createClass3['default'])(PromiseQueue, [{
    key: '_next',
    value: function _next() {
      this._pendingCount--;
      this._tryToStartAnother();
    }
  }, {
    key: '_resolvePromises',
    value: function _resolvePromises() {
      this._resolveEmpty();
      this._resolveEmpty = empty;
      if (this._pendingCount === 0) {
        this._resolveIdle();
        this._resolveIdle = empty;
      }
    }
  }, {
    key: '_onResumeInterval',
    value: function _onResumeInterval() {
      this._onInterval();
      this._initializeIntervalIfNeeded();
      this._timeoutId = undefined;
    }
  }, {
    key: '_isIntervalPaused',
    value: function _isIntervalPaused() {
      var _this3 = this;

      var now = Date.now();
      if (this._intervalId === undefined) {
        var delay = this._intervalEnd - now;
        if (delay < 0) {
          this._intervalCount = this._carryoverConcurrencyCount ? this._pendingCount : 0;
        } else {
          if (this._timeoutId === undefined) {
            this._timeoutId = setTimeout(function () {
              _this3._onResumeInterval();
            }, delay);
          }
          return true;
        }
      }
      return false;
    }
  }, {
    key: '_tryToStartAnother',
    value: function _tryToStartAnother() {
      if (this._queue.size === 0) {
        if (this._intervalId) {
          clearInterval(this._intervalId);
        }
        this._intervalId = undefined;
        this._resolvePromises();
        return false;
      }
      if (!this._isPaused) {
        var canInitializeInterval = !this._isIntervalPaused();
        if (this._doesIntervalAllowAnother && this._doesConcurrentAllowAnother) {
          this.emit('active');
          this._queue.dequeue()();
          if (canInitializeInterval) {
            this._initializeIntervalIfNeeded();
          }
          return true;
        }
      }
      return false;
    }
  }, {
    key: '_initializeIntervalIfNeeded',
    value: function _initializeIntervalIfNeeded() {
      var _this4 = this;

      if (this._isIntervalIgnored || this._intervalId !== undefined) {
        return;
      }
      this._intervalId = setInterval(function () {
        _this4._onInterval();
      }, this._interval);
      this._intervalEnd = Date.now() + this._interval;
    }
  }, {
    key: '_onInterval',
    value: function _onInterval() {
      if (this._intervalCount === 0 && this._pendingCount === 0 && this._intervalId) {
        clearInterval(this._intervalId);
        this._intervalId = undefined;
      }
      this._intervalCount = this._carryoverConcurrencyCount ? this._pendingCount : 0;
      this._processQueue();
    }
  }, {
    key: '_processQueue',
    value: function _processQueue() {
      while (this._tryToStartAnother()) {}
    }
  }, {
    key: 'add',

    // 添加异步Promise到队列
    value: function () {
      var _ref = (0, _asyncToGenerator3['default'])( /*#__PURE__*/_regenerator2['default'].mark(function _callee2(fn) {
        var _this5 = this;

        var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
        return _regenerator2['default'].wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                return _context2.abrupt('return', new Promise(function (resolve, reject) {
                  var run = function () {
                    var _ref2 = (0, _asyncToGenerator3['default'])( /*#__PURE__*/_regenerator2['default'].mark(function _callee() {
                      var operation;
                      return _regenerator2['default'].wrap(function _callee$(_context) {
                        while (1) {
                          switch (_context.prev = _context.next) {
                            case 0:
                              _this5._pendingCount++;
                              _this5._intervalCount++;
                              _context.prev = 2;
                              operation = _this5._timeout === undefined && options.timeout === undefined ? fn() : pTimeout(Promise.resolve(fn()), options.timeout === undefined ? _this5._timeout : options.timeout, function () {
                                if (options.throwOnTimeout === undefined ? _this5._throwOnTimeout : options.throwOnTimeout) {
                                  reject(timeoutError);
                                }
                                return undefined;
                              });
                              _context.t0 = resolve;
                              _context.next = 7;
                              return operation;

                            case 7:
                              _context.t1 = _context.sent;
                              (0, _context.t0)(_context.t1);
                              _context.next = 14;
                              break;

                            case 11:
                              _context.prev = 11;
                              _context.t2 = _context['catch'](2);

                              reject(_context.t2);

                            case 14:
                              _this5._next();

                            case 15:
                            case 'end':
                              return _context.stop();
                          }
                        }
                      }, _callee, _this5, [[2, 11]]);
                    }));

                    return function run() {
                      return _ref2.apply(this, arguments);
                    };
                  }();
                  _this5._queue.enqueue(run, options);
                  _this5._tryToStartAnother();
                }));

              case 1:
              case 'end':
                return _context2.stop();
            }
          }
        }, _callee2, this);
      }));

      function add(_x2) {
        return _ref.apply(this, arguments);
      }

      return add;
    }()
    // 添加一组异步Promise到队列

  }, {
    key: 'addAll',
    value: function () {
      var _ref3 = (0, _asyncToGenerator3['default'])( /*#__PURE__*/_regenerator2['default'].mark(function _callee4(functions, options) {
        var _this6 = this;

        return _regenerator2['default'].wrap(function _callee4$(_context4) {
          while (1) {
            switch (_context4.prev = _context4.next) {
              case 0:
                return _context4.abrupt('return', Promise.all(functions.map(function () {
                  var _ref4 = (0, _asyncToGenerator3['default'])( /*#__PURE__*/_regenerator2['default'].mark(function _callee3(function_) {
                    return _regenerator2['default'].wrap(function _callee3$(_context3) {
                      while (1) {
                        switch (_context3.prev = _context3.next) {
                          case 0:
                            return _context3.abrupt('return', _this6.add(function_, options));

                          case 1:
                          case 'end':
                            return _context3.stop();
                        }
                      }
                    }, _callee3, _this6);
                  }));

                  return function (_x5) {
                    return _ref4.apply(this, arguments);
                  };
                }())));

              case 1:
              case 'end':
                return _context4.stop();
            }
          }
        }, _callee4, this);
      }));

      function addAll(_x3, _x4) {
        return _ref3.apply(this, arguments);
      }

      return addAll;
    }()
    // 开始执行队列

  }, {
    key: 'start',
    value: function start() {
      if (!this._isPaused) {
        return this;
      }
      this._isPaused = false;
      this._processQueue();
      return this;
    }
    // 暂停当前队列

  }, {
    key: 'pause',
    value: function pause() {
      this._isPaused = true;
    }
    // 清空当前队列

  }, {
    key: 'clear',
    value: function clear() {
      this._queue = new this._queueClass();
    }
    // 当前队列执行空后进行响应

  }, {
    key: 'onEmpty',
    value: function () {
      var _ref5 = (0, _asyncToGenerator3['default'])( /*#__PURE__*/_regenerator2['default'].mark(function _callee5() {
        var _this7 = this;

        return _regenerator2['default'].wrap(function _callee5$(_context5) {
          while (1) {
            switch (_context5.prev = _context5.next) {
              case 0:
                if (!(this._queue.size === 0)) {
                  _context5.next = 2;
                  break;
                }

                return _context5.abrupt('return');

              case 2:
                return _context5.abrupt('return', new Promise(function (resolve) {
                  var existingResolve = _this7._resolveEmpty;
                  _this7._resolveEmpty = function () {
                    existingResolve();
                    resolve();
                  };
                }));

              case 3:
              case 'end':
                return _context5.stop();
            }
          }
        }, _callee5, this);
      }));

      function onEmpty() {
        return _ref5.apply(this, arguments);
      }

      return onEmpty;
    }()
    // 当前队列执行完毕（包括数据响应处理完毕）后进行响应

  }, {
    key: 'onIdle',
    value: function () {
      var _ref6 = (0, _asyncToGenerator3['default'])( /*#__PURE__*/_regenerator2['default'].mark(function _callee6() {
        var _this8 = this;

        return _regenerator2['default'].wrap(function _callee6$(_context6) {
          while (1) {
            switch (_context6.prev = _context6.next) {
              case 0:
                if (!(this._pendingCount === 0 && this._queue.size === 0)) {
                  _context6.next = 2;
                  break;
                }

                return _context6.abrupt('return');

              case 2:
                return _context6.abrupt('return', new Promise(function (resolve) {
                  var existingResolve = _this8._resolveIdle;
                  _this8._resolveIdle = function () {
                    existingResolve();
                    resolve();
                  };
                }));

              case 3:
              case 'end':
                return _context6.stop();
            }
          }
        }, _callee6, this);
      }));

      function onIdle() {
        return _ref6.apply(this, arguments);
      }

      return onIdle;
    }()
  }, {
    key: 'sizeBy',
    value: function sizeBy(options) {
      return this._queue.filter(options).length;
    }
  }, {
    key: '_doesIntervalAllowAnother',
    get: function get() {
      return this._isIntervalIgnored || this._intervalCount < this._intervalCap;
    }
  }, {
    key: '_doesConcurrentAllowAnother',
    get: function get() {
      return this._pendingCount < this._concurrency;
    }
  }, {
    key: 'concurrency',
    get: function get() {
      return this._concurrency;
    },
    set: function set(newConcurrency) {
      if (!(typeof newConcurrency === 'number' && newConcurrency >= 1)) {
        throw new TypeError('Expected `concurrency` to be a number from 1 and up, got `' + newConcurrency + '` (' + (typeof newConcurrency === 'undefined' ? 'undefined' : (0, _typeof3['default'])(newConcurrency)) + ')');
      }
      this._concurrency = newConcurrency;
      this._processQueue();
    }
  }, {
    key: 'size',
    get: function get() {
      return this._queue.size;
    }
  }, {
    key: 'pending',
    get: function get() {
      return this._pendingCount;
    }
  }, {
    key: 'isPaused',
    get: function get() {
      return this._isPaused;
    }
  }, {
    key: 'timeout',
    set: function set(milliseconds) {
      this._timeout = milliseconds;
    },
    get: function get() {
      return this._timeout;
    }
  }]);
  return PromiseQueue;
}(_eventemitter2['default']);

exports['default'] = PromiseQueue;