/**
 * @class brand.overlay 
 * Singleton class offers pop-over display functionality.
 * It supports one visible window at a time.
 *
 * Extension of brx.overlay. Additional features for Mac:
 * - showing pre-constructed popover (vs. creating a container for the content dynamically & inserting that into the DOM)
 * - args.foregroundNode
 * - args.removeOnHide
 * - args.displayDuration
 * - args.displayInline 
 */
brand.overlay = function() {
    var isVisible = false;
    var isCentered = true;
    var backgroundNode = null;
    var foregroundNode = null;
    var containerNode = null;
    var anchorNode = null;
    var offsetLeft = 0;
    var offsetTop = 0;
    var removeOnHide = true;
    var displayDuration = null;
    var durationTimer = null;
    var onClose = null;

    var scaleElementToPage = function(ele) {
        if (!Object.isElement(ele)) {
            return;
        }
        var docsize = $(document.body).getDimensions();
        ele.setStyle({
            height: docsize.height+'px',
            width: docsize.width+'px'
        });        
    };

    var centerElement = function(ele) {
        if (!Object.isElement(ele)) {
            return;
        }        
        var contentDimensions = ele.getDimensions();
        var windowScrollOffsets = document.viewport.getScrollOffsets();
        var windowDimensions = document.viewport.getDimensions();
        var yPosition;
        if (windowDimensions.height < contentDimensions.height) {
            yPosition = 0;
        } else {
            yPosition = (windowDimensions.height/2) - (contentDimensions.height/2) + (windowScrollOffsets.top);
        }
        ele.style.top = yPosition + "px";
        var xPosition = (windowDimensions.width/2) - (contentDimensions.width/2) + (windowScrollOffsets.left);
        ele.style.left = xPosition + "px";
    };

    var scrollHandler = function(evt) {
        if (isCentered) {
            centerElement(foregroundNode);            
        } else if (anchorNode) {
            foregroundNode.clonePosition(anchorNode, {setWidth: false, setHeight: false, offsetLeft: offsetLeft, offsetTop: offsetTop});
        }
    };
    var insertCloseLink = function(containerEle) {
        var closeLink = new Element("a", {"class": "close-link"});
        closeLink.insert(generic.rb.language.rb_close);
        var closeDiv = new Element("div", {"class": "close-container"});
        closeDiv.insert(closeLink);
        containerEle.insert({"top": closeDiv});
        closeLink.observe("click", function (closeClickEvt) {
            closeClickEvt.preventDefault();
            brand.overlay.hide();
        });
        return closeLink;
    };
    var hideSelects = function () {
        var selectNodes = $$("select");
        selectNodes.each( function(node) {
            node.addClassName("overlay-hidden");
        });
    };
    var restoreSelects = function() {
        var selectNodes = $$("select.overlay-hidden");
        selectNodes.each( function(node) {
            node.removeClassName("overlay-hidden");
        });
    };

    return {
        /**
         * This function displays a pop-over window. If a pop-over is already showing, the launch()
         * function will replace it with the new one.
         * @example 
         * // brand.overlay.launch({
         * //     content: htmlNode,
         * //     cssStyle: {
         * //         border: #000000 1px solid,
         * //         backgroundColor: #ffffff,
         * //         left: "100px",
         * //         top: "350px",
         * //         width: "250px",
         * //         height: "350px"
         * //     },
         * //     cssClass: 'product-overlay',
         * //     lockPosition: true,
         * //     includeBackground: false
         * // });
         * @param {Object} args.cssStyle Hash of CSS style definitions for the window. Uses JS notation (i.e., "marginLeft").
         * @param {Object} args.cssClass Name of CSS class that is optional to add to the overlay window.
         * @param {String|Node} args.content HTML, node, or text that will display in the window.
         * @param {Boolean} lockPosition if true, the overlay layer will remain anchored at the same x,y coords
         * when the user scrolls or resizes.
         * @param {Boolean} includeBackground if true, a background Node will cover the page directly behind the
         * overlay contents.
         * @param {Boolean} args.center, no value (undefined) centers the content, setting this to 'false' allows 
         * a top and left css value. 'undefined' was used to prevent having to add a 'true' value to every overlay
         * @param {Node} args.lockToDomNode node whose position will be inherited by the overlay element.
         * @param {Number} args.offsetTop vertical offset from lockToDomNode node. Only applies if lockToDomNode is included.
         * @param {Number} args.offsetLeft horizontal offset from lockToDomNode node. Only applies if lockToDomNode is included.
         * @param {String|Node} args.foregroundNode overlay container node, if it already exists in the DOM and doesn't need to be created 
         * @param {Boolean} args.displayInline keep overlay content in its specified place in the DOM. Default is false.
         * @param {Boolean} args.removeOnHide if true, remove the overlay foreground Node from DOM on hide. Default is true. 
         * @param {Number} args.displayDuration milliseconds to show ovleray, after which the overlay the overlay is closed/hidden
         */
        launch : function(args) {
            if (isVisible) { // check internal flag
                this.hide();
            }
            
            // set variables per args & reset undefined params to defaults
            removeOnHide = ((typeof args.removeOnHide !== "undefined") ? args.removeOnHide : true);
            onClose = (Object.isFunction(args.onClose) ? args.onClose : null);
            displayDuration = args.displayDuration;
            foregroundNode = (args.foregroundNode ? args.foregroundNode : null);
            
            if (!removeOnHide && foregroundNode) {
                // if overlay is not set to be destroyed on hide, check or set class name that 
                // identifies whether overlay behavior has been hooked up
                if (foregroundNode.hasClassName("overlay-created")) {
                    this.show(args);
                } else {
                    this.create(args);
                    foregroundNode.addClassName("overlay-created");
                }
            } else {
                this.create(args);
            }
            
            hideSelects();
            isVisible = true; // set internal flag
            
            if (displayDuration) {
                var self = this;
                var reset = function() {
                    clearTimeout(durationTimer);
                    self.hide();
                }
                durationTimer = setTimeout(reset, displayDuration);
            } else {
                clearTimeout(durationTimer);
            }
        },
        create : function(args) {
            var displayInline = (args.displayInline ? args.displayInline : false);
            
            if (!containerNode && !displayInline) { // can't retrieve body variable in function declaration b/c it hasn't finished loading
                containerNode = $(document.body);
            }
            if (args.includeBackground) {
                if (!backgroundNode) { // create background node, if necessary
                    backgroundNode = new Element('div', {"class":"overlay-background", style:"display:none"});
                    containerNode.insert(backgroundNode);
                }
                backgroundNode.style.display = "block";
                scaleElementToPage(backgroundNode);
            }
            if (!foregroundNode) { // create foreground node, if necessary
                foregroundNode = new Element('div', {"class":"overlay-container", style:"display:none"});
                containerNode.insert(foregroundNode);
            }
            // insert elements into DOM if needed and adjust layout
            if (args.content) {
                foregroundNode.insert(args.content);
            }
            foregroundNode.style.display = "block";
            if (args.cssStyle) {
                foregroundNode.setStyle(args.cssStyle);
            }
            if (args.cssClass) {
                foregroundNode.addClassName(args.cssClass);
            }
            var closeLinks = foregroundNode.select(".close-link"); // look for a close link
            if (closeLinks.length < 1) {
                var newCloseLink = insertCloseLink(foregroundNode); // Insert link if one is not found
                closeLinks.push(newCloseLink);
            }
            var self = this;
            closeLinks.each( function(link) { // attach event handler to close links
                link.observe("click", function(clickEvt) {
                    self.hide();
                });
            });
            // attach events for scroll & resize
            if (!args.lockPosition && !displayInline) {
                Event.observe( window, 'resize', scrollHandler );
                Event.observe( window, 'scroll', scrollHandler );
            }
            if (args.center == undefined && !displayInline) {
                isCentered = true;
            } else {
                isCentered = !!args.center;
            }
            if (isCentered) {
                centerElement(foregroundNode);
            }
            if (Object.isElement(args.lockToDomNode)) {
                if (args.offsetLeft) {
                    offsetLeft = args.offsetLeft;
                } else {
                    offsetLeft = 0;
                }
                if (args.offsetTop) {
                    offsetTop = args.offsetTop;
                } else {
                    offsetTop = 0;
                }
                anchorNode = args.lockToDomNode;
                foregroundNode.clonePosition(anchorNode, {setWidth: false, setHeight: false, offsetLeft: offsetLeft, offsetTop: offsetTop});
            } else {
                anchorNode = null;
            }
        }, 
        /**
         * This function shows a pop-over that's already been created and 
         * just needs hide/show toggling
         */
        show: function() {
            if (Object.isElement(foregroundNode)) {
                foregroundNode.style.display = "block";
            }
        },
        /**
         * This function "closes" the pop-over window. If this.recreateOnShow 
         * is set to true: It completely removes the foreground node and its children
         * from the DOM.  Otherwise, foregroundNode is just hidden.
         * The background element is set to display: none.
         */
        hide: function() {
            isVisible = false; // set internal flag
            restoreSelects();
            // clean up DOM and layout
            if (Object.isElement(foregroundNode)) {
                // MERGE NOTE: want option of removing or just hiding overlay
                if (removeOnHide) {
                    // remove events for scroll & resize
                    Event.stopObserving( window, 'resize', scrollHandler );
                    Event.stopObserving( window, 'scroll', scrollHandler );
                    // remove overlay node
                    foregroundNode.remove();
                    foregroundNode = null;
                } else {
                    foregroundNode.style.display="none";
                }
            }
            if (Object.isElement(backgroundNode)) {
                backgroundNode.style.display="none";
            }
            if (onClose) onClose();
        },
        /**
         * This function scans the DOM for <a class="overlay-links"> elements. It takes the href attribute
         * from those links and preloads that URL via AJAX into a hidden DIV. This div is used
         * as the content for an overlay window when the link is clicked.
         */
        initLinks: function() {
            var linksToModify = $$("a.overlay-link");
            linksToModify.each( function(link) {
                if (link.hasClassName("overlay-ready")) {
                    return;
                }
                var styleObj = {};
                var linkClassNames = link.className;
                var cssClass = "";
                var widthRegexResults = linkClassNames.match(/overlay-width-(\d+)/);
                if (widthRegexResults) {
                    styleObj.width = widthRegexResults[1] + "px";
                }
                var heightRegexResults = linkClassNames.match(/overlay-height-(\d+)/);
                if (heightRegexResults) {
                    styleObj.height = heightRegexResults[1] + "px";
                }
                var cssClassRegexResults = linkClassNames.match(/overlay-addclass-([a-z\-\_]+)/);
                if (cssClassRegexResults) {
                    cssClass = cssClassRegexResults[1];
                }
                
                var containerDiv = new Element("div");
                containerDiv.style.display = "none";
                document.body.appendChild(containerDiv);
                containerDiv.addClassName("overlay-content-container");
                
                var req = new Ajax.Request(link.href, {
                    method:'get',
                    onSuccess: function(transport) {
                        var response = transport.responseText || "no response text";
                        containerDiv.update(response);
                    },
                    onFailure: function(){
                        var errMsg = "Error loading " + link.href
                        containerDiv.update(errMsg);
                    }
                });
                link.observe("click", function(clickEvt) {
                    clickEvt.preventDefault();
                    containerDiv.style.display = "block";
                    brand.overlay.launch({
                        content: containerDiv,
                        includeBackground: true,
                        cssStyle: styleObj,
                        cssClass: cssClass
                    });
                });
                link.addClassName("overlay-ready");
            }); // end linksToModify.each()
            
        },
        /**
         * This function is used to fetch specific rb keys needed for the
         * overlay. It is called on dom::loaded so each key is only 
         * fetched once.  
         * The specific language bundle is needed to be included in each page 
         * template header.  If not, the key name called will be returned.
        */
        getRBKeys: function() {
            generic.rb.language = generic.rb("language");
            generic.rb.language.rb_close = generic.rb.language.get('close');
        }
    };
}();

