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

PromiseArray,
apiRejection,
tryConvertToPromise,
INTERNAL) {

var getDomain = Promise._getDomain; var async = require(“./async.js”); var util = require(“./util.js”); var tryCatch = util.tryCatch; var errorObj = util.errorObj; var PENDING = {}; var EMPTY_ARRAY = [];

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

this.constructor$(promises);
this._promise._captureStackTrace();
var domain = getDomain();
this._callback = domain === null ? fn : domain.bind(fn);
this._preservedValues = _filter === INTERNAL
    ? new Array(this.length())
    : null;
this._limit = limit;
this._inFlight = 0;
this._queue = limit >= 1 ? [] : EMPTY_ARRAY;
async.invoke(init, this, undefined);

} util.inherits(MappingPromiseArray, PromiseArray); function init() {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 (values[index] === PENDING) {
    values[index] = value;
    if (limit >= 1) {
        this._inFlight--;
        this._drainQueue();
        if (this._isResolved()) return;
    }
} else {
    if (limit >= 1 && this._inFlight >= limit) {
        values[index] = value;
        this._queue.push(index);
        return;
    }
    if (preservedValues !== null) preservedValues[index] = value;

    var callback = this._callback;
    var receiver = this._promise._boundValue();
    this._promise._pushContext();
    var ret = tryCatch(callback).call(receiver, value, index, length);
    this._promise._popContext();
    if (ret === errorObj) return this._reject(ret.e);

    var maybePromise = tryConvertToPromise(ret, this._promise);
    if (maybePromise instanceof Promise) {
        maybePromise = maybePromise._target();
        if (maybePromise._isPending()) {
            if (limit >= 1) this._inFlight++;
            values[index] = PENDING;
            return maybePromise._proxyPromiseArray(this, index);
        } else if (maybePromise._isFulfilled()) {
            ret = maybePromise._value();
        } else {
            return this._reject(maybePromise._reason());
        }
    }
    values[index] = ret;
}
var totalResolved = ++this._totalResolved;
if (totalResolved >= length) {
    if (preservedValues !== null) {
        this._filter(values, preservedValues);
    } else {
        this._resolve(values);
    }

}

};

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) {

var limit = typeof options === "object" && options !== null
    ? options.concurrency
    : 0;
limit = typeof limit === "number" &&
    isFinite(limit) && limit >= 1 ? limit : 0;
return new MappingPromiseArray(promises, fn, limit, _filter);

}

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

if (typeof fn !== "function") return apiRejection("fn must be a function\u000a\u000a    See http://goo.gl/916lJJ\u000a");

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

};

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

if (typeof fn !== "function") return apiRejection("fn must be a function\u000a\u000a    See http://goo.gl/916lJJ\u000a");
return map(promises, fn, options, _filter).promise();

};

};