"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports["default"] = NoPromise;
exports.noPromise = noPromise;

/**
* This is not a `Promise`.
*
* Chain callback functions with `.then(function (res, cb))` and execute them
* as soon as previous callbacks have finished.
*
* Catch passed or thrown errors with `.catch(function (err, res, cb))` as they may occur.
* End the chain with `.end(function (err, res))`.
*
* If errors are thrown inside a `task` they are catched and can be processed attaching
* `.catch()` or `.end()` to the chain.
*
* This method is similar to {@link module:serial.connect|connect} but allows adding `tasks` on the go through chaining.
*
* @name NoPromise
* @class
* @param {Any} arg - initial argument which is passed to first chain
* @example <caption>Normal usage</caption>
* var arr = []
* var n = new NoPromise(arr)
* n.then((res, cb) => {
*   res.push(1)
*   cb(null, res)
* }).then((res, cb) => {
*   res.push(2)
*   cb(null, res)
* }).end((err, res) => {
*   //> err = null
*   //> res = [1, 2]
*   //> (arr ==== res) = true
* })
* @example <caption>Catch errors</caption>
* var arr = []
* var n = new NoPromise(arr)
* n.then((res, cb) => {
*   res.push(1)
*   cb(null, res)
* }).then((res, cb) => {
*   res.push(2)
*   cb('err1', res)             // <-- cause an error
* }).catch((err, res, cb) => {  // catches err1
*   res.push(err)
*   cb(null, res)               // <-- continue normally
* }).then((res, cb) => {
*   res.push(3)
*   cb(null, res)
* }).catch((err, res, cb) => {  // jumps over, as there is no error in the chain
*   res.push(4)
*   cb(null, res)
* }).then((res, cb) => {
*   res.push(5)
*   cb('err2', res)             // <-- next error
* }).end((err, res) => {
*   //> err = 'err2'
*   //> res = [1, 2, 'err1', 3, 5]
*   //> (arr ==== res) = true
* })
* @example <caption>Deferred usage</caption>
* var arr = []
* // creates a new instance passing `arr`
* var n = new NoPromise(arr)
* // execute the first async method
* n.then((res, cb) => {
*   res.push(1)
*   cb(null, res)
* })
* // take a time off
* setTimeout(() => {
*   // continue processing
*   n.then((res, cb) => {
*     res.push(2)
*     cb(null, res)
*   }).end((err, res) => {
*     //> err = null
*     //> res = [1, 2]
*     //> (arr ==== res) = true
*   })
* }, 10)
*/
function NoPromise(arg) {
  this._tasks = [];
  this.result = arg;
  this.error = undefined;
  this._lock = false;
}

NoPromise.prototype = {
  /**
   * runs the next function
   * @private
   */
  _run: function _run() {
    var _this = this;

    if (this._lock) return;
    this._lock = true;

    var task = this._tasks.shift();

    var tstType = this.error ? ['catch', 'end'] : ['then', 'end'];

    while (task && !~tstType.indexOf(task.type)) {
      task = this._tasks.shift();
    }

    if (task) {
      var cb = function cb(err, res) {
        _this.error = err;
        _this.result = res || _this.result;
        _this._lock = false;

        _this._run();
      };

      var fn = task.fn;

      if (task.type === 'end') {
        // .end
        fn(this.error, this.result);
      } else {
        try {
          if (task.type === 'catch') {
            // .catch
            fn(this.error, this.result, cb);
          } else {
            // .then
            fn(this.result, cb);
          }
        } catch (e) {
          cb(e);
        }
      }
    } else {
      this._lock = false;
    }
  },

  /**
   * Chain the next async function
   * @param {Function} task - async function `function (res: any, cb: Function)`.
   * Never forget to call `cb(err: <Error>, res: any)` inside `fn`
   */
  then: function then(task) {
    this._tasks.push({
      type: 'then',
      fn: task
    });

    this._run();

    return this;
  },

  /**
   * Catch any previous errors from the chain
   * @param {Function} trap - async function `function (err: <Error>, res: any, cb: Function)`.
   * Never forget to call `cb(err: <Error>, res: any)` inside `fn`
   */
  "catch": function _catch(trap) {
    this._tasks.push({
      type: 'catch',
      fn: trap
    });

    this._run();

    return this;
  },

  /**
   * End the chain
   * @param {Function} callback - `function (err: <Error>, res: any)`
   */
  end: function end(callback) {
    this._tasks.push({
      type: 'end',
      fn: callback
    });

    this._run();
  }
};
/**
* This is not a `Promise`.
*
* Chain callback functions with `.then(function (res, cb))` and execute them
* as soon as previous callbacks have finished.
*
* Catch passed or thrown errors with `.catch(function (err, res, cb))` as they may occur.
* End the chain with `.end(function (err, res))`.
*
* If errors are thrown inside a `task` they are catched and can be processed attaching
* `.catch()` or `.end()` to the chain.
*
* See full API here {@link NoPromise}.
*
* @name noPromise
* @memberOf module:serial
* @static
* @method
* @param {Any} arg - initial argument which is passed to first chain
* @return {NoPromise}
*/

function noPromise(arg) {
  return new NoPromise(arg);
}