// (C) Copyright 2020 Hewlett-Packard Enterprise Company, L.P.
/**
 * @constructor
 * @type {NotificationsView}
 */
define(['hp/core/Notifications',
    'hp/presenter/ResourceNotificationsPresenter',
    'hp/view/NotificationDetailsView',
    'hp/core/UrlFragment',
    'hp/core/Style',  
    'text!hpPages/core/details_notifications.html',
    'jquery',
    'hp/lib/date',
    'hp/lib/jquery.hpStatus',
    'hp/lib/jquery.hpProgressBar',
    'hp/lib/jquery.hpTextFormat',
    'hp/lib/jquery.hpTimestamp'],
function(bannerNotifications, ResourceNotificationsPresenter, notificationDetailsView,
    urlFragment, style, notificationsHtml) {"use strict";

    var NotificationsView = (function() {

        var NOTIFICATION = '.hp-notification';
        var CONTROLS = '.hp-controls';
        var ASIDE = '.hp-aside';
        var SELECTOR = '.hp-selector';
        var ITEM = SELECTOR + ' li';
        var SPINNER = '.hp-spinner';
        var ACTIVITY_DETAILS = '.hp-activity-details';
        var BODY = '#hp-body-div';
        var AVAILABLE = 'hp-available';
        var ACTIVE = 'hp-active';
        var SELECTED = 'hp-selected';
        var OPEN = 'hp-notify-open';
        var UP = 38;
        var DOWN = 40;
        var SPACE = 32;
        var TAB = 9;
        var ESCAPE = 27;
        var STATUSES = ['error', 'warning', 'ok'];

        /**
         * Constructor
         */
        function NotificationsView() {

            var presenter = new ResourceNotificationsPresenter();
            var container = null;
            var allLocation;
            var template = null;
            var subTemplate = null;
            var selecting = false;
            var layoutTimer;
            var summaryLayoutTimer = null;
            // to animate the selected loading spinner more gracefully
            var selectionLoadingTimer = null;
            var selectionUri;
            // track whether caller is using pause/resume
            var initResume = true;
            var resumed = false;
            
            function fireRelayout() {
                // we don't delay here because there's no animation involved.
                container.parents('.hp-page').trigger('relayout');
            }
            
            function onMouseMove(event) {
               (($(event.target).parents(SELECTOR).length) === 0) &&
                (($(event.target).parents(NOTIFICATION).length) === 0) ? 
                  selecting = false : selecting = true;
            }
            
            function onMouseUp(event) {
                $(BODY).off('mousemove', onMouseMove);
                $(BODY).off('mouseup', onMouseUp);
            }
            
            function onMouseDown(event) {
                $(BODY).on('mousemove', onMouseMove);
                $(BODY).on('mouseup', onMouseUp);
            }
            
            var onKeypress = function() {};

            function deactivate(event) {
                if (! selecting) {
                    if (! event || $(event.target).parents(SELECTOR).length === 0) {
                        $(BODY).off('click', deactivate);
                        $(container).off('mousedown', onMouseDown);
                        $(document).off('keydown', onKeypress);
                        $(container).removeClass(ACTIVE);
                        presenter.wantDetails(false);
                    }
                } else {
                    selecting = false;
                }
            }

            function activate() {
                if (! $(container).hasClass(ACTIVE)) {
                    $(container).addClass(ACTIVE);
                    $(document).on('keydown', onKeypress);
                    presenter.wantDetails(true);
                    fireRelayout(); // avoid delay in Firefox
                    // delay to avoid flickering
                    setTimeout(function () {
                        $(container).on('mousedown', onMouseDown);
                        $(BODY).on('click', deactivate);}, 50);
                }
            }
            
            function layout() {
                var parent = $(NOTIFICATION, container).parent();
                var message = $(NOTIFICATION + ' .hp-notification-summary .hp-message', container);
                var notificationWidth = parent.innerWidth();
                var messageMaxWidth;
                var selector;
                var timestamp = $(ASIDE + ' .hp-timestamp', container); 

                if ($(CONTROLS, container).is(':visible')) {
                    notificationWidth -= $(CONTROLS, container).outerWidth();
                    messageMaxWidth = $(CONTROLS, container).offset().left - message.offset().left;
                    selector = $(CONTROLS + ' ' + SELECTOR + ':visible', container);
                    if (selector.length > 0) {
                        if (parent.height() - $(CONTROLS + ' header').outerHeight(true) > selector[0].scrollHeight /*selectorHeight*/) {
                            // The parent is larger, just keep the original height.
                            selector.css('height', 'auto');
                        } else {
                            selector.css('height', parent.height() - $(CONTROLS + ' header').outerHeight(true));
                        }
                    }
                } 
                if (timestamp.length > 0 && timestamp.is(':visible')) {
                    messageMaxWidth = timestamp.offset().left - message.offset().left;
                } else {
                    // Extra 50 padding for when only one alert present
                    messageMaxWidth = notificationWidth - 50;
                }
                // set notification width to avoid overlap with controls
                $(NOTIFICATION, container).css('width', notificationWidth - 1);
                // set message max-width so ellipsis works
                message.css('max-width', messageMaxWidth);
                // set parent min-width to avoid getting too small, 50/50
                parent.css('min-width', $(CONTROLS, container).outerWidth() * 2);
                // set the components visible or not
                displayComponents();
            }
            
            function displayComponents () {
                var message = $(NOTIFICATION + ' .hp-notification-summary .hp-message', container);
                var owner = $(ASIDE + ' .hp-owner', container);
                var steps = $(NOTIFICATION + ' .hp-notification-summary .hp-step', container);
                var state = $(NOTIFICATION + ' .hp-notification-summary .hp-state', container);
                var space = $(ASIDE + ' .hp-timestamp', container).offset().left - (message.offset().left + message.outerWidth(true));
                
                if (space < ( state.outerWidth(true) + owner.outerWidth(true) + steps.outerWidth(true))) {
                    owner.hide();
                    if (space < ( state.outerWidth(true) + steps.outerWidth(true))) {
                        steps.hide();
                    	if (space < state.outerWidth(true)) {
                            state.hide();
                        } else {
                            state.show();
                        }
                    } else {
                    	steps.show();
                    }
                } else {
                    // Show all
                    steps.show();
                    state.show();
                    owner.show();
                }
            }
            
            function onResize(event) {
                if (event.target == window) {
                    clearTimeout(layoutTimer);
                    layoutTimer = setTimeout(layout, 50);
                }
            }
            
            function hide() {
                if ($(container).hasClass(AVAILABLE)) {
                    $(container.removeClass(AVAILABLE));
                    $(container).parent().removeClass(OPEN);
                    fireRelayout();
                }
            }
            
            function show() {
                if (! $(container).hasClass(AVAILABLE)) {
                    $(container.addClass(AVAILABLE));
                    $(container).parent().addClass(OPEN);
                    fireRelayout();
                }
            }
            
            function updateStatusSummary(summary) {
                var status, length = STATUSES.length;
                for (var i=0; i<length; i++) {
                    status = STATUSES[i];
                    $('.hp-summary-' + status + ' .hp-count', container).
                        text(summary[status]);
                    $('.hp-summary-' + status + ' > .hp-status', container).
                        toggleClass('hp-unset', summary[status] <= 0);
                }
                $(CONTROLS, container).toggle(summary.total > 1);
                if (!summaryLayoutTimer) {
                    summaryLayoutTimer = setTimeout(function() {
                        summaryLayoutTimer = null;
                        layout();
                    }, 50);
                }
            }
            
            function updateSummary(notif) {
                var summary =
                    $(NOTIFICATION + ' header.hp-notification-summary', container);
                if (notif.status) {
                    $('> .hp-status', summary).
                        hpStatus(notif.status, (notif.changing && !notif.progress));
                } else {
                    $('> .hp-status', summary).attr('class', 'hp-status').text('');
                }
                // Don't replace if it hasn't changed,
                // allows user selection to persist.
                if ($('.hp-message', summary).text() !== notif.summary) {
                    $('.hp-message', summary).hpTextFormat(notif.summary);
                }
                if (notif.timestamp) {
                    $('.hp-timestamp', summary).hpTimestamp(notif.timestamp);
                } else {
                    $('.hp-timestamp', summary).text('');
                }
                if (notif.owner) {
                    $('.hp-owner', summary).text(notif.owner);
                } else {
                    $('.hp-owner', summary).text('');
                }
                if (false && notif.sourceName) {
                    if (notif.sourceUri) {
                        $('.hp-source', summary).
                            attr('href', notif.sourceUri).
                            text(notif.sourceName);
                    } else {
                        $('.hp-source-name', summary).
                            text(notif.sourceName);
                    }
                } else {
                    $('.hp-source', summary).text('');
                }
                // allow progress to be 0
                if (notif.changing && null !== notif.progress &&
                    undefined !== notif.progress) {
                    var progress = $('<div></div>').
                        hpProgressBar(parseInt(notif.progress, 10)).css('width', 100);
                    $('.hp-state .hp-duration', summary).remove();
                    $('.hp-state', summary).text('').append(progress);
                } else {
                    $('.hp-state', summary).text(notif.state ? notif.state : '');
                    if (notif.endTimestamp) {
                        // don't show if duration is 0 seconds
                        var start = Date.parseISOString(notif.timestamp);
                        var end = Date.parseISOString(notif.endTimestamp);
                        if (start && end && (end.getTime() - start.getTime()) > 1000) {
                            var duration = $('<span></span>').addClass('hp-duration').
                                text(notif.timestamp + '/' + notif.endTimestamp).
                                hpTimestamp();
                            $('.hp-state', summary).append(duration);
                        }
                    } else {
                        $('.hp-state .hp-duration', summary).remove();
                    }
                }
                if (notif.changing && notif.step) {
                    $('.hp-step', summary).hpTextFormat(notif.step);
                } else {
                    $('.hp-step', summary).text('');
                }
            }
            
            function resetDetails() {
                $(SPINNER, container).hide();
                notificationDetailsView.reset($('.hp-details-container', container));
            }

            function updateDetails(notif) {
                var location;
                
                if (selectionLoadingTimer && selectionUri === notif.uri) {
                    clearTimeout(selectionLoadingTimer);
                    selectionLoadingTimer = null;
                }
                
                // don't change while the user is selecting
                if (! selecting && notif) {
                  
                    $(SPINNER, container).hide();
                    
                    notificationDetailsView.update(
                        $('.hp-details-container', container), notif);
                    
                    $('.hp-close', container).toggleClass(ACTIVE, (notif.dismiss ? true : false));
                    
                    if (notif.uri && allLocation) {
                        location = allLocation;
                        if (allLocation.lastIndexOf('/activity', 0) >= 0) {
                            // global activity page
                            location = urlFragment.replaceUris(location, notif.uri);
                        } else {
                            // resource activity view
                            if (notif.sourceUri) {
                                location = urlFragment.replaceUris(location, [notif.sourceUri]);
                            }
                            location = urlFragment.replaceParameters(location,
                                {activityUri: notif.uri});
                        }
                        $(ACTIVITY_DETAILS, container).attr('href', '#' + location).show();
                    } else {
                        $(ACTIVITY_DETAILS, container).attr('href', '').hide();
                    }
                    
                    if ($(CONTROLS + ' ' + SELECTOR, container).is(':visible')) {
                        // relayout to update the selector height
                        clearTimeout(layoutTimer);
                        layoutTimer = setTimeout(layout, 50);
                    }
                }
            }
            
            function updateSelection(data) {
                $(ITEM).removeClass(SELECTED);
                if (data.selection.index >= 0) {
                    $(ITEM + ':nth-child(' + (data.selection.index + 1) + ')').
                        addClass(SELECTED);
                    updateSummary(data.selection.notification);
                }
                
          
                if (! data.selection.notification) {
                        // nothing there
                    resetDetails();
                    // if the data is not index service , populate details if available
                } else if (! data.selection.notification.indexResource ||
                    data.selection.resource) {
                        // this isn't an index resource or we already have it
                    selectionUri = null;
                    updateDetails(data.selection.notification);
                } else if (($(container).hasClass(ACTIVE)) &&
                         (selectionUri !== data.selection.notification.uri) ) {
                        // we are awaiting the resource data, show a spinner after a while
                    selectionUri = data.selection.notification.uri;
                    resetDetails();
                    clearTimeout(selectionLoadingTimer);
                        if (selectionUri) {
                            selectionLoadingTimer = setTimeout(function () {
                                $(SPINNER, container).show();
                            }, style.animationDelay());
                        }
                    }
                    // else: if we're already at selectionUri, nothing to do here
                
            }
            
            function addItem(notif) {
                var item = template.clone();
                item.attr('data-uri', notif.uri);
                if (notif.status) {
                    $('.hp-status', item).hpStatus(notif.status);
                }
                if (notif.timestamp) {
                    $('.hp-timestamp', item).
                        text(style.beautifyTimestamp(notif.timestamp));
                    item.attr('data-timestamp', notif.timestamp);
                }
                $('.hp-message', item).text(notif.summary);
                $(SELECTOR, container).append(item);
            }
            
            // Presenter events
            
            function onNotificationsChange(data) {
                var length;
                
                updateStatusSummary(data.summary);
                
                $(ITEM).remove();
                length = data.notifications.length;
                for (var i=0; i<length; i++) {
                    addItem(data.notifications[i]);
                }
                
                updateSelection(data);
                
                if (length > 0) {
                    show();
                } else {
                    hide();
                }
            }
            
            function onSelectionChange(data) {
                updateSelection(data);
            }
            
            function onNotificationChange(notification) {
                updateDetails(notification);
            }
            
            // DOM events
            
            onKeypress = function(ev) {
                var keyCode = (ev.which ? ev.which : ev.keyCode);
                if (keyCode == SPACE) {
                    activate();
                } else if (keyCode == ESCAPE || keyCode == TAB) {
                    deactivate();
                } else if (keyCode == UP) {
                    presenter.selectPrevious();
                    ev.preventDefault();
                } else if (keyCode == DOWN) {
                    presenter.selectNext();
                    ev.preventDefault();
                }
            };
            
            function onClickContainer(ev) {
                if (ev && ($(ev.target).hasClass('hp-view-all') || 
                    $(ev.target).hasClass('hp-activity-details'))) {
                    deactivate();
                    presenter.resetActivityResourceFilters();
                } else {
                    activate();
                }
            }
            
            function onDismiss(ev) {
                // TODO
                /*var notif = notifications[selectedIndex];
                if (notif.dismiss) {
                    notifications.splice(selectedIndex, 1);
                    updateItem(true);
                    notif.dismiss(notif);
                }
                ev.stopPropagation();*/
            }
            
            function onSelect(ev) {
                var item = $(ev.currentTarget);
                var uri = item.attr('data-uri');
                if (uri) {
                    presenter.selectUri(uri);
                } else {
                    presenter.selectTimestamp(item.attr('data-timestamp'));
                }
            }

            /**
             * @public
             * Add a notification message.
             * @param {notif} Object with properties as follows:
             *
             *    status: one of 'ok', 'error', 'warning', 'info'
             *    summary: minimal text summary, should start with an action if possible
             *    uri: identifier for this alert, task, etc.
             *          non-server-side notifications can use anything they want,
             *          this is used to allow updating progress of an existing
             *          notification.
             *    category: 'alerts' or 'tasks'
             *    timestamp: timestamp in ISO 8601 string format
             *    owner: user name for task or alert owner
             *    sourceName: user visible name for the source
             *    sourceUri: uri for the source
             *    sourceCategory: category for the source
             *    changing: true|false, whether the status should be anotated as changing
             *    progress: 0-100, a number indication what % of a task has been completed
             *    step: a short text description of the current progress step
             *    details: longer description that can have line breaks and links,
             *              will show up in a tooltip/flyout. Will use the hpTextFormat
             *              to format text.
             *    resolution: multi-line recommendation on how the user should proceed
             *    actions: Array of action links
             *    dismiss: Function called when the user dismisses
             *      function (notif){}
             *    children: child notif objects. Typically used for sub-tasks DEPRECATED
             *
             *    The only property that is required is the summary.
             *
             * @param expand Boolean expand any details right away
             * @param addActivity Boolean add to banner
             * @param flashBanner Boolean flash in banner
             *
             */
            this.add = function (notif, expand, addActivity, flashBanner) {

                presenter.add(notif);
                
                if (expand) {
                    activate();
                }
                if (addActivity) {
                    bannerNotifications.add(notif, flashBanner);
                }
            };

            /**
             * @public
             * Remove a notification message
             * @param {notif} Object with similar fields to the add().
             *    These fields are used in this order to find one to match:
             *      uri, timestamp + summary
             */
            this.remove = function (notif) {
                presenter.remove(notif);
            };

            this.clear = function () {
                presenter.clear();
            };

            /**
             * @public
             */
            this.init = function (context, allLocationArg) {
                var options;
                if (context.hasOwnProperty('length')) {
                    options = {context : context, allLocation: allLocationArg};
                } else {
                    options = context;
                }
                
                presenter.init(options.resource);
                allLocation = options.allLocation;
                
                container = $(notificationsHtml).filter('#hp-page-notifications');

                // insert HTML in context
                if ($('.hp-details-header', options.context).length > 0) {
                    $('.hp-details-header', options.context).after(container);
                } else if ($('.hp-master-header', options.context).length > 0) {
                    $('.hp-master-header', options.context).after(container);
                } else {
                    $('> header', options.context).after(container);
                }
                template = $(SELECTOR + ' .hp-template', container).remove().
                    removeClass('hp-template');
                subTemplate = $('.hp-sub-notifications .hp-template', container).remove().
                    removeClass('hp-template');

                $(container).on('click', onClickContainer);
                if (options.allLocation) {
                    $('.hp-view-all', container).attr('href', '#' + options.allLocation);
                } else {
                    $('.hp-view-all', container).hide();
                }
                $(container).on('click', '.hp-close', onDismiss);
                $(container).on('click', ITEM, onSelect);
                
                // allow partners to adopt pause/resume at their leisure
                this.resume();
                initResume = false;
            };
            
            this.pause = function () {
                if (resumed) {
                    resumed = false;
                    $('.hp-page').off('relayout', layout);
                    $(window).off('resize', onResize);
                    presenter.pause();
                    presenter.off('notificationsChange', onNotificationsChange);
                    presenter.off('selectionChange', onSelectionChange);
                    presenter.off('notificationChange', onNotificationChange);
                }
            };
            
            this.resume = function () {
                // allow either init() or caller to call this for now
                if (! resumed) {
                    resumed = true;
                    $('.hp-page').on('relayout', layout);
                    $(window).on('resize', onResize);
                    presenter.on('notificationsChange', onNotificationsChange);
                    presenter.on('selectionChange', onSelectionChange);
                    presenter.on('notificationChange', onNotificationChange);
                    presenter.resume();
                }
                if (! initResume) {
                    presenter.poll();
                }
            };
        }

        return NotificationsView;
    }());

    return NotificationsView;
});
