/* */ 
(function(process) {
  'use strict';
  var $ = require("./$"),
      ctx = require("./$.ctx"),
      cof = require("./$.cof"),
      $def = require("./$.def"),
      assert = require("./$.assert"),
      forOf = require("./$.for-of"),
      setProto = require("./$.set-proto").set,
      same = require("./$.same"),
      species = require("./$.species"),
      SPECIES = require("./$.wks")('species'),
      RECORD = require("./$.uid").safe('record'),
      PROMISE = 'Promise',
      global = $.g,
      process = global.process,
      isNode = cof(process) == 'process',
      asap = process && process.nextTick || require("./$.task").set,
      P = global[PROMISE],
      isFunction = $.isFunction,
      isObject = $.isObject,
      assertFunction = assert.fn,
      assertObject = assert.obj,
      Wrapper;
  function testResolve(sub) {
    var test = new P(function() {});
    if (sub)
      test.constructor = Object;
    return P.resolve(test) === test;
  }
  var useNative = function() {
    var works = false;
    function P2(x) {
      var self = new P(x);
      setProto(self, P2.prototype);
      return self;
    }
    try {
      works = isFunction(P) && isFunction(P.resolve) && testResolve();
      setProto(P2, P);
      P2.prototype = $.create(P.prototype, {constructor: {value: P2}});
      if (!(P2.resolve(5).then(function() {}) instanceof P2)) {
        works = false;
      }
      if (works && $.DESC) {
        var thenableThenGotten = false;
        P.resolve($.setDesc({}, 'then', {get: function() {
            thenableThenGotten = true;
          }}));
        works = thenableThenGotten;
      }
    } catch (e) {
      works = false;
    }
    return works;
  }();
  function isPromise(it) {
    return isObject(it) && (useNative ? cof.classof(it) == 'Promise' : RECORD in it);
  }
  function sameConstructor(a, b) {
    if (!$.FW && a === P && b === Wrapper)
      return true;
    return same(a, b);
  }
  function getConstructor(C) {
    var S = assertObject(C)[SPECIES];
    return S != undefined ? S : C;
  }
  function isThenable(it) {
    var then;
    if (isObject(it))
      then = it.then;
    return isFunction(then) ? then : false;
  }
  function notify(record) {
    var chain = record.c;
    if (chain.length)
      asap.call(global, function() {
        var value = record.v,
            ok = record.s == 1,
            i = 0;
        function run(react) {
          var cb = ok ? react.ok : react.fail,
              ret,
              then;
          try {
            if (cb) {
              if (!ok)
                record.h = true;
              ret = cb === true ? value : cb(value);
              if (ret === react.P) {
                react.rej(TypeError('Promise-chain cycle'));
              } else if (then = isThenable(ret)) {
                then.call(ret, react.res, react.rej);
              } else
                react.res(ret);
            } else
              react.rej(value);
          } catch (err) {
            react.rej(err);
          }
        }
        while (chain.length > i)
          run(chain[i++]);
        chain.length = 0;
      });
  }
  function isUnhandled(promise) {
    var record = promise[RECORD],
        chain = record.a || record.c,
        i = 0,
        react;
    if (record.h)
      return false;
    while (chain.length > i) {
      react = chain[i++];
      if (react.fail || !isUnhandled(react.P))
        return false;
    }
    return true;
  }
  function $reject(value) {
    var record = this,
        promise;
    if (record.d)
      return ;
    record.d = true;
    record = record.r || record;
    record.v = value;
    record.s = 2;
    record.a = record.c.slice();
    setTimeout(function() {
      asap.call(global, function() {
        if (isUnhandled(promise = record.p)) {
          if (isNode) {
            process.emit('unhandledRejection', value, promise);
          } else if (global.console && console.error) {
            console.error('Unhandled promise rejection', value);
          }
        }
        record.a = undefined;
      });
    }, 1);
    notify(record);
  }
  function $resolve(value) {
    var record = this,
        then;
    if (record.d)
      return ;
    record.d = true;
    record = record.r || record;
    try {
      if (then = isThenable(value)) {
        asap.call(global, function() {
          var wrapper = {
            r: record,
            d: false
          };
          try {
            then.call(value, ctx($resolve, wrapper, 1), ctx($reject, wrapper, 1));
          } catch (e) {
            $reject.call(wrapper, e);
          }
        });
      } else {
        record.v = value;
        record.s = 1;
        notify(record);
      }
    } catch (e) {
      $reject.call({
        r: record,
        d: false
      }, e);
    }
  }
  if (!useNative) {
    P = function Promise(executor) {
      assertFunction(executor);
      var record = {
        p: assert.inst(this, P, PROMISE),
        c: [],
        a: undefined,
        s: 0,
        d: false,
        v: undefined,
        h: false
      };
      $.hide(this, RECORD, record);
      try {
        executor(ctx($resolve, record, 1), ctx($reject, record, 1));
      } catch (err) {
        $reject.call(record, err);
      }
    };
    require("./$.mix")(P.prototype, {
      then: function then(onFulfilled, onRejected) {
        var S = assertObject(assertObject(this).constructor)[SPECIES];
        var react = {
          ok: isFunction(onFulfilled) ? onFulfilled : true,
          fail: isFunction(onRejected) ? onRejected : false
        };
        var promise = react.P = new (S != undefined ? S : P)(function(res, rej) {
          react.res = assertFunction(res);
          react.rej = assertFunction(rej);
        });
        var record = this[RECORD];
        record.c.push(react);
        if (record.a)
          record.a.push(react);
        if (record.s)
          notify(record);
        return promise;
      },
      'catch': function(onRejected) {
        return this.then(undefined, onRejected);
      }
    });
  }
  $def($def.G + $def.W + $def.F * !useNative, {Promise: P});
  cof.set(P, PROMISE);
  species(P);
  species(Wrapper = $.core[PROMISE]);
  $def($def.S + $def.F * !useNative, PROMISE, {reject: function reject(r) {
      return new (getConstructor(this))(function(res, rej) {
        rej(r);
      });
    }});
  $def($def.S + $def.F * (!useNative || testResolve(true)), PROMISE, {resolve: function resolve(x) {
      return isPromise(x) && sameConstructor(x.constructor, this) ? x : new this(function(res) {
        res(x);
      });
    }});
  $def($def.S + $def.F * !(useNative && require("./$.iter-detect")(function(iter) {
    P.all(iter)['catch'](function() {});
  })), PROMISE, {
    all: function all(iterable) {
      var C = getConstructor(this),
          values = [];
      return new C(function(res, rej) {
        forOf(iterable, false, values.push, values);
        var remaining = values.length,
            results = Array(remaining);
        if (remaining)
          $.each.call(values, function(promise, index) {
            C.resolve(promise).then(function(value) {
              results[index] = value;
              --remaining || res(results);
            }, rej);
          });
        else
          res(results);
      });
    },
    race: function race(iterable) {
      var C = getConstructor(this);
      return new C(function(res, rej) {
        forOf(iterable, false, function(promise) {
          C.resolve(promise).then(res, rej);
        });
      });
    }
  });
})(require("process"));
