// (C) Copyright 2020 Hewlett-Packard Enterprise Company, L.P.

define(['hp/core/Router', 'hp/core/Localizer', 'hp/core/UrlFragment', 'jquery', 'hp/lib/jquery.hpSelect'],
function(router, localizer, urlFragment) {
    (function($) {
        // jQuery plugin definition
        $.fn.hpStackedPanels = function(options) {
            
            var VISIBLE_PANEL = '> li:visible';
            var instance;

            function hpStackedPanels(elem) {
              
                // Changes can come from three places:
                // Scrolling
                //    find panel first visible panel, if changed
                //    update title, replace route location
                //
                // Route location changing
                //    find panel with the matching anchor, if changed
                //    start scrolling to it -> see Scrolling above
                //
                // Caller focuses on an input element
                //    find panel with the matching anchor, if changed
                //    start scrolling to it -> see Scrolling above
                
                var panels;
                var panelSelector;
                var updateSelector = false;
                var focusPanel; // the first panel with any visible region
                var scrollToPanel; // where we're scrolling to if the route changed
                var paused = false;
                var scrolling = false; // used by scrollToAnchor() for disabling onScroll handler
                var panelSelectorOnClickRegistered = false;
                
                function setHeight(elem) {
                    var hasSinglePanel = $(VISIBLE_PANEL, elem).length === 1;
                    
                    var fullHeight = Math.max(200, $(elem).parent().height());
                    var last = $(VISIBLE_PANEL, elem).last();
                    var lastHeight = last.height();
                    
                    // Set bottom padding to 0 when there is only one panel visible
                    // For multiple panels, adjust the paddingBottom of the last element so we can scroll
                    // it to the top.
                    var padding = hasSinglePanel ? 0 : Math.max(0, (fullHeight - lastHeight));
                    
                    last.css({paddingBottom: padding}); 
                }
                
                function pinPanelHeader(panel) {
                    $(panel).addClass('hp-active');
                }
                
                function unpinPanelHeader(panel) {
                    $(panel).removeClass('hp-active');
                }
                
                function isBehindModalDialog() {
                    var behindModalDialog = false;
                    //check to see whether there is an active dialog 
                    var modalDialog = $('#hp-body-div').children(".hp-dialog-overlay.hp-active");
                    //verify that we are not a child of this active dialog
                    if (modalDialog.length !== 0) {  
                        behindModalDialog = $(modalDialog).find("#" +$(panels).attr('id')).length === 0;
                    }
                    return behindModalDialog ;
                }                
                
                function setRouteAndTitleForAnchor(anchor) {
                    if (anchor && !paused) {
                        if (updateSelector) {
                            // align panel selector with route location
                            $(panelSelector).hpSelect('set', $(anchor).text());
                            // align route location with anchor and keep existing query parameters
                                                
                            // if there is an active dialog dont' change the route                    
                            if (!isBehindModalDialog()) {
                                var uri = $(anchor).attr('href').substr(1);
                                router.replaceWith(urlFragment.replaceParameters(uri,
                                    $.extend(
                                         urlFragment.getParameters(uri),
                                         urlFragment.getParameters(router.location()))),
                                   'stacked panel');
                            }
                        } else {
                            $('li', panelSelector).removeClass('hp-selected');
                            $(anchor).parent('li').addClass('hp-selected');
                        }
                    }
                }
                
                function getPanelForAnchor(anchor) {
                    var panel = $('#' + $(anchor).data('panel-id'), panels);
                    return (panel.length === 1 ? panel[0] : null);
                }
                
                function getAnchorForPanel(panel) {
                    var anchor =
                        $('a[data-panel-id="'+$(panel).attr('id')+'"]', panelSelector);
                    return (anchor.length === 1 ? anchor[0] : null);
                }
                
                function getAnchorForLocation(location) {
                    var anchor = $('a[href^="' + (location.split('?')[0]) + '"]', panelSelector);
                    return (anchor.length === 1 ? anchor[0] : null);
                }
                

                
                function focusOnPanel(panel) {
                    // no-op if we're already focusing on this panel
                    if ( focusPanel !== panel ) {
                        unpinPanelHeader(focusPanel);
                        
                        focusPanel = panel;
                        // if we aren't scrolling due to a location change or
                        // we are but this is the panel we are going to.
                        if (! scrollToPanel || scrollToPanel === focusPanel ) {
                            var anchor = getAnchorForPanel(focusPanel);
                            setRouteAndTitleForAnchor(anchor);
                            scrollToPanel = null;
                        }
                        pinPanelHeader(focusPanel);
                    }
                }
                
                function onScroll() {
                    var targetPanel = null;
                    var topOffset = 10;
                    var focused = false;
                    // We potentially have to unpin all panels, in case we
                    // jump from the last to the first
                    $('> li:visible', elem).each(function(index, panel) {
                      
                        if (! targetPanel) {
                            targetPanel = panel;
                        }
                        
                        // We focus on the panel immediately before the one
                        // who's top is passed the top of the container.
                        // We vary topOffset to avoid jumpiness at the transition
                        // points.
                        if ($(panel).position().top > topOffset) {
                            focusOnPanel(targetPanel);
                            focused = true;
                            return false;
                        }
                        
                        targetPanel = panel;
                        if (targetPanel === focusPanel) {
                            topOffset = 0;
                        }
                    });
                    
                    if (targetPanel && ! focused) {
                        // If we have only one panel, we'll be here
                        focusOnPanel(targetPanel);
                        return false;
                    }
                }
                
                function scrollToAnchor(anchor) {
                    var panel;
                    var currentScrollTop; // current panels scrollTop
                    var scrollTo; // what we'd like to change the scrollTop to
                    var scrolledToPanel; // the panel we are scrolling to which we return
                    
                    scrolling = true;
                    $(elem).off('scroll.hpStackedPanels', onScroll);
                    // scroll to location and focus
                    panel = getPanelForAnchor(anchor);
                    if (panel && panel !== focusPanel) {
                        currentScrollTop = Math.floor($(panels).scrollTop());
                        scrollTo =  Math.ceil(currentScrollTop + $(panel).position().top);
                        if ($(panels).scrollTop() === scrollTo) {
                            onScroll(); // in case we're resuming and need to reset
                        } else {
                            scrollToPanel = panel;
                            $(panels).animate({scrollTop: scrollTo}, 300,
                                function () {
                                    // if content has changed such that
                                    // where we are focused isn't where we want
                                    // to scroll to, re-scroll
                                    if (scrollToPanel && scrollToPanel !== focusPanel) {
                                        scrollToAnchor(anchor);
                                    }
                                });
                        }
                        scrolledToPanel = panel;
                    }
                    scrolling = false;
                    if (!paused) {
                        $(elem).off('scroll.hpStackedPanels', onScroll).on('scroll.hpStackedPanels', onScroll);
                    }
                    return scrolledToPanel;
                }
                
                function changeToAnchor(anchor) {
                    if (anchor) {
                        scrollToAnchor(anchor);
                    }
                }
                
                function onResize() {
                    setHeight(elem);
                }

                function onClickPanelSelectorLink(event) {
                    scrollToAnchor(getAnchorForLocation($(event.currentTarget).attr('href')));
                }
                
                function registerPanelSelectorClickHandlers() {
                    if (panelSelector && !panelSelectorOnClickRegistered) {
                    	panelSelectorOnClickRegistered = true;
                        $('a', panelSelector).each( function (index, anchorElem) {
                            // Only add hp-route-replace if it's a section in stacked panel
                            if (getPanelForAnchor(anchorElem)) {
                                $(anchorElem).addClass('hp-route-replace').on('click', onClickPanelSelectorLink);
                            }
                        });
                    }                
                }

                function unregisterPanelSelectorClickHandlers() {
                    if (panelSelector) {
                    	panelSelectorOnClickRegistered = false;
                        $('a', panelSelector).each( function (index, anchorElem) {
                            // Only remove hp-route-replace if it's a section in stacked panel
                            if (getPanelForAnchor(anchorElem)) {
                                $(anchorElem).removeClass('hp-route-replace').off('click', onClickPanelSelectorLink);
                            }
                        });
                    }                
                }

                this.pause = function() {
                    paused = true;
                    $(window).off('resize.hpStackedPanels');
                    $(elem).off('scroll.hpStackedPanels', onScroll);
                    focusPanel = null;
                    $('.hp-stacked-panel', panels).removeClass('hp-active');
                    unregisterPanelSelectorClickHandlers();
                };
                
                this.resume = function() {
                    paused = false;
                    $(window).on('resize.hpStackedPanels', onResize);
                    if (!scrolling) { // Prevent panel from jumping around when doing scrollToAnchor()
                        $(elem).off('scroll.hpStackedPanels', onScroll).on('scroll.hpStackedPanels', onScroll);
                    }
                    setTimeout(function () {setHeight(elem);}, 100);
                    registerPanelSelectorClickHandlers();
                };

                this.reset = function() {
                    scrolling = true; // Prevent resume() from adding onScroll until reset() finishes
                    // remove onScroll handler and let scrollToAnchor handle the rest
                    $(elem).off('scroll.hpStackedPanels', onScroll);
                    setTimeout(function () {
                        var anchor = getAnchorForPanel(scrollToPanel ? scrollToPanel : focusPanel);
                        focusPanel = null;
                        setHeight(panels);
                        scrollToAnchor(anchor);
                    }, 100);
                };

                //onScroll should not be listening when panel data
                //is being rendered or removed dynamically
                this.preReset = function () {
                    $(elem).off('scroll.hpStackedPanels', onScroll);
                };

                this.initialize = function(elem) {
                    panels = elem;
                    
                    if (options && options.panelSelector) {
                        panelSelector = $(options.panelSelector);

                        if (panelSelector.hasClass('hp-select')) {
                            $(panelSelector).hpSelect();
                            // make sure selector has a view label
                            if (! $('> label', panelSelector).text()) {
                                $('> label', panelSelector).
                                    text(localizer.getString('core.details.view'));
                            }
                            
                            updateSelector = true;
                        }
                        registerPanelSelectorClickHandlers();
                    }
                    $(elem).on('scroll.hpStackedPanels', onScroll);
                    setTimeout(function () {setHeight(elem);}, 100);
                    
                    
                };
                
                this.setPanelByLocation = function (location) {
                    var anchor = getAnchorForLocation(location);
                    changeToAnchor(anchor);
                };
                
                this.setFirstPanel = function () {
                    var anchor = $('a[href]:first', panelSelector);
                    changeToAnchor(anchor);
                };
            }
            
            if ( typeof options === 'string' ) { 
                // call method
                var args = Array.prototype.slice.call( arguments, 1 );

                this.each(function() {
                    instance = $.data( this, 'hpStackedPanels' );
                    if ( !instance ) {
                        if (window.console) {
                            window.console.error(
                                "cannot call methods on hpStackedPanels prior to initialization; " +
                                "attempted to call method '" + options + "'" );
                        }
                        return;
                    }
                    if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) {
                        if (window.console) {
                            window.console.error(
                                "no such method '" + options + "' for hpStackedPanels instance" );
                        }
                        return;
                    }
                    // apply method
                    instance[ options ].apply( instance, args );
                });  
            } else {
                // pluginify
                var ret;
                this.each(function() {
                    var $elem = $(this);
                    instance = new hpStackedPanels($elem[0]);
                    $.data(this, 'hpStackedPanels', instance);
                    instance.initialize($elem[0]);
                    ret = ret ? ret.add($elem) : $elem;
                });
                return ret;
            }
        };
    }(jQuery));
});
