/*** Start of file: generic\common\bannerpool.js ***/
/**
 * Bannerpool object, cycles through a set of banners that are inside a supplied HTML element. The banner pool allows lots of customizations, such as animation speed,
 * whether to pause on mouseover, autostart, etc.
 * @class Bannerpool The bannerpool object can be used to cycle through a set of banners and show each in turn
 * @constructor
 * @param {Object} configParams The configuration settings for initializing the banner pool
 * @param {Function} callback The callback method to call after each cycle in the banner pool (when the fade-in of the new banner has ended)
 */
var BannerPool = function(configParams, callback){
    
    /**
     * Configuration object for the validator. Can be customized through the configParams constructor parameter.
     * @namespace Configuration object for the banner pool; contains default settings.
     */
    var config = {
        
        /**
         * The HTML element that contains the banner items
         * @type HTMLElement
         */
        container : null, 

        /**
         * Rotation speed in seconds
         * @type Integer
         */
        rotationspeed : 5,

        /**
         * Animation speed in milliseconds
         * @type Integer
         */
        animationspeed : 750,

        /**
         * Index of the banner to show first
         * @type Integer
         */
        startindex : 0,

        /**
         * Determines wheter to autostart animating
         * @type Boolean
         */
        autostart : true,

        /**
         * Stops the animation while the cursor is over the banner
         * @type Boolean
         */
        pauseonmouseover : true, 

        /**
         * May the banner items stretch the containeritem if they are bigger
         * @type Boolean
         */
        allowoverrulesizes : false,

        /**
         * Default logging object
         * @type Function
         */
        debuglogger : function(){Log.Debug(msg);},

        /**
         * Callback function that will be called after the fade-in of each new banner
         * @type Function
         */
        callback: function(){},
                
        /**
         * For internal use. Cycle through the configuration parameters and overwrite defaults if necessary.
         * @param Object params The new configuration parameters.
         * @param Function callback The callback function to use.
         * @type Function
         * @private
         */
        bind : function(params, callback){
            if(params){
                for(param in params){
                    eval("config." + param + " = params." + param);
                }
            }
            if(typeof(callback) == "function"){
                config.callback = callback;
            }
            //config binding done
            config.init = true;
        },

        /**
         * For internal use only. Determines if the binding of the config is done.
         * @type Boolean
         * @private
         */
        init: false
    }; 
    
    //bind the configuration
    config.bind(configParams, callback);
        
    /**
     * @namespace Private methods and variables
     */
    var priv = {
        
        /**
         * The current item shown in the banner
         * @type HTMLElement
         * @private
         */        
        currentItem : null,

        /**
         * The index of the item currently shown in the banner
         * @type Integer
         * @private
         */        
        currentIndex : 0,

        /**
         * Array containing all the items in the bannerpool
         * @type Array
         * @private
         */        
        items : [],

        /**
         * The height of the space in which the banners are shown
         * @type Integer
         * @private
         */        
        itemHeight : 0,

        /**
         * The width of the space in which the banners are shown
         * @type Integer
         * @private
         */        
        itemWidth : 0,

        /**
         * Whether the banner is locked
         * @type Boolean
         * @private
         */        
        locked : false,

        /**
         * Whether the banner is currently rotating
         * @type Boolean
         * @private
         */        
        rotating : false,

        /**
         * The ID of the interval used for rotating the banners.
         * @type Integer
         * @private
         */        
        timer : null,

        /**
         * The time remaining (used when resuming after a pause)
         * @type Date
         * @private
         */        
        timerremain : 0,

        /**
         * The time (in milliseconds) to next step; used when resuming after a pause
         * @type Integer
         * @private
         */        
        timeToNextStep : config.rotationspeed,

        /**
         * Initialize the banner pool
         * @private
         */        
        init : function(){
            //test whether we actually have a valid container element, that actually contains items
            if(!config.container && $(config.container).children().length > 0){
                config.debuglogger("BannerPool: No container element or banner items found.");
                return;
            }
            
            //first determine all the banner items and store them in the array
            priv.items = $(config.container).children();
            
            //make sure the startindex actually exists
            config.startindex = Math.min(config.startindex, priv.items.length-1);
            priv.currentIndex = config.startindex;
            
            //make sure the rotationspeed is >= animationspeed
            config.rotationspeed = Math.max(config.rotationspeed, (config.animationspeed + 100)/1000);
            priv.timeToNextStep = config.rotationspeed*1000;
            
            //now determine the heighest element and set the parent element to this height
            if(config.allowoverrulesizes){
                priv.itemHeight = 0;
                priv.itemWidth = 0;
            
                $(priv.items).each(
                    function(index){
                        priv.itemHeight = Math.max($(this).height(), priv.itemHeight);
                        priv.itemWidth = Math.max($(this).height(), priv.itemHeight);
                        config.debuglogger("BannerPool height: " + priv.itemHeight + "\nWidth: " + priv.itemWidth);
                    }
                );
            }
            
            //now set the default styles
            priv.setItemDefaults();
                        
            if(config.autostart){
                priv.start();
            }
            
        },
                
        /**
         * Does the actual animation
         * @private
         */
        move : function(newIndex){		
            //first test whether the new index actually exists
            if(newIndex >= priv.items.length){
                //non existent item... do nothing
                config.debuglogger("BannerPool: not moving. Reason: non existing item");
                return;
            }
            //if newIndex is the same as currentIndex do nothing
            if(newIndex == priv.currentIndex){
                config.debuglogger("BannerPool: not moving. Reason: Same index.");
                return;
            }
            //make sure we are not locked
            if(priv.locked){
                //we are currently locked by another animation, do nothing
                config.debuglogger("BannerPool: not moving. Reason: locked.");
                return;
            }
            //lock for other moves
            priv.locked = true;
            
            config.debuglogger("BannerPool: moving. CurrentIndex: " + priv.currentIndex + ", NewIndex: " + newIndex);
            
            //(re)set the time to next step, for continue after mouseover/mouseleave actions
            priv.timerremain = new Date();
            priv.timeToNextStep = config.rotationspeed*1000;
            
            //now make the animation
            //make sure we have the new element starting at top z-index and opacity = 0
            $(priv.items[newIndex]).css({"z-index" : "10"});
            //position the current element at second z-index level
            $(priv.items[priv.currentIndex]).css({"z-index" : "5"});
                        
            //now fadeIn the new element
            $(priv.items[newIndex]).fadeIn(config.animationspeed, 
                function(){
                    
                    //on callback unlock
                    priv.locked = false;
                    //reset the z-index and opacity to defaults
                    $(priv.items[priv.currentIndex]).hide().css({"z-index" : "1"});
                    
                    //set the new currentIndex
                    priv.currentIndex = newIndex;
                    priv.currentItem = priv.items[newIndex];
                    
                    //make the callback after finishing
                    config.callback();
                }
            );
        },
        
        /**
         * Start the animation of the bannerpool.
         * @param {Boolean} continueAnimating Optional. Whether to automatically continue animating after a previous stop.
         * @private
         */
        start : function(continueAnimating){
            config.debuglogger("BannerPool: Starting rotation");
            //first clearany existing timer
            priv.clear();
            
            priv.rotating = true;
            priv.timerremain = new Date();
            
            //if we continue from a previous stop, make sure to start from the remaining time first
            if((typeof(continueAnimating) != "undefined" && continueAnimating == true)
                &&
                priv.timeToNextStep > 0){
                    //set the action on after first run
                    var moveNextAndStart = function(){
                        priv.next();
                        priv.start();
                    };
                    
                    priv.timer = window.setTimeout(moveNextAndStart, Math.min(priv.timeToNextStep, (config.rotationspeed*1000)));
                    config.debuglogger("BannerPool: Moving next in: " + priv.timeToNextStep + "ms");
                    return;
            }
                        
            //set the regular timer
            priv.timer = window.setInterval(priv.next, config.rotationspeed*1000);
        },
        
        /**
         * Stop the animation of the bannerpool, usually on mouse over.
         * @param {Boolean} continueAnimating Optional. Whether to automatically continue animating again after the mouse has left.
         * @private
         */
        stop : function(continueAnimating){
            config.debuglogger("BannerPool: Stopping rotation");
            priv.clear();
            //if continueAnimating is true, make sure it starts up again on mouseleave
            if(typeof(continueAnimating) == "undefined" || continueAnimating == false){
                config.debuglogger("BannerPool: Stop the rotating, continueAnimating : " + continueAnimating);
                priv.rotating = false;
            }
            else {
                //set the remaining time to the next step if we continue after mouseover/leave events
                var now = new Date();
                priv.timeToNextStep -= (now - priv.timerremain);
                config.debuglogger("BannerPool: timetonextstep: " + priv.timeToNextStep);
                priv.timerremain = new Date();
            }
        },
        
        /**
         * Clear the timer
         * @private
         */
        clear: function(){
            try {
                window.clearInterval(priv.timer);
                priv.timer = null;
            }
            catch(err){
                //for IE when timer has not been initialized
                priv.timer = null;
            }
            try {
                window.clearTimeout(priv.timer);
                priv.timer = null;
            }
            catch(err){
                //for IE when timer has not been initialized
                priv.timer = null;
            }
        },
        
        /**
         * Step to the next banner in the pool
         * @private
         */
        next : function(){
            var newIndex;
            //determine the next index:
            if(priv.items.length == 0 || priv.items.length == 1){
                //if we have no elements, or but one, do nothing
                return;
            }
            else if(priv.currentIndex + 1 < priv.items.length){
                //if we have a next object, go there
                newIndex = priv.currentIndex + 1;
            }
            else {
                //go to the first item
                newIndex = 0;
            }
            
            priv.move(newIndex);
        },
        
        /**
         * Step to the previous banner in the pool
         * @private
         */
        prev : function(){
            var newIndex;
            //determine the next index:
            if(priv.items.length == 0 || priv.items.length == 1){
                //if we have no elements, or but one, do nothing
                return;
            }
            else if(priv.currentIndex - 1 >= 0){
                //if we have a previous object, go there
                newIndex = priv.currentIndex - 1;
            }
            else {
                //go to the last item
                newIndex = priv.items.length - 1;
            }
            
            priv.move(newIndex);
        },
        
        /**
         * Sets the styles needed for the banners to function correctly
         * @private
         */
        setItemDefaults	: function(){			
            //first set the default styles for the container and items, needed to function correclty
            if(priv.itemHeight > 0){
                $(config.container).height(priv.itemHeight);
            }
            if(priv.itemWidth > 0){
                $(config.container).width(priv.itemWidth);
            }
            
            $(config.container).css({"overflow" : "hidden", "position" : "relative"});
            priv.items.hide().css({"position" : "absolute", "z-index" : "1"});
            $(priv.items[priv.currentIndex]).show().css({"z-index" : "10"});
            
            //now bind the default events
            //bind pause on mouseover
            $(config.container).hover(
                function(evt){
                    if(config.pauseonmouseover && priv.rotating){
                        //stop, but make sure we keep priv.rotating set to true so it starts again after mouseleave
                        priv.stop(true);
                    }
                },
                function(evt){
                    if(config.pauseonmouseover && priv.rotating){
                        //start, and make sure we we resume were we where at stop
                        priv.start(true);
                    }
                }
            );
        }
    };
    priv.init();
   
    /** @scope BannerPool */
    return {
        
        /**
         * Starts the animation of the bannerpool
         */
        Start : function(){
            priv.start();
        },
        
        /**
         * Stops the animation of the bannerpool
         */
        Stop : function(){
            priv.stop();
        },
        
        /**
         * Move to the next item in the pool
         */
        Next : function(){
            priv.next();
        },
        
        /**
         * Move to the previous item in the pool
         */
        Prev : function(){
            priv.prev();
        },
        
        /**
         * Directly step to the given item index
         * @param {Integer} newIndex the index of the item to move to
         */
        To : function(newIndex){
            priv.move(newIndex);
        }
    };
};

/*** End of file: generic\common\bannerpool.js ***/
