var App = App || {};

App.Promise = function(_, jQuery, progress) {

    function BrokenPromiseError(promiseId) {
        this.name = 'BrokenPromiseError';
        this.message = 'Broken promise (promise ID: ' + promiseId + ')';
    }
    BrokenPromiseError.prototype = new Error();

    var active = [];
    var promiseId = 0;

    function make(callback, useProgress) {
        var deferred = jQuery.Deferred();
        var promise = deferred.promise();
        promise.promiseId = ++ promiseId;

        if (useProgress === true) {
            progress.start();
        }
        callback(function() {
            try {
                deferred.resolve.apply(deferred, arguments);
                active = _.without(active, promise.promiseId);
                progress.done();
            } catch (e) {
                if (!(e instanceof BrokenPromiseError)) {
                    console.log(e);
                }
                progress.reset();
            }
        }, function() {
            try {
                deferred.reject.apply(deferred, arguments);
                active = _.without(active, promise.promiseId);
                progress.done();
            } catch (e) {
                if (!(e instanceof BrokenPromiseError)) {
                    console.log(e);
                }
                progress.reset();
            }
        });

        active.push(promise.promiseId);

        promise.always(function() {
            if (!_.contains(active, promise.promiseId)) {
                throw new BrokenPromiseError(promise.promiseId);
            }
        });

        return promise;
    }

    function wait() {
        var promises = arguments;
        var deferred = jQuery.Deferred();
        return jQuery.when.apply(jQuery, promises)
            .then(function() {
                return deferred.resolve.apply(deferred, arguments);
            }).fail(function() {
                return deferred.reject.apply(deferred, arguments);
            });
    }

    function abortAll() {
        active = [];
    }

    function getActive() {
        return active.length;
    }

    return {
        make: function(callback) { return make(callback, true); },
        makeSilent: function(callback) { return make(callback, false); },
        wait: wait,
        getActive: getActive,
        abortAll: abortAll,
    };

};

App.DI.registerSingleton('promise', ['_', 'jQuery', 'progress'], App.Promise);