"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var DEFAULT_MAX = 5;
/**
 * Default checker which validates if a next task should begin.
 * This can be overwritten to write own checks for example checking the amount
 * of used ram and waiting till the ram is low enough for a next task.
 *
 * It should always resolve with a boolean, either `true` to start a next task
 * or `false` to stop executing a new task.
 *
 * If this method rejects, the error will propagate to the caller
 * @param status
 * @param tasks
 * @returns {Promise}
 */
var defaultNextTaskCheck = function (status, tasks) {
    return new Promise(function (resolve, reject) {
        resolve(status.amountStarted < tasks.length);
    });
};
var DEFAULT_OPTIONS = {
    maxInProgress: DEFAULT_MAX,
    failFast: false,
    nextCheck: defaultNextTaskCheck
};
/**
 * Raw throttle function, which can return extra meta data.
 * @param tasks required array of tasks to be executed
 * @param options Options object
 * @returns {Promise}
 */
function raw(tasks, options) {
    return new Promise(function (resolve, reject) {
        var myOptions = Object.assign({}, DEFAULT_OPTIONS, options);
        var result = {
            amountDone: 0,
            amountStarted: 0,
            amountResolved: 0,
            amountRejected: 0,
            amountNextCheckFalsey: 0,
            rejectedIndexes: [],
            resolvedIndexes: [],
            nextCheckFalseyIndexes: [],
            taskResults: []
        };
        if (tasks.length === 0) {
            return resolve(result);
        }
        var failedFast = false;
        var currentTaskIndex = 0;
        var executeTask = function (index) {
            result.amountStarted++;
            if (typeof tasks[index] === 'function') {
                tasks[index]().then(function (taskResult) {
                    result.taskResults[index] = taskResult;
                    result.resolvedIndexes.push(index);
                    result.amountResolved++;
                    taskDone();
                }, function (error) {
                    result.taskResults[index] = error;
                    result.rejectedIndexes.push(index);
                    result.amountRejected++;
                    if (myOptions.failFast === true) {
                        failedFast = true;
                        return reject(result);
                    }
                    taskDone();
                });
            }
            else {
                failedFast = true;
                return reject(new Error('tasks[' + index + ']: ' + tasks[index] + ', is supposed to be of type function'));
            }
        };
        var taskDone = function () {
            //make sure no more tasks are spawned when we failedFast
            if (failedFast === true) {
                return;
            }
            result.amountDone++;
            if (typeof myOptions.progressCallback === 'function') {
                myOptions.progressCallback(result);
            }
            if (result.amountDone === tasks.length) {
                return resolve(result);
            }
            if (currentTaskIndex < tasks.length) {
                nextTask(currentTaskIndex++);
            }
        };
        var nextTask = function (index) {
            //check if we can execute the next task
            myOptions.nextCheck(result, tasks).then(function (canExecuteNextTask) {
                if (canExecuteNextTask === true) {
                    //execute it
                    executeTask(index);
                }
                else {
                    result.amountNextCheckFalsey++;
                    result.nextCheckFalseyIndexes.push(index);
                    taskDone();
                }
            }, reject);
        };
        //spawn the first X task
        for (var i = 0; i < Math.min(myOptions.maxInProgress, tasks.length); i++) {
            nextTask(currentTaskIndex++);
        }
    });
}
exports.raw = raw;
/**
 * Executes the raw function, but only return the task array
 * @param tasks
 * @param options
 * @returns {Promise}
 */
function executeRaw(tasks, options) {
    return new Promise(function (resolve, reject) {
        raw(tasks, options).then(function (result) {
            resolve(result.taskResults);
        }, function (error) {
            if (error instanceof Error) {
                reject(error);
            }
            else {
                reject(error.taskResults[error.rejectedIndexes[0]]);
            }
        });
    });
}
/**
 * Simply run all the promises after each other, so in synchronous manner
 * @param tasks required array of tasks to be executed
 * @param options Options object
 * @returns {Promise}
 */
function sync(tasks, options) {
    var myOptions = Object.assign({}, { maxInProgress: 1, failFast: true }, options);
    return executeRaw(tasks, myOptions);
}
exports.sync = sync;
/**
 * Exposes the same behaviour as Promise.All(), but throttled!
 * @param tasks required array of tasks to be executed
 * @param options Options object
 * @returns {Promise}
 */
function all(tasks, options) {
    var myOptions = Object.assign({}, { failFast: true }, options);
    return executeRaw(tasks, myOptions);
}
exports.all = all;
//# sourceMappingURL=throttle.js.map