'use strict';

class Handlers {
  constructor () {
    this.fulfill = null;
    this.reject = null;
  }
}

/**
 * @class promise
 */
class FidelityPromise {
  constructor (state, value) {
    this.state = state;
    this.value = value;
    this.queue = [];
    this.handlers = new Handlers();
    this._fidelity = true;
  }

  /**
   * Follows the [Promises/A+](https://promisesaplus.com/) spec
   * for a `then` function.
   * @returns a promise
   * @function then
   */
  then (onFulfilled, onRejected) {
    const next = new FidelityPromise(PENDING, null);
    if (typeof onFulfilled === 'function') {
      next.handlers.fulfill = onFulfilled;
    }
    if (typeof onRejected === 'function') {
      next.handlers.reject = onRejected;
    }
    this.queue.push(next);
    process(this);
    return next;
  }

  catch (onRejected) {
    return this.then(null, onRejected);
  }
}

const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;
const TRUE = new FidelityPromise(FULFILLED, true);
const FALSE = new FidelityPromise(FULFILLED, false);
const NULL = new FidelityPromise(FULFILLED, null);
const UNDEFINED = new FidelityPromise(undefined);
const ZERO = new FidelityPromise(FULFILLED, 0);
const EMPTYSTRING = new FidelityPromise(FULFILLED, '');

/**
 * @module fidelity
*/
module.exports = {
  promise: promise,
  deferred: deferred,
  resolve: resolveValue,
  PENDING: PENDING,
  FULFILLED: FULFILLED,
  REJECTED: REJECTED
};

/**
 * Creates a promise that will be resolved or rejected at some time
 * in the future.
 * @param {function} fn The function that will do the work of this promise.
 * The function is passed two function arguments, `resolve()` and `reject()`.
 * Call one of these when the work has completed (or failed).
 * @returns {object} A promise object
 * @instance
 */
function promise (fn) {
  const p = new FidelityPromise(PENDING, null);
  if (typeof fn === 'function') {
    try {
      fn((v) => resolve(p, v), (r) => transition(p, REJECTED, r));
    } catch (e) {
      transition(p, REJECTED, e);
    }
  }
  return p;
}

/**
 * Returns a promise that is resolved with `value`.
 * @param {any} value The value to resolve the returned promise with
 * @returns {object} A promise resolved with `value`
 * @method resolve
 * @instance resolve
 */
function resolveValue (value) {
  if (value && value.then) return value;

  switch (value) {
    case null:
      return NULL;
    case undefined:
      return UNDEFINED;
    case true:
      return TRUE;
    case false:
      return FALSE;
    case 0:
      return ZERO;
    case '':
      return EMPTYSTRING;
  }

  return new FidelityPromise(FULFILLED, value);
}

/**
 * Creates a `deferred` object, containing a promise which may
 * be resolved or rejected at some point in the future.
 * @returns {object} deferred The deferred object
 * @returns {function} deferred.resolve(value) The resolve function
 * @returns {function} deferred.reject(cause) The reject function
 * @returns {object} deferred.promise The inner promise object
 * @instance
 */
function deferred () {
  var resolver;
  var rejecter;
  var p = promise((resolve, reject) => {
    resolver = resolve;
    rejecter = reject;
  });

  function resolve (value) {
    resolver(value);
  }

  function reject (cause) {
    rejecter(cause);
  }

  return {
    promise: p,
    resolve: resolve,
    reject: reject
  };
}

function resolve (p, x) {
  if (x === p) {
    transition(p, REJECTED, new TypeError('The promise and its value are the same.'));
    return;
  }

  if (x && x._fidelity) {
    if (x.state === PENDING) {
      x.then((value) => resolve(p, value), (cause) => transition(p, REJECTED, cause));
    } else {
      transition(p, x.state, x.value);
    }
  } else if (x && ((typeof x === 'function') || (typeof x === 'object'))) {
    let called = false;
    let thenFunction;
    try {
      thenFunction = x.then;
      if (thenFunction && (typeof thenFunction === 'function')) {
        thenFunction.call(x, (y) => {
          if (!called) {
            resolve(p, y);
            called = true;
          }
        }, (r) => {
          if (!called) {
            transition(p, REJECTED, r);
            called = true;
          }
        });
      } else {
        transition(p, FULFILLED, x);
        called = true;
      }
    } catch (e) {
      if (!called) {
        transition(p, REJECTED, e);
        called = true;
      }
    }
  } else {
    transition(p, FULFILLED, x);
  }
}

function process (p) {
  if (p.state === PENDING) return;
  global.process.nextTick(() => {
    let qp, handler, value;
    while (p.queue.length) {
      qp = p.queue.shift();
      if (p.state === FULFILLED) {
        handler = qp.handlers.fulfill || ((v) => v);
      } else if (p.state === REJECTED) {
        handler = qp.handlers.reject || ((r) => {
          throw r;
        });
      }
      try {
        value = handler(p.value);
      } catch (e) {
        transition(qp, REJECTED, e);
        continue;
      }
      resolve(qp, value);
    }
  });
  return p;
}

function transition (p, state, value) {
  if (p.state === state ||
    p.state !== PENDING ||
    arguments.length !== 3) return;
  p.state = state;
  p.value = value;
  return process(p);
}

@@ -166,24 +166,34 @@
 uctor (state
+ = PENDING
 , value) %7B%0A 
@@ -187,24 +187,24 @@
 G, value) %7B%0A
-
     this.sta
@@ -300,35 +300,8 @@
 ();%0A
-    this._fidelity = true;%0A
   %7D%0A
@@ -427,28 +427,8 @@
 ise%0A
-   * @function then%0A
    *
@@ -497,37 +497,24 @@
 lityPromise(
-PENDING, null
 );%0A    if (t
@@ -1303,21 +1303,16 @@
  resolve
-Value
 ,%0A  PEND
@@ -1870,24 +1870,31 @@
 ) =%3E resolve
+Promise
 (p, v), (r) 
@@ -2244,13 +2244,8 @@
 olve
-Value
  (va
@@ -3324,16 +3324,23 @@
  resolve
+Promise
  (p, x) 
@@ -3476,217 +3476,8 @@
 x &&
- x._fidelity) %7B%0A    if (x.state === PENDING) %7B%0A      x.then((value) =%3E resolve(p, value), (cause) =%3E transition(p, REJECTED, cause));%0A    %7D else %7B%0A      transition(p, x.state, x.value);%0A    %7D%0A  %7D else if (x &&
  ((t
@@ -3764,16 +3764,23 @@
  resolve
+Promise
 (p, y);%0A
@@ -4720,24 +4720,24 @@
 ue;%0A      %7D%0A
-
       resolv
@@ -4737,16 +4737,23 @@
  resolve
+Promise
 (qp, val

Remove fidelity-specific code and cleanup