/**
 * This module provides an api for handling content objects e.g. Posts, Polls...
 *
 * @type undefined|Function
 */

humhub.module('space.chooser', function (module, require, $) {
    var event = require('event');
    var space = require('space');
    var client = require('client');
    var ui = require('ui');
    var Widget = ui.widget.Widget;
    var object = require('util').object;
    var pjax = require('client.pjax');
    var additions = require('ui.additions');
    var user = require('user');
    var view = require('ui.view');

    var SELECTOR_ITEM = '[data-space-chooser-item]';
    var SELECTOR_ITEM_REMOTE = '[data-space-none],[data-space-archived]';

    var SpaceChooser = Widget.extend();

    SpaceChooser.prototype.init = function () {
        this.$menu = $('#space-menu');
        this.$chooser = $('#space-menu-spaces');
        this.$search = $('#space-menu-search');
        this.$remoteSearch = $('#space-menu-remote-search');


        if (view.isLarge()) {
            // set niceScroll to SpaceChooser menu
            this.$chooser.niceScroll({
                cursorwidth: "7",
                cursorborder: "",
                cursorcolor: "#555",
                cursoropacitymax: "0.2",
                nativeparentscrolling: false,
                railpadding: {top: 0, right: 3, left: 0, bottom: 0}
            });

            this.$chooser.on('touchmove', function (evt) {
                evt.preventDefault();
            });
        }

        this.initEvents();
        this.initSpaceSearch();
        this.initMessageCounters();
    };

    SpaceChooser.prototype.initEvents = function () {
        this.lazyLoad = module.config.lazyLoad && !this.hasItems();

        var that = this;

        // Forward click events to actual link
        this.$.on('click', SELECTOR_ITEM, function (evt) {
            if (this === evt.target) {
                $(this).find('a')[0].click();
            }
        });

        // Focus on search on open and clear item selection when closed
        this.$menu.parent().on('shown.bs.dropdown', function () {
            if (!view.isSmall()) {
                that.$search.focus();
            }

            if (that.lazyLoad) {
                that.triggerRemoteSearch('');
            }
        }).on('hidden.bs.dropdown', function () {
            that.clearSelection();
        });

        // The dropdown toggle is plain Bootstrap markup and can be opened by the user
        // before this widget has finished initializing (e.g. while the page/assets are
        // still loading). In that case shown.bs.dropdown already fired with no listener
        // attached yet, so the lazy space list never gets requested and the dropdown is
        // left empty. Detect that state here and kick off the load immediately.
        if (this.lazyLoad && this.$.hasClass('show')) {
            this.triggerRemoteSearch('');
        }

        if (!pjax.isActive()) {
            return;
        }

        // Set no space icon for non space views and set space icon for space views.
        event.on('humhub:ready', function () {
            if (!space.isSpacePage()) {
                that.setNoSpace();
            }
        }).on('humhub:space:changed', function (evt, options) {
            that.setSpace(options);
        }).on('humhub:space:archived', function (evt, space) {
            that.removeItem(space);
        }).on('humhub:space:unarchived', function (evt, space) {
            that.prependItem(space);
        }).on('humhub:modules:content:live:NewContent', function (evt, liveEvents) {
            that.handleNewContent(liveEvents);
        });
    };

    SpaceChooser.prototype.initMessageCounters = function () {
        $('[data-space-guid] [data-message-count]').each(function () {
            $(this).toggle($(this).data('message-count') > 0);
        });
    }

    SpaceChooser.prototype.handleNewContent = function (liveEvents) {
        var that = this;
        var increments = {};

        liveEvents.forEach(function (event) {
            if (event.data.uguid || event.data.originator === user.guid() || event.data.silent) {
                return;
            }

            if (increments[event.data.sguid]) {
                increments[event.data.sguid]++;
            } else {
                increments[event.data.sguid] = 1;
            }
        });

        $.each(increments, function (guid, count) {
            that.incrementMessageCount(guid, count);
        });
    };

    SpaceChooser.prototype.incrementMessageCount = function (guid, count) {
        var $messageCount = $('[data-space-guid="' + guid + '"]').find('[data-message-count]');
        var newCount = $messageCount.data('message-count') + count;

        $messageCount.addClass('d-none').text(newCount).data('message-count', newCount);
        setTimeout(function () {
            $messageCount.removeClass('d-none');
        }, 100);
    };

    SpaceChooser.prototype.prependItem = function (space) {
        if (!this.findItem(space).length) {
            var $space = $(space.output);
            this.$chooser.prepend($space);
            additions.applyTo($space);
        }
    };

    SpaceChooser.prototype.appendItem = function (space) {
        if (!this.findItem(space).length) {
            var $space = $(space.output);
            this.$chooser.append($space);
            additions.applyTo($space);
        }
    };

    SpaceChooser.prototype.findItem = function (space) {
        var guid = object.isString(space) ? space : space.guid;
        return this.$.find('[data-space-guid="' + guid + '"]');
    };

    SpaceChooser.prototype.removeItem = function (space) {
        var guid = object.isString(space) ? space : space.guid;
        this.getItems().filter('[data-space-guid="' + guid + '"]').remove();
    };

    SpaceChooser.prototype.initSpaceSearch = function () {
        var that = this;

        $('#space-search-reset').click(function () {
            that.resetSearch();
        });

        $('#space-directory-link').on('click', function () {
            that.$menu.trigger('click');
        });

        this.$search.on('keyup', function (event) {
            var $selection = that.getSelectedItem();
            switch (event.keyCode) {
                case 40: // Down -> select next
                    if (!$selection.length) {
                        SpaceChooser.selectItem(that.getFirstItem());
                    } else if ($selection.nextAll(SELECTOR_ITEM + ':not(.d-none)').length) {
                        SpaceChooser.deselectItem($selection)
                            .selectItem($selection.nextAll(SELECTOR_ITEM + ':not(.d-none)').first());
                    }
                    break;
                case 38: // Up -> select previous
                    if ($selection.prevAll(SELECTOR_ITEM + ':not(.d-none)').length) {
                        SpaceChooser.deselectItem($selection)
                            .selectItem($selection.prevAll(SELECTOR_ITEM + ':not(.d-none)').first());
                    }
                    break;
                case 13: // Enter
                    if ($selection.length) {
                        $selection.find('a')[0].click();
                    }
                    break;
                default:
                    that.triggerSearch();
                    break;
            }
        }).on('keydown', function (event) {
            if (event.keyCode === 13) {
                event.preventDefault();
            }
        }).on('focus', function () {
            $('#space-directory-link').addClass('focus');
        }).on('blur', function () {
            $('#space-directory-link').removeClass('focus');
        });
    };

    SpaceChooser.prototype.triggerSearch = function () {
        var input = this.$search.val().toLowerCase();

        // Don't repeat the search querys
        if (this.$search.data('last-search') === input) {
            return;
        }

        // Reset search if no input is given, else fade in search reset
        if (!input.length) {
            this.resetSearch();
            return;
        } else {
            $('#space-search-reset').fadeIn('fast');
        }

        // Filter all existing items and highlight text
        this.filterItems(input);
        this.highlight(input);

        this.triggerRemoteSearch(input);
    };

    SpaceChooser.prototype.filterItems = function (input) {
        this.clearSelection();
        this.$search.data('last-search', input);

        // remove max-height property to hide the nicescroll scrollbar in case of search input
        this.$chooser.css('max-height', ((input) ? 'none' : '400px'));

        this.getItems().each(function () {
            var $item = $(this);
            var itemText = $item.text().toLowerCase();

            // Show only space items where a keyword is searched
            if (itemText.search(input) >= 0) {
                $item.removeClass('d-none');
                // Display space tags only if a keyword is searched inside the tags
                var $spaceTags = $item.find('.space-tags');
                if ($spaceTags.length) {
                    $spaceTags.toggle($spaceTags.text().toLowerCase().search(input) >= 0);
                }
            } else {
                $item.addClass('d-none'); // Do not use hide() because the d-flex class is set in Bootstrap CSS to flex !important
            }
        });

        // Select the first item if search was successful
        SpaceChooser.selectItem(this.getFirstItem());
    };

    SpaceChooser.prototype.highlight = function (input, selector) {
        this.$chooser.find(selector || SELECTOR_ITEM).unhighlight().highlight(input);
    };

    SpaceChooser.prototype.triggerRemoteSearch = function (input) {
        var that = this;

        this.remoteSearch(input).then(function (data) {
            if (data === true) { //Outdated result, just ignore this...
                return;
            } else if (!data) {
                that.onChange(input);
                return;
            }

            $.each(data, function (index, space) {
                that.appendItem(space);
            });

            that.initMessageCounters();
            that.highlight(input, SELECTOR_ITEM_REMOTE);
            that.onChange(input);

            // make sure lazy load is disabled to prevent future duplication
            that.lazyLoad = false;
        }).catch(function (e) {
            if (!e.textStatus === "abort") {
                module.log.error(e, true);
            }
        });
    };

    SpaceChooser.prototype.remoteSearch = function (input) {
        var that = this;
        return new Promise(function (resolve, reject) {
            if (that.currentXhr) {
                that.currentXhr.abort();
            }

            // Clear all current remote results not matching the current search
            that.clearRemoteSearch(input);

            var url = module.config.remoteSearchUrl;
            if (that.lazyLoad && module.config.lazySearchUrl) {
                url = module.config.lazySearchUrl;
            }

            if (!url) {
                reject('Could not execute space remote search, set data-space-search-url in your space search input');
                return;
            } else if (input.length < 2 && !that.lazyLoad) {
                resolve(false);
                return;
            }

            var searchTs = Date.now();
            var options = {
                data: that.lazyLoad ? {} : {keyword: input, target: 'chooser'},
                beforeSend: function (xhr) {
                    that.currentXhr = xhr;
                }
            };

            ui.loader.set(that.$remoteSearch, {'wrapper': '<div class="d-flex">', 'css': {padding: '5px'}});

            client.get(url, options).then(function (response) {
                that.currentXhr = undefined;
                var lastSearchTs = that.$remoteSearch.data('last-search-ts');
                var isOutDated = lastSearchTs && lastSearchTs > searchTs;
                var hastData = response.data && response.data.length;

                if (!isOutDated) {
                    that.$remoteSearch.empty();
                }

                // If we got no result we return
                if (!hastData || isOutDated) {
                    resolve(isOutDated);
                } else {
                    that.$remoteSearch.data('last-search-ts', searchTs);
                    resolve(response.data);
                }
            }).catch(reject);
        });
    };

    /**
     * Clears all remote results which do not match with the input search.
     * If no input is given, all remote results will be removed.
     *
     * @param {string} input search filter
     * @returns {undefined}
     */
    SpaceChooser.prototype.clearRemoteSearch = function (input) {
        // Clear all non member and non following spaces
        this.$chooser.find(SELECTOR_ITEM_REMOTE).each(function () {
            var $this = $(this);
            if (!input || !input.length || $this.find('.space-name').text().toLowerCase().search(input) < 0) {
                $this.remove();
            }
        });
    };

    SpaceChooser.prototype.resetSearch = function () {
        $('#space-search-reset').fadeOut('fast');
        this.clearRemoteSearch('');

        if (!view.isSmall()) {
            this.$search.val('').focus();
        }
        this.$search.removeData('last-search');
        this.getItems().unhighlight().removeClass(['d-none', 'selected']);
        this.$chooser.css('max-height', '400px');
        this.$remoteSearch.empty();
        this.trigger('resetSearch');
    };

    SpaceChooser.prototype.onChange = function (input) {
        this.showMessage(input);
        this.trigger('changed', input);
    };

    SpaceChooser.prototype.showMessage = function (input) {
        var emptyResult = !this.getFirstItem().length;
        var inputLength = input ? input.length : 0;

        if (emptyResult && inputLength > 1) {
            this.$remoteSearch.html('<div class="text-body-secondary">' + module.text('info.emptyResult') + '</div>');
        } else if (emptyResult) {
            this.$remoteSearch.html('<div class="text-body-secondary">' + module.text('info.emptyOwnResult') + '<br/>' + module.text('info.remoteAtLeastInput') + '</div>');
        } else if (inputLength === 1) {
            this.$remoteSearch.html('<div class="text-body-secondary">' + module.text('info.remoteAtLeastInput') + '</div>');
        }
    }

    SpaceChooser.prototype.clearSelection = function () {
        return this.getSelectedItem().removeClass('selected');
    };

    SpaceChooser.prototype.getFirstItem = function () {
        return this.$chooser.find('[data-space-chooser-item]:not(.d-none)').first();
    };

    SpaceChooser.prototype.hasItems = function () {
        return this.$chooser.find('[data-space-chooser-item]').length > 0;
    };

    SpaceChooser.selectItem = function ($item) {
        $item.addClass('selected');
        return SpaceChooser;
    };

    SpaceChooser.deselectItem = function ($item) {
        $item.removeClass('selected');
        return SpaceChooser;
    };

    /**
     * Resets the space chooser icon, if no space view is set.
     *
     * @returns {undefined}
     */
    SpaceChooser.prototype.setNoSpace = function () {
        if (!this.$menu.find('.no-space').length) {
            this._changeMenuButton(module.config.noSpace);
        }
    };

    /**
     * Changes the space chooser icon, for the given space options.
     *
     * @param {type} spaceOptions
     * @returns {undefined}
     */
    SpaceChooser.prototype.setSpace = function (space) {
        this.setSpaceMessageCount(space, 0);
        this._changeMenuButton(space.image);
    };

    SpaceChooser.prototype.setSpaceMessageCount = function (space, count) {
        var guid = object.isString(space) ? space : space.guid;
        var $messageCount = $('[data-space-guid="' + guid + '"]').find('[data-message-count]');
        if ($messageCount.length) {
            if (count) {
                $messageCount.text(count);
            } else {
                $messageCount.fadeOut('fast');
            }
        }
    };

    SpaceChooser.prototype._changeMenuButton = function (newButton) {
        var $newTitle = (newButton instanceof $) ? newButton : $(newButton);
        var $oldTitle = this.$menu.children();
        this.$menu.append($newTitle.addClass('d-none'));
        ui.additions.switchButtons($oldTitle, $newTitle, {remove: true});
    };

    SpaceChooser.prototype.getSelectedItem = function () {
        return this.$.find('[data-space-chooser-item].selected');
    };

    SpaceChooser.prototype.getItems = function () {
        return this.$.find('[data-space-chooser-item]');
    };

    module.export({
        SpaceChooser: SpaceChooser,
        init: function () {
            SpaceChooser.instance($('#space-menu-dropdown'));
        }
    });
});
