“use strict”; module.exports = function(Promise,

PromiseArray,
apiRejection,
tryConvertToPromise,
INTERNAL,
debug) {

var util = require(“./util”); var tryCatch = util.tryCatch; var errorObj = util.errorObj; var async = Promise._async;

function MappingPromiseArray(promises, fn, limit, _filter) {

this.constructor$(promises);
this._promise._captureStackTrace();
var context = Promise._getContext();
this._callback = util.contextBind(context, fn);
this._preservedValues = _filter === INTERNAL
    ? new Array(this.length())
    : null;
this._limit = limit;
this._inFlight = 0;
this._queue = [];
async.invoke(this._asyncInit, this, undefined);
if (util.isArray(promises)) {
    for (var i = 0; i < promises.length; ++i) {
        var maybePromise = promises[i];
        if (maybePromise instanceof Promise) {
            maybePromise.suppressUnhandledRejections();
        }
    }
}

} util.inherits(MappingPromiseArray, PromiseArray);

MappingPromiseArray.prototype._asyncInit = function() {

this._init$(undefined, -2);

};

MappingPromiseArray.prototype._init = function () {};

MappingPromiseArray.prototype._promiseFulfilled = function (value, index) {

var values = this._values;
var length = this.length();
var preservedValues = this._preservedValues;
var limit = this._limit;

if (index < 0) {
    index = (index * -1) - 1;
    values[index] = value;
    if (limit >= 1) {
        this._inFlight--;
        this._drainQueue();
        if (this._isResolved()) return true;
    }
} else {
    if (limit >= 1 && this._inFlight >= limit) {
        values[index] = value;
        this._queue.push(index);
        return false;
    }
    if (preservedValues !== null) preservedValues[index] = value;

    var promise = this._promise;
    var callback = this._callback;
    var receiver = promise._boundValue();
    promise._pushContext();
    var ret = tryCatch(callback).call(receiver, value, index, length);
    var promiseCreated = promise._popContext();
    debug.checkForgottenReturns(
        ret,
        promiseCreated,
        preservedValues !== null ? "Promise.filter" : "Promise.map",
        promise
    );
    if (ret === errorObj) {
        this._reject(ret.e);
        return true;
    }

    var maybePromise = tryConvertToPromise(ret, this._promise);
    if (maybePromise instanceof Promise) {
        maybePromise = maybePromise._target();
        var bitField = maybePromise._bitField;
        ;
        if (((bitField & 50397184) === 0)) {
            if (limit >= 1) this._inFlight++;
            values[index] = maybePromise;
            maybePromise._proxy(this, (index + 1) * -1);
            return false;
        } else if (((bitField & 33554432) !== 0)) {
            ret = maybePromise._value();
        } else if (((bitField & 16777216) !== 0)) {
            this._reject(maybePromise._reason());
            return true;
        } else {
            this._cancel();
            return true;
        }
    }
    values[index] = ret;
}
var totalResolved = ++this._totalResolved;
if (totalResolved >= length) {
    if (preservedValues !== null) {
        this._filter(values, preservedValues);
    } else {
        this._resolve(values);
    }
    return true;
}
return false;

};

MappingPromiseArray.prototype._drainQueue = function () {

var queue = this._queue;
var limit = this._limit;
var values = this._values;
while (queue.length > 0 && this._inFlight < limit) {
    if (this._isResolved()) return;
    var index = queue.pop();
    this._promiseFulfilled(values[index], index);
}

};

MappingPromiseArray.prototype._filter = function (booleans, values) {

var len = values.length;
var ret = new Array(len);
var j = 0;
for (var i = 0; i < len; ++i) {
    if (booleans[i]) ret[j++] = values[i];
}
ret.length = j;
this._resolve(ret);

};

MappingPromiseArray.prototype.preservedValues = function () {

return this._preservedValues;

};

function map(promises, fn, options, _filter) {

if (typeof fn !== "function") {
    return apiRejection("expecting a function but got " + util.classString(fn));
}

var limit = 0;
if (options !== undefined) {
    if (typeof options === "object" && options !== null) {
        if (typeof options.concurrency !== "number") {
            return Promise.reject(
                new TypeError("'concurrency' must be a number but it is " +
                                util.classString(options.concurrency)));
        }
        limit = options.concurrency;
    } else {
        return Promise.reject(new TypeError(
                        "options argument must be an object but it is " +
                         util.classString(options)));
    }
}
limit = typeof limit === "number" &&
    isFinite(limit) && limit >= 1 ? limit : 0;
return new MappingPromiseArray(promises, fn, limit, _filter).promise();

}

Promise.prototype.map = function (fn, options) {

return map(this, fn, options, null);

};

Promise.map = function (promises, fn, options, _filter) {

return map(promises, fn, options, _filter);

};

};