/*@flow*/
/*
 * Caleb James DeLisle
 * Sat Mar 23 01:42:29 EDT 2013
 * Public Domain or MIT License
 */
/*::
export type Nthen_WaitFor_t = {
    (?(...any)=>any): (...any)=>any,
    abort: ()=>void
};
export type Nthen_Ret_t = {
    nThen: Nthen_t,
    orTimeout:((Nthen_WaitFor_t)=>void, number)=>Nthen_Ret_t
};
export type Nthen_t = ((Nthen_WaitFor_t)=>void)=>Nthen_Ret_t;
module.exports = */ (function() {
var nThen /*:Nthen_t*/ = function(next) {
    var funcs /*:Array<(Nthen_WaitFor_t)=>void>*/ = [];
    var timeouts = [];
    var calls = 0;
    var abort;
    var inNthen = 0;
    var callNext = function (arg) {
        inNthen++;
        funcs.shift()(arg);
        inNthen--;
    };
    var waitFor /*:Nthen_WaitFor_t*/ = function(func) {
        calls++;
        return function() {
            var args = Array.prototype.slice.call(arguments);
            var f = function () {
                var ret;
                if (func) {
                    ret = func.apply(null, args);
                }
                calls = (calls || 1) - 1;
                while (!calls && funcs.length && !abort) {
                    callNext(waitFor);
                }
                return ret;
            };
            if (inNthen) {
                setTimeout(f);
            } else {
                f();
            }
        };
    };
    waitFor.abort = function () {
        timeouts.forEach(clearTimeout);
        abort = 1;
    };
    var ret = {
        nThen: function(next) {
            if (!abort) {
                if (!calls) {
                    inNthen++;
                    next(waitFor);
                    inNthen--;
                } else {
                    funcs.push(next);
                }
            }
            return ret;
        },
        orTimeout: function(func, milliseconds) {
            if (abort) { return ret; }
            if (!milliseconds) { throw Error("Must specify milliseconds to orTimeout()"); }
            var cto;
            var timeout = setTimeout(function() {
                while (funcs.shift() !== cto) { }
                inNthen++;
                func(waitFor);
                inNthen--;
                calls = (calls || 1) - 1;
                while (!calls && funcs.length) { callNext(waitFor); }
            }, milliseconds);
            funcs.push(cto = function() {
                var idx = timeouts.indexOf(timeout);
                if (idx > -1) {
                    timeouts.splice(idx, 1);
                    clearTimeout(timeout);
                    return;
                }
                throw new Error('timeout not listed in array');
            });
            timeouts.push(timeout);
            return ret;
        }
    };
    return ret.nThen(next);
};

if (typeof(module) !== 'undefined') { module.exports = nThen; }
else if (typeof(window.define) === 'function' && window.define.amd) { window.define(function () { return nThen; }); }
else { window.nThen = nThen; }
/*:: return nThen; */
})();
