/* */ 
"use strict";
module.exports = function(Promise, tryConvertToPromise) {
  var util = require('./util');
  var CancellationError = Promise.CancellationError;
  var errorObj = util.errorObj;
  function FinallyHandlerCancelReaction(finallyHandler) {
    this.finallyHandler = finallyHandler;
  }
  FinallyHandlerCancelReaction.prototype._resultCancelled = function() {
    checkCancel(this.finallyHandler);
  };
  function checkCancel(ctx, reason) {
    if (ctx.cancelPromise != null) {
      if (arguments.length > 1) {
        ctx.cancelPromise._reject(reason);
      } else {
        ctx.cancelPromise._cancel();
      }
      ctx.cancelPromise = null;
      return true;
    }
    return false;
  }
  function succeed() {
    return finallyHandler.call(this, this.promise._target()._settledValue());
  }
  function fail(reason) {
    if (checkCancel(this, reason))
      return;
    errorObj.e = reason;
    return errorObj;
  }
  function finallyHandler(reasonOrValue) {
    var promise = this.promise;
    var handler = this.handler;
    if (!this.called) {
      this.called = true;
      var ret = this.type === 0 ? handler.call(promise._boundValue()) : handler.call(promise._boundValue(), reasonOrValue);
      if (ret !== undefined) {
        promise._setReturnedNonUndefined();
        var maybePromise = tryConvertToPromise(ret, promise);
        if (maybePromise instanceof Promise) {
          if (this.cancelPromise != null) {
            if (maybePromise.isCancelled()) {
              var reason = new CancellationError("late cancellation observer");
              promise._attachExtraTrace(reason);
              errorObj.e = reason;
              return errorObj;
            } else if (maybePromise.isPending()) {
              maybePromise._attachCancellationCallback(new FinallyHandlerCancelReaction(this));
            }
          }
          return maybePromise._then(succeed, fail, undefined, this, undefined);
        }
      }
    }
    if (promise.isRejected()) {
      checkCancel(this);
      errorObj.e = reasonOrValue;
      return errorObj;
    } else {
      checkCancel(this);
      return reasonOrValue;
    }
  }
  Promise.prototype._passThrough = function(handler, type, success, fail) {
    if (typeof handler !== "function")
      return this.then();
    return this._then(success, fail, undefined, {
      promise: this,
      handler: handler,
      called: false,
      cancelPromise: null,
      type: type
    }, undefined);
  };
  Promise.prototype.lastly = Promise.prototype["finally"] = function(handler) {
    return this._passThrough(handler, 0, finallyHandler, finallyHandler);
  };
  Promise.prototype.tap = function(handler) {
    return this._passThrough(handler, 1, finallyHandler);
  };
  return finallyHandler;
};
