/* eslint-disable no-inner-declarations */
// https://github.com/timjansen/PinkySwear.js/blob/fa78f9799868893101b0960ef977ffa3900e9a94/pinkyswear.js
// Modified to remove UMD

/*
 * PinkySwear.js 2.2.2 - Minimalistic implementation of the Promises/A+ spec
 *
 * Public Domain. Use, modify and distribute it any way you like. No attribution required.
 *
 * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
 *
 * PinkySwear is a very small implementation of the Promises/A+ specification. After compilation with the
 * Google Closure Compiler and gzipping it weighs less than 500 bytes. It is based on the implementation for
 * Minified.js and should be perfect for embedding.
 *
 *
 * PinkySwear has just three functions.
 *
 * To create a new promise in pending state, call pinkySwear():
 *         var promise = pinkySwear();
 *
 * The returned object has a Promises/A+ compatible then() implementation:
 *          promise.then(function(value) { alert("Success!"); }, function(value) { alert("Failure!"); });
 *
 *
 * The promise returned by pinkySwear() is a function. To fulfill the promise, call the function with true as first argument and
 * an optional array of values to pass to the then() handler. By putting more than one value in the array, you can pass more than one
 * value to the then() handlers. Here an example to fulfill a promsise, this time with only one argument:
 *         promise(true, [42]);
 *
 * When the promise has been rejected, call it with false. Again, there may be more than one argument for the then() handler:
 *         promise(true, [6, 6, 6]);
 *
 * You can obtain the promise's current state by calling the function without arguments. It will be true if fulfilled,
 * false if rejected, and otherwise undefined.
 * 		   var state = promise();
 *
 * https://github.com/timjansen/PinkySwear.js
 */
var undef;

function isFunction(f) {
  return typeof f == "function";
}
function isObject(f) {
  return typeof f == "object";
}
function defer(callback) {
  if (typeof setImmediate != "undefined") setImmediate(callback);
  else if (typeof process != "undefined" && process["nextTick"])
    process["nextTick"](callback);
  else setTimeout(callback, 0);
}

function pinkySwear(extend) {
  var state; // undefined/null = pending, true = fulfilled, false = rejected
  var values = []; // an array of values as arguments for the then() handlers
  var deferred = []; // functions to call when set() is invoked

  var set = function (newState, newValues) {
    if (state == null && newState != null) {
      state = newState;
      values = newValues;
      if (deferred.length)
        defer(function () {
          for (var i = 0; i < deferred.length; i++) deferred[i]();
        });
    }
    return state;
  };

  set["then"] = function (onFulfilled, onRejected) {
    var promise2 = pinkySwear(extend);
    var callCallbacks = function () {
      try {
        var f = state ? onFulfilled : onRejected;
        if (isFunction(f)) {
          function resolve(x) {
            var then,
              cbCalled = 0;
            try {
              if (
                x &&
                (isObject(x) || isFunction(x)) &&
                isFunction((then = x["then"]))
              ) {
                if (x === promise2) throw new TypeError();
                then["call"](
                  x,
                  function () {
                    if (!cbCalled++) resolve.apply(undef, arguments);
                  },
                  function (value) {
                    if (!cbCalled++) promise2(false, [value]);
                  }
                );
              } else promise2(true, arguments);
            } catch (e) {
              if (!cbCalled++) promise2(false, [e]);
            }
          }
          resolve(f.apply(undef, values || []));
        } else promise2(state, values);
      } catch (e) {
        promise2(false, [e]);
      }
    };
    if (state != null) defer(callCallbacks);
    else deferred.push(callCallbacks);
    return promise2;
  };
  if (extend) {
    set = extend(set);
  }
  return set;
}

module.exports = pinkySwear;
