/*
 * Provides base Briefing namespace and functionality to load additional script files.
 * Include on all pages.
 */

// Takes n arguments and returns a function wrapped to those arguments; No scope altering!
Function.prototype.createCallback = function(){
    var args = arguments;
    var method = this;
    return function() {
        return method.apply(window, args);
    };
};

/**
 * Creates a delegate (callback) that sets the scope to obj.
 * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
 * Will create a function that is automatically scoped to this.
 * @param {Object} obj (optional) The object for which the scope is set
 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
 *                                             if a number the args are inserted at the specified position
 * @return {Function} The new function
 */
Function.prototype.createDelegate = function(obj, args, appendArgs){
    var method = this;
    return function() {
        var callArgs = args || arguments;
        if(appendArgs === true){
            callArgs = Array.prototype.slice.call(arguments, 0);
            callArgs = callArgs.concat(args);
        }else if(typeof appendArgs == "number"){
            callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
            var applyArgs = [appendArgs, 0].concat(args); // create method call params
            Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
        }
        return method.apply(obj || window, callArgs);
    };
} 

// Check for the global Briefing namespace and create if it doesn't exist
if (typeof Briefing == "undefined" || !Briefing) {
    window.Briefing = {};
}

Briefing.isDebug = false;
Briefing._debugCheckComplete = false;

/**
 * Briefing.env is used to keep track of what is known about the YUI library and
 * the Briefing library environment
 * @class Briefing.env
 * @static
 */
Briefing.env = Briefing.env || {

    ymodules: {},
    /**
     * Keeps the version info for all Briefing modules that have reported themselves
     * @property modules
     * @type Object[]
     */
    modules: {},
    
    /**
     * Stores Briefing modules that were explicitly listed when a script file
     * was requested via a Briefing.getScripts() call.
     */  
    expectedModules : {},
    
    loadListeners: [],
    
    isDomReady : false
};

/**
 * Returns the namespace specified and creates it if it doesn't exist
 * <pre>
 * Briefing.namespace("property.package");
 * Briefing.namespace("Briefing.property.package");
 * </pre>
 * Either of the above would create Briefing.property, then
 * Briefing.property.package
 *
 * Be careful when naming packages. Reserved words may work in some browsers
 * and not others. For instance, the following will fail in Safari:
 * <pre>
 * Briefing.namespace("really.long.nested.namespace");
 * </pre>
 * This fails because "long" is a future reserved word in ECMAScript
 *
 * @method namespace
 * @static
 * @param  {String*} arguments 1-n namespaces to create 
 * @return {Object}  A reference to the last namespace object created
 */
Briefing.namespace = function() {
    var a=arguments, o=null, i, j, d;
    for (i=0; i<a.length; i=i+1) {
        d=a[i].split(".");
        o=Briefing;

        // Briefing is implied, so it is ignored if it is included
        for (j=(d[0] == "Briefing") ? 1 : 0; j<d.length; j=j+1) {
            o[d[j]]=o[d[j]] || {};
            o=o[d[j]];
        }
    }

    return o;
};

/**
 * Registers a module with the Briefing object
 * @method register
 * @static
 * @param {String}   name       the name of the module (event, slider, etc)
 * @param {Function} mainClass  a reference to class in the module.
 * @param {Object}   data       currently not used; not tracking multiple versions like YUI
 */
Briefing.register = function(name, mainClass, data) {
    if (mainClass) {
        // 
        // YAHOO.log('Registering ' + name, 'info', 'Load');
        
        var mods = Briefing.env.modules;
        if (!mods[name]) {
            mods[name] = {};
        }
        var m=mods[name];
        m.name = name;
        m.mainClass = mainClass;
        
        window.setTimeout(Briefing.checkListeners, 50);
    }
    else {
        //YAHOO.log("mainClass is undefined for module " + name, "warn");
    }
};

Briefing.processYahooClassRegistration = function(versionObj){
    Briefing.env.ymodules[versionObj.name] = versionObj.mainClass;
};

/**
 * Gets a Briefing class by unique module name if it exists.  Similar to YAHOO.getVersion
 * but the version and build are not maintained for Briefing modules.
 * @method getModule
 * @static
 * @param {String}   name       the name of the module (event, slider, etc)
 * @return {Object}  A reference to the Briefing class requested; or null if it isn't registered
 */
Briefing.getModule = function(name) {
    return Brifing.env.modules[name] || null;
};

/**
 * Returns boolean indicating if a specific briefing module was listed as being
 * expected in the Briefing.getScripts() call.
 *
 * This can be used by scripts to see if certain modules are expected before they
 * are loaded and registered.
 */
Briefing.checkForExpectedModule = function(name) {
    return Briefing.env.expectedModules[name] || false;
};

Briefing.execAfterDependencies = function(name, yDeps, bDeps, waitForDom, callback){
    var listeners = Briefing.env.loadListeners;
    var l = {
        name : name,
        yuiDeps : yDeps,
        briefingDeps : bDeps,
        waitForDom : waitForDom,
        callback : callback,
        actionComplete : false
    };
    
    listeners.push(l);
    
    //Briefing.checkListeners();
    window.setTimeout(Briefing.checkListeners, 30);
};


Briefing.injectAfterDependencies = function(name, yDeps, bDeps, waitForDom, url){
    var listeners = Briefing.env.loadListeners;
    var l = {
        name : name,
        yuiDeps : yDeps,
        briefingDeps : bDeps,
        waitForDom : waitForDom,
        url : url,
        actionComplete : false
    };
    
    listeners.push(l);
    
    //Briefing.checkListeners();
    window.setTimeout(Briefing.checkListeners, 30);
};

Briefing._checkListenerIsRunning = false;
Briefing._checkListenerRequestCount = 0;

Briefing.checkListeners = function() {
    
    if (Briefing._checkListenerIsRunning){
        Briefing._checkListenerRequestCount++;
    }
    else {
        Briefing._checkListenerIsRunning = true;
        Briefing._checkListenerRequestCount = 0;
        
        var lls = Briefing.env.loadListeners;

        var ll, j;
        for (var i=0; i < lls.length; i++){
            ll = lls[i];
            
            if (!ll.actionComplete && (Briefing._debugCheckComplete || ll.name == 'debug' || ll.name == 'checkDomReady') ){
                
                // check isDomReady if the listener requires it
                if (ll.waitForDom === true && ll.waitForDom != Briefing.env.isDomReady){
                    break;
                }
                
                // check YUI dependencies
                if (ll.yuiDeps !== true && ll.yuiDeps.length > 0){
                    for (j=0; j < ll.yuiDeps.length; ++j){
                        if (!Briefing.env.ymodules[ll.yuiDeps[j]]){
                            break;
                        }
                    }

                    if (j == ll.yuiDeps.length){ //all YUI dependencies available
                        ll.yuiDeps = true;
                    }
                }

                // check Briefing dependencies
                if (ll.briefingDeps !== true && ll.briefingDeps.length > 0){
                    for (j=0; j < ll.briefingDeps.length; ++j){
                        if (!Briefing.env.modules[ll.briefingDeps[j]]){
                            break;
                        }
                    }

                    if (j == ll.briefingDeps.length){ //all YUI dependencies available
                        ll.briefingDeps = true;
                    }
                }

                // execute once all dependencies are available
                if ((ll.yuiDeps === true || ll.yuiDeps.length === 0) && (ll.briefingDeps === true || ll.briefingDeps.length === 0)){
                    if (ll.url){
                        Briefing.injectScript(ll.url);
                    }
                    else if(ll.callback){
                        ll.callback.call(window);
                    }
                    
                    ll.actionComplete = true;
                }
            }
        }
        
        Briefing._checkListenerIsRunning = false;
        if (Briefing._checkListenerRequestCount > 0) {
            window.setTimeout(Briefing.checkListeners, 50);
        }
    }

};

/**
 * Injects scripts into the page to allow parallel downloading and execution.
 * Script must control for its load order and execution.  No execution sequence
 * is guaranteed.
 * 
 * Scripts to be downloaded are specified as an array of objects 
 * that specifies the script URL and optionally the modules that the script
 * will register.
 *
 * For example:
 *   Briefing.getScripts([{url:'/path/to/my/script.js', expectedModules:['the_module']}]);
 *   Briefing.getScripts([{url:'/path/to/my/script.js');
 */
Briefing.getScripts = function(filesArr){
    var i, j;
    
    // record expectedModule registration before any script gets inserted
    for(i=0; i < filesArr.length; ++i){
        if (filesArr[i].expectedModules && filesArr[i].expectedModules.length){
            for(j=0; j < filesArr[i].expectedModules.length; ++j){
                Briefing.env.expectedModules[ filesArr[i].expectedModules[j] ] = true;
            }
        }
    }
    
    // inject scripts
    for(i=0; i < filesArr.length; ++i){
        Briefing.injectAfterDependencies(filesArr[i].name, filesArr[i].yDeps, filesArr[i].bDeps, filesArr[i].waitForDom, filesArr[i].url);
    }    
};

Briefing.getFourDigitYear = function(){
	var year = (new Date()).getYear();
	return (year < 1000) ? year + 1900 : year;
};

Briefing.injectScript = function(url){
    var head = window.document.getElementsByTagName("head")[0], 
        n;
        
    n = document.createElement('script');
    n.setAttribute('type', 'text/javascript');
    n.setAttribute('src', url);
    n.setAttribute('charset', 'utf-8');
    head.appendChild(n);
};

// create second level namespaces
Briefing.namespace('widget', 'config', 'util', 'user');

// add listener to track YUI modules as they are loaded
YAHOO_config = {
    listener : Briefing.processYahooClassRegistration
};

Briefing.execAfterDependencies('checkDomReady', ['event'],[], false,
    function() {
        YAHOO.util.Event.onDOMReady(function(){Briefing.env.isDomReady = true; Briefing.checkListeners();});
     }
);

// @todo: check query string for debug=
if (window.location.href.search(/(\?|\&)debug=/) > 0) {
    Briefing.isDebug = true;
    Briefing.injectScript('/Common/Javascript/2.5.2/build/yuiloader/yuiloader-beta-min.js');
    
    Briefing.execAfterDependencies('debug', ['yahoo', 'dom', 'event', 'get'],['user'], true,
        function() {
            var loader = new YAHOO.util.YUILoader({
               require: ['logger'],
               loadOptional: true,
               onSuccess: function(){
                   var myEl = document.createElement("div");
                   myEl.className = 'yui-skin-sam';
                   YAHOO.util.Dom.setStyle(myEl, "position", "absolute");
                   YAHOO.util.Dom.setStyle(myEl, "top", "200px");
                   YAHOO.util.Dom.setStyle(myEl, "left", "200px");

                   var thatEl = document.createElement("div");
                   thatEl.style.cssText = 'width:800px; font-size:100%;';
                   myEl.appendChild(thatEl);
                   document.body.appendChild(myEl);
                   
                   Briefing.logReader = new YAHOO.widget.LogReader(thatEl, {
                       entryFormat : "<span class='{category}'>{label}</span> : {totalTime}ms : {sourceAndDetail} : {message}"
                   });
                   
                   Briefing._debugCheckComplete = true;
                   Briefing.User.init();
                   Briefing.checkListeners();
               },
               onFailure: function(msg, xhrobj){
                   alert("DEBUG Mode failed to load.  Remove debug argument from URL.");
               }
            });
            loader.insert();
        }
    );
}
else {
    Briefing.isDebug = false;
    Briefing._debugCheckComplete = true;
}