/**
 * Class for loading multiple items using the method add(). The given items must be instances of Deferred or at least have
 * the methods .done(callbackDone) and .fail(callbackFail)
 *
 * The class dispatches two events:
 *   - JQueryLoader.EVT_PROGRESS dispatched while the downloads progress, handler gets the following parameters:
 *        -> base.$.trigger(JQueryLoader.EVT_PROGRESS, [progress, totalDownloads, doneDownloads, failedDownloads])
 *   - JQueryLoader.EVT_COMPLETE dispatched when all downloads are complete, handler gets the following parameters:
 *        -> base.$.trigger(JQueryLoader.EVT_PROGRESS, [progress, totalDownloads, doneDownloads, failedDownloads])
 *
 * @author Karsten John Gerber (karsten.john.gerber[at]googlemail.com)
 */
LIB3m5.JQueryLoader = (function($) {

    /** this pointer **/
    var base;

    /**
     * @constructor
	 */
	var JQueryLoader = function() {
        base = this;
		base.clazz = JQueryLoader;
		base.$ = $(this);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // PRIVATE VARS
    /** total number of totalDownloads **/
    var totalDownloads = 0;
    /** successful downloads **/
    var doneDownloads = 0;
    /** failed downloads **/
    var failedDownloads = 0;
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// STATIC VARS
	/**
	 * Event when download progresses
	 * -> parameter is an Array: [progress, totalDownloads, doneDownloads, failedDownloads]
	 */
	JQueryLoader.EVT_PROGRESS = 'EVT_PROGRESS';
	/**
	 * Event when all downloads are completed
	 * -> parameter is an Array: [progress, totalDownloads, doneDownloads, failedDownloads]
	 */
	JQueryLoader.EVT_COMPLETE = 'EVT_COMPLETE';
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // PUBLIC METHODS
    /**
     * add object to be downloaded. The objects must be instances of Deferred
     *
     * @param {Deferred} firstDeferred parameters are possible
     *
     * @return {LIB3m5.JQueryLoader} the loader itself
     */
    JQueryLoader.prototype.add = function( firstDeferred /* lots of deferred */ ){
        // go through all params
        for (var i = 0; i < arguments.length; i++) {
            // pick one
            var deferred = arguments[i];
            // try to add it. by acessing done() and fail() we do implicit type-check
            try {
                deferred
                    .done(deferredDone)
                    .fail(deferredFailed)
                ;
                totalDownloads++;
            }
            catch(e) {
                $.log("Loader.load all params must be instances of Deferred! This one seems not to be", deferred);
            }
        }

        return this;
    };

    /**
     * Add a CSS to the loader
     *
     * It first loads the css via $.get and then creates a <link> in the head part of the document to include the css.
     * We use the caching capabilities of the browser. So we do not write the css content directly into that <link>-tag.
     * Instead we give the tag the same URL as we already loaded. The browser then uses its cached data.
     *
     * Doing it this way we can dynamically load CSS and still have decent support with tools like firebug, showing
     * us which style comes from which file. We would loose this if we would write the css's content into the <link>-tag
     * using javascript.
     *
     * @param cssURL
     * @param head
     */
    JQueryLoader.prototype.addCSS = function (cssURL, head) {
        var css = $("<link>");
        head.append(css);
        base.add(
            $.get(cssURL)
            .done(function(data) {
                css.attr({
                    rel:  "stylesheet",
                    type: "text/css",
                    href: cssURL
                });
            })
        );
    };

    JQueryLoader.prototype.addScripts = function (firstScriptURL /** lots of urls **/ ) {
        if(arguments.length == 0) {
            return;
        }

        // $.log("addScripts", arguments);
        var files = Array.prototype.slice.call(arguments);

        var theDeferred = $.Deferred();

        // $.log("addScripts argsArray", files);

        var loadNext = function (data) {
           //  $.log("files", files);

            if(files.length > 0) {

                var url = files[0];
                $.log("loading script", url);
                files.splice(0, 1);
                $.ajax({
                    url: url,
                    dataType: "script",
                    cache: true
                })
                .done(loadNext)
                .fail(onFail)
                ;
            }
            else {
                // $.log("DONE with scripts!");
                theDeferred.resolve();
            }
        };

        var onFail = function () {
            theDeferred.reject();
        };

        loadNext.call();

        base.add(theDeferred);
    };

    /**
     * Resets the Loaders internal counters and state, BUT does NOT stop any downloads
     *
     * @return {Loader} the loader itself
     */
    JQueryLoader.prototype.reset = function() {
        totalDownloads = 0;
        doneDownloads = 0;
        failedDownloads = 0;

        return this;
    };
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // PRIVATE METHODS
    /**
     * callback that is called when one downloads successfully completes
     */
    var deferredDone = function(obj) {
        doneDownloads++;
        dispatchEvents();
    };

    /**
     * callback that is called when one download failes
     */
    var deferredFailed = function(obj) {
        failedDownloads++;
        dispatchEvents();
    };

    /**
     * dispatches events EVT_PROGRESS and EVT_COMPLETE
     */
    var dispatchEvents = function() {
        // calculate the progess
        var progress = totalDownloads > 0 ? (doneDownloads +  failedDownloads) / totalDownloads : 1;
        // trigger the EVT_PROGRESS event
        base.$.trigger(JQueryLoader.EVT_PROGRESS, [progress, totalDownloads, doneDownloads, failedDownloads]);
        // if all downloads are finished trigger the EVT_COMPLETE event
        if(doneDownloads +  failedDownloads >= totalDownloads) {
            base.$.trigger(JQueryLoader.EVT_COMPLETE, [progress, totalDownloads, doneDownloads, failedDownloads]);
        }
    };

    return JQueryLoader;
})(jQuery);
