// (C) Copyright 2020 Hewlett-Packard Enterprise Company, L.P.
define(['hp/core/Sidebar',
    'hp/presenter/SessionNotificationsPresenter',
    'hp/view/NotificationDetailsView',
    'hp/model/DevelopmentSettings',
    'hp/core/LinkTargetBuilder',
    'hp/core/Localizer',
    'hp/core/Style',
    'jquery',
    'modernizr',
    'hp/lib/date',
    'hp/lib/jquery.hpStatus',
    'hp/lib/jquery.hpSafeClone',
    'hp/lib/jquery.hpTooltip',
    'hp/lib/jquery.hpTextFormat',
    'hp/lib/jquery.hpTimestamp'],
function (sidebar, SessionNotificationsPresenter, notificationDetailsView,
    settings, linkTargetBuilder, localizer, style) {
"use strict";

    var Notifications = (function () {

        var BRIEF = '#hp-activity-notification';
        var BRIEF_STATUS = BRIEF + ' .hp-status';
        var BRIEF_MESSAGE = BRIEF + ' .hp-message';
        var CONTROL = '#hp-activity-control';
        var CONTROL_COUNT = '#hp-activity-control-new-count';
        var NEW_COUNT = '#hp-activity-new-count';
        var FLYOUT = '#hp-activity-flyout';
        var CONTENTS = FLYOUT + ' .hp-flyout-contents';
        var LIST = '#hp-flyout-activities';
        var ITEM = LIST + ' .hp-activity';
        var FULL = '.hp-full';
        var SPINNER = '.hp-spinner';
        var TEMPLATE = '#hp-flyout-activity-template';
        var BODY = '#hp-body-div';
        var ACTIVE = 'hp-active';
        var NEW = 'hp-new';
        var SELECTED = 'hp-selected';
        var TAB = 9;
        var ESCAPE = 27;
        var UP_ARROW = 38;
        var DOWN_ARROW = 40;

        /**
         * @constructor
         * @type {Notifications}
         */
        function Notifications() {

            var presenter = new SessionNotificationsPresenter();
            var template = null;
            // to animate the selected loading spinner more gracefully
            var selectionLoadingTimer = null;
            var selectionUri;
            // references to elements
            var flyout;
            var control;
            
            function updateCount() {
                var count = $(ITEM + '.' + NEW, flyout).length;
                if (count > 0) {
                    $(CONTROL_COUNT, control).addClass(ACTIVE);
                } else {
                    $(CONTROL_COUNT, control).removeClass(ACTIVE);
                }
                $(CONTROL_COUNT, control).text(count);
                $(NEW_COUNT, control).text(count);
            }
            
            // forward references for Sonar
            var onKeypress = function() {};
            
            function deselectItem() {
                var item = $(ITEM + '.' + SELECTED);
                item.removeClass(SELECTED);
                $(BODY).off('click', deselectItem);
                $(document).off('keydown', onKeypress);
            }
            
            function selectItem(item) {
                item.addClass(SELECTED);
                // position next to activity, unless it would be off the bottom
                if (item.position().top + $(FULL, item).outerHeight() >
                    $(CONTENTS).position().top + $(CONTENTS).height() + 10) {
                    $(FULL, item).css('top',
                        Math.max(0, $(CONTENTS).position().top +
                            $(CONTENTS).height() -
                            $(FULL, item).outerHeight()));
                } else {
                    $(FULL, item).css('top', item.position().top);
                }
                $(document).on('keydown', onKeypress);
                // delay to avoid flickering
                setTimeout(function () {$(BODY).on('click', deselectItem);}, 50);
            }
            
            /**
             * Completely hide flyout.
             */
            function hideSidebar(remove) {
                if ($(FLYOUT).hasClass(ACTIVE)) {
                    deselectItem();
                    if (remove) {
                        sidebar.remove($(FLYOUT));
                    }
                    $(FLYOUT).removeClass(ACTIVE);
                    $(CONTROL).removeClass(SELECTED);
                }
            }
            
            /**
             * Put the flyout into the sidebar.
             */
            function showSidebar() {
                if (! $(FLYOUT).hasClass(ACTIVE)) {
                    $(FLYOUT).addClass(ACTIVE);
                    $(CONTROL).addClass(SELECTED);
                    sidebar.add($(FLYOUT));
                    $(ITEM + '.' + NEW).removeClass(NEW);
                    updateCount();
                }
            }
            
            function updateSummary(item, notif) {
                if (notif.status || notif.hasOwnProperty('changing')) {
                    if (notif.changing) {
                        $('.hp-status', item).hpStatus('changing');
                    } else {
                        $('.hp-status', item).hpStatus(notif.status);
                    }
                }
                $('.hp-activity-message', item).hpTextFormat(notif.summary);
                if (notif.timestamp) {
                    $('.hp-timestamp', item).show().hpTimestamp(notif.timestamp);
                } else {
                    $('.hp-timestamp', item).text('').hide();
                }
                if (notif.sourceName) {
                    $('.hp-activity-source', item).text(notif.sourceName);
                }
                if (notif.sourceUri) {
                    var link = $(linkTargetBuilder.makeLink(notif.sourceName, notif.sourceUri)).
                        addClass('hp-activity-source');
                    $(FULL + ' a.hp-activity-source', item).replaceWith(link);
                    $(FULL + ' a.hp-activity-source', item).show();
                    $(FULL + ' span.hp-activity-source', item).hide();
                } else {
                    $(FULL + ' a.hp-activity-source', item).hide();
                    $(FULL + ' span.hp-activity-source', item).show();
                }
            }
            
            function resetDetails(item) {
                $(SPINNER, item).hide();
                notificationDetailsView.reset($('.hp-details-container', item));
            }
            
            function updateDetails(item, notification) {
                if (selectionLoadingTimer && selectionUri === notification.uri) {
                    clearTimeout(selectionLoadingTimer);
                    selectionLoadingTimer = null;
                }
                $(SPINNER, item).hide();
                notificationDetailsView.update(
                    $('.hp-details-container', item), notification);                
            }
            
            function getItem(notification) {
                if (notification.uri) {
                    return $(ITEM + '[data-uri="' + notification.uri + '"]', flyout);
                } else {
                    return $(ITEM + '[data-timestamp="' + notification.timestamp + '"]', flyout);
                }
            }
            
            function addItem(notification) {
                var item = template.hpSafeClone();
                if (notification.uri) {
                    item.attr('data-uri', notification.uri);
                } else {
                    item.attr('data-timestamp', notification.timestamp);
                }
                item.addClass(ACTIVE);
                return item;
            }
            
            function addOrUpdate(notification, index) {
                var item = getItem(notification);
                if (item.length === 0) {
                    item = addItem(notification);
                }
                
                // put it in the right place
                if (index === 0) {
                    $(LIST, flyout).prepend(item);
                } else {
                    $(ITEM + ':nth-child(' + index + ')', flyout).after(item);
                }
                
                updateSummary(item, notification);
                
                if (! flyout.hasClass(ACTIVE) && notification._statusChanged) {
                    item.addClass(NEW);
                }
            }
            
            // Sidebar events
            
            function onSidebarRemove(element) {
                if (element.attr('id') === $(FLYOUT).attr('id')) {
                    hideSidebar();
                }
            }
            
            function onSidebarAdd(element) {
                if (element.attr('id') === $(FLYOUT).attr('id')) {
                    showSidebar();
                }
            }
            
            // Presenter events
            
            function onNotificationsChange(data) {
                var length, notification, current = {}, i;
                
                // add new ones, update existing ones
                length = data.notifications.length;
                for (i=0; i<length; i++) {
                    notification = data.notifications[i];
                    if (notification.uri) {
                        current[notification.uri] = true;
                    } else {
                        current[notification.timestamp] = true;
                    }
                    addOrUpdate(notification, i);
                }
                
                // remove old ones
                $(ITEM, flyout).each(function (index, item) {
                    var id = $(item).attr('data-uri');
                    if (! id) {
                        id = $(item).attr('data-timestamp');
                    }
                    if (! current[id]) {
                        $(item).remove();
                    }
                });
                
                updateCount();
                
                flyout.addClass('hp-notable');
            }
            
            function onSelectionChange(data) {
                var notification = data.selection.notification;
                if (notification) {
                    var item = getItem(notification);
                    var add = ! item.hasClass(SELECTED);
                    deselectItem();
                    if (add) {
                        selectItem(item);
                        
                        if (! data.selection.notification.indexResource ||
                            data.selection.resource) {
                            updateDetails(item, notification);
                        } else if (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, item).show();
                                }, style.animationDelay());
                            }
                        }
                    }
                } else {
                    deselectItem();
                }
            }
            
            function onNotificationChange(notification) {
                var item = getItem(notification);
                updateDetails(item, notification);
            }
            
            function onShowFlash(notification) {
                var bannerIconsWidth = $('#hp-main-banner .hp-header-secondary').outerWidth();
                if (! notification.status || 'unknown' != notification.status) {
                    $(BRIEF_STATUS).attr('class',
                        'hp-active hp-status hp-status-' + notification.status);
                } else {
                    $(BRIEF_STATUS).removeClass('hp-active');
                }
                $(BRIEF_MESSAGE).text(notification.summary +
                    (notification.sourceName ? (' ' + notification.sourceName) : ''));
                $(BRIEF).css('right',bannerIconsWidth);
                $(BRIEF).addClass(ACTIVE);
                if (Modernizr.audio && ! settings.disableAudio()) {
                    setTimeout(function () {
                        try {
                            var audio = new Audio();
                            audio.src = Modernizr.audio.mp3 ? 'media/notify.mp3' :
                                        'media/notify.ogg';
                            audio.play();
                        } catch(err) {
                            // do nothing
                        }
                    }, 1000);
                }
            }
            
            function onClearFlash() {
                $(BRIEF).css('right','');
            	  $(BRIEF).removeClass(ACTIVE);
            }
            
            // DOM events
            
            onKeypress = function (ev) {
                var keyCode = (ev.which ? ev.which : ev.keyCode);
                if (keyCode == ESCAPE || keyCode == TAB) {
                    deselectItem();
                } else if (keyCode == DOWN_ARROW) {
                    presenter.selectNext();
                    ev.preventDefault();
                } else if (keyCode == UP_ARROW) {
                    presenter.selectPrevious();
                    ev.preventDefault();
                }
            };
            
            function onMouseUp() {
                $(BODY).off('mouseup', onMouseUp);
            }
            
            function onScrollFull() {
                $(BODY).off('mouseup', onMouseUp);
            }
            
            function onMouseDown() {
                $(BODY).on('mouseup', onMouseUp);
            }
            
            function onClickFull(event) {
                //click event within the flyout should not close 
                //the flyout unless user clicked on a link
                if (event.target.tagName !== "A") {
                    event.preventDefault();
                    event.stopPropagation();
                }
            }
            
            function onClickItem(event) {
                var item = $(this).closest('.hp-activity');
                var add = !item.hasClass(SELECTED);
                if (item.attr('data-uri')) {
                    presenter.selectUri(item.attr('data-uri'));
                } else {
                    presenter.selectTimestamp(item.attr('data-timestamp'));
                }
                // if user re-selects the same item the flyout needs to show up
                if (add) {
                    selectItem(item);
                }
            }
            
            function onScroll() {
                presenter.deselect();
            }
            
            /** For Banner use only
             */
            this.init = function(banner, options) {
                if (typeof options === 'string') {
                    options = {placement: options};
                }
                
                presenter.init();
                template = $(TEMPLATE, banner);
                template.remove().attr('id', '').removeClass('hp-template');
                
                flyout = $(FLYOUT, banner);
                control = $(CONTROL, banner);
                
                if (! options.placement || options.placement === 'sidebar') {
                    $(BRIEF, banner).removeClass(ACTIVE);
                    $(CONTROL, banner).attr('tooltip',
                        localizer.getString('core.notify.control.tooltip')).
                        hpTooltip();
                    $(CONTROL, banner).click(function (ev) {
                        if (!$(CONTROL, banner).hasClass(SELECTED)) {
                            showSidebar();
                        } else {
                            hideSidebar(true);
                        }
                    });
                    sidebar.on('sidebarAdd', onSidebarAdd);
                    sidebar.on('sidebarRemove', onSidebarRemove);
                    
                    $(CONTENTS, banner).on('click', ITEM, onClickItem);
                    $(CONTENTS, banner).on('scroll', onScroll);
                    $(CONTENTS, banner).on('mousedown', ITEM, onMouseDown);
                    // click event within the details should not close the details
                    $(CONTENTS, banner).on('click', FULL, onClickFull);
                    // scroll event of details needs to stop capturing the mouseup
                    $(CONTENTS, banner).on('scroll', FULL, onScrollFull);
                    
                } else if (options.placement === 'inline') {
                    $(CONTROL, banner).parent().hide();
                }
                
                presenter.on('notificationsChange', onNotificationsChange);
                presenter.on('selectionChange', onSelectionChange);
                presenter.on('notificationChange', onNotificationChange);
                presenter.on('showFlash', onShowFlash);
                presenter.on('clearFlash', onClearFlash);
                
                if (options.poll) {
                    presenter.poll();
                }
            };
            
            /**
             * Turns on automatic polling for any Activity related to the current user.
             * The intent is that all partners eventually enable this but it is
             * turned off by default for now to allow graceful adoption.
             */
            this.poll = function () {
                presenter.poll();
            };
            
            /**
             * @public
             * Add a notification message.
             * @param {data} Object with properties as follows:
             *
             *    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.
             *    timestamp: timestamp in ISO 8601 string format
             *    sourceName: user visible name for the source
             *    sourceUri: uri for the source
             *    status: one of 'ok', 'error', 'warning', 'info'
             *    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
             *
             *    The only property that is required is the summary.
             *
             * @param flash Boolean flash in banner    
             */
            this.add = function (notif, flash) {
                presenter.add(notif, flash);
            };
            
            // Deprecated !!! restoring just for Fusion beta
            this.show = function (text, status) {
                this.add({summary: text, status: status}, true);
            };
        }

        return new Notifications();
    }());

    return Notifications;
});
