define(['jquery', '../../UI/search/list.tmpl', '../../UI/search/error.tmpl', '../../UI/search/user.tmpl', '../../UI/search/document.tmpl', '../../UI/search/im.tmpl', 'underscore'], function ($, list_template, error_template, user_template, doc_template, im_template) {

    var previousTimeout;
    var previousRequest;
    var pauseDuration = 300;

    return (function () {
        var navbarHeight = $('.js-claro-nav-container').innerHeight();
        var suggestionBox = $('#suggestions');
        var suggestionInner = $('.search-suggestions-inner');
        var suggestionBoxIn = 'suggestion-in';
        var searchfield = $('#searchfield');
        var searchForm = $('#head_searchbox');


        function suggestionTop() {
            if ($(window).width() > 768) {
                $(suggestionBox).css('top', navbarHeight);
            }
        }

        suggestionTop();


        // Update js on screen size change
        var $window = $(window);
        var lastWindowWidth = $window.width();

        $window.resize(function () {
            var windowWidth = $window.width();
            if (lastWindowWidth !== windowWidth) {
                suggestionTop();
                lastWindowWidth = windowWidth;
            }
        });

        searchfield.on('input', {suggestionInner: suggestionInner}, handleKeyPress);

        $('html').on('click', function (event) {
            if (!$.contains(searchForm[0], event.target)) {
                $(suggestionBox).removeClass(suggestionBoxIn);
                $('body').removeClass('mobile-search');
            }
        });

        searchForm.on('keyup focus click blur change', function () {
            suggestionTop();

            $('.result').text($(searchfield).val());

            if ($(searchfield).val().length >= 1) {
                $(suggestionBox).addClass(suggestionBoxIn);
                $('body').addClass('mobile-search');
            } else {
                $(suggestionBox).removeClass(suggestionBoxIn);
                $('body').removeClass('mobile-search');
            }

        });

        // --------------------------
        // TOGGLE ACTIVE CLASS ON SEARCH

        $('.navbar-search-icon').on('click', function() {
        if ($('.claro-navbar-search-form').hasClass('search-open')) {
          $('.claro-navbar-search-form').removeClass('search-open');
          $('.claro-navbar-search-form .form-control').val('');
          setTimeout(function () {document.activeElement.blur()}, 500);
        } else {
          $('.claro-navbar-search-form').addClass('search-open');
        }

        });





        suggestionBox.on('click', '#all-results-link', function (event) {
            event.preventDefault();
            searchForm.submit();
        });


        searchForm.on("keydown", ".js-suggestion-item", function (event) {
            var items = searchForm.find('.js-suggestion-item');
            var index = items.index(event.currentTarget);
            var num_items = items.length;


            //enter
            if (event.keyCode === 13) {
                event.preventDefault();
                var href = $(event.currentTarget).attr("data-suggestion-href");
                if (href !== undefined) {
                    window.location.href = href;
                } else {
                    searchForm.submit();
                }

            } else if (event.keyCode === 38) { //arrow up
                event.preventDefault();
                if (index > 0) {
                    var prevElem = $(items[index - 1]);
                    items.removeClass('active');
                    prevElem.focus();
                    prevElem.addClass('active');
                }
            } else if (event.keyCode === 40) { //arrow down
                event.preventDefault();
                if (index < num_items - 1) {
                    var nextElem = $(items[index + 1]);
                    items.removeClass('active');
                    nextElem.focus();
                    nextElem.addClass('active');
                }
                if (index === num_items - 1) {
                    items.removeClass('active');
                    $('#searchfield').addClass('active');
                    $('#searchfield').focus();
                }
            }
        });

        updateSearchfield.call(searchfield[0], suggestionInner);
    });

    /**
     * Draw a list of search suggestions below the search box, based on a given query and results
     * @param data The list of search results, uncategorised
	 * @param recommendations A list of user-defined best bets and suggestions
     * @param recent The list of recent searches similar to the query string
     * @param apps The list of apps (modules or components) that matched the query
     * @param query The query itself (displayed at the bottom of the list)
     * @param box The suggestion box to draw in
     * @param searchfield the search field containing the search string
     */
    function drawSearchSuggestions(data, recommendations, recent, apps, query, box, searchfield) {
        box.empty();
        var height = 0;

        data = doHighlightsForCategory(data);
        recent = doHighlightsForCategory(recent);
        apps = doHighlightsForCategory(apps);

        var categorised = separateByType(data, recent, apps);
        var templates = {
            user: user_template,
            document: doc_template,
            comms_message: im_template
        };
        var list_html = list_template({categories: categorised, best_bets: recommendations.best_bets, suggestions: recommendations.suggestions, query: query, templates: templates});

        if ($(searchfield).val().length >= 2) {
            box.html(list_html);
        } else {
            box.html('');
        }
        //Find height of search wrapper

        $('.js-suggestion-group').each(function () {
            height += $(this).innerHeight();
        });

        $('.search-suggestions-inner').css('height', height);


        //Checks for apps and adds class to outer div if present
        if (data.length === 0 && recent.length === 0 && apps.length === 0) {
            box.removeClass('open-inner').addClass('close-inner');
        } else {
            box.addClass('open-inner').removeClass('close-inner');
        }


    }

    function doHighlightsForCategory(category) {
        var l = category.length;
        var item;
        var highlight;
        for (var i = 0; i < l; ++i) {
            item = category[i];
            highlight = item['highlight'];
            category[i]['highlight'] = formatHighlightArray(highlight);
        }

        return category;
    }

    function formatHighlightArray(highlightArray) {
        for (var field in highlightArray)
            highlightArray[field] = joinHighlights(highlightArray[field].map(formatHighlights));
        return highlightArray;
    }

    function formatHighlights(highlight) {
        var l = highlight.length;
        var out = '';
        for (var i = 0; i < l; ++i) {
            if (highlight[i][0])
                out += applyHighlight(highlight[i][1]);
            else
                out += highlight[i][1];
        }
        return out;
    }

    function joinHighlights(highlights) {
        return highlights.join(' ');
    }

    function applyHighlight(fragment) {
        return '<strong>' + fragment + '</strong>';
    }

    /**
     * Arrange raw list of results into category buckets
     * @param items
     * @param recent
     * @param apps
     * @return {Array}
     */
    function separateByType(items, recent, apps) {
        var categories = {};
        var names = {};
        var scores = {};
        var numItems = items.length;

        //put search results into buckets according to type names
        for (var i = 0; i < numItems; ++i) {
            /**
             * @var {{obj_type_plural:string, obj_type:string}} item;
             */
            var item = items[i];
            var type_name = item.obj_type_plural;
            var type = item.obj_type;

            if (!Array.isArray(categories[type]))
                categories[type] = [];
            categories[type].push(item);
            names[type] = type_name;
        }

        for(type in categories)
        {
            var category = categories[type];
            var l = category.length;
            var total_score = 0;
            for(i = 0; i < l; ++i)
            {
                total_score += category[i]['score'];
            }
            scores[type] = total_score / l;
        }

        //put result buckets into an array, along with human-readable type names (for heading)
        var types = Object.getOwnPropertyNames(names);
        var return_array = [];

        // People always come first -- see CORE-1067
        var people_type = 'user';
        if (people_type in names) {
            return_array.push({
                type: people_type,
                name: names[people_type],
                items: categories[people_type],
                score: scores[people_type]
            });
        }

        for (i = 0; i < types.length; ++i) {
            type = types[i];
            if (type === people_type)
                continue;

            return_array.push({
                type: type,
                name: names[type],
                items: categories[type],
                score: scores[type]
            });
        }

        return_array.sort(function(a, b){
            return b['score'] - a['score'];
        });

        //add in recent searches at the beginning of the list
        if (recent.length > 0) {
            return_array.unshift({
                type: recent[0].obj_type,
                name: recent[0].obj_type_plural,
                items: recent
            });
        }

        //add apps to the end of the list
        if (apps.length > 0) {
            return_array.push({
                type: apps[0].obj_type,
                name: apps[0].obj_type_plural,
                items: apps
            });
        }


        return return_array;
    }

    function handleKeyPress(event) {
        if (previousTimeout !== undefined)
            clearTimeout(previousTimeout);
        var suggestionInner = event.data.suggestionInner;
        previousTimeout = setTimeout(function () {
            updateSearchfield.call(event.currentTarget, suggestionInner);
            previousTimeout = undefined;
        }, pauseDuration);

    }

    /**
     * Send an AJAX request to get new list of suggestions, then re-draw them
     * @param {jQuery.fn.init} suggestionInnerBox
     */
    function updateSearchfield(suggestionInnerBox) {
        var inputValue = $(this).val();
        var searchfield = this;
        if (inputValue.length >= 1) {
            if (previousRequest !== undefined)
                previousRequest.abort();
            previousRequest = $.ajax('/api/search/v2/simple', {
                data: {
                    q: inputValue,
                    fuzzy: 1,
                    limit: 10
                },
                /**
                 * @param {{results: object, recommendations: array, success: bool, recent: array, apps: array}} data
                 */
                success: function (data) {
                    if (data.success)
                    {
                        $(suggestionInnerBox).removeClass('no-height');
                        drawSearchSuggestions(data.results.data, data.recommendations, data.recent, data.apps, inputValue, suggestionInnerBox, searchfield);
                    }
                    else
                    {
                        $(suggestionInnerBox).addClass('no-height');
                        var errorHtml = error_template({message: data.error_message});
                        $(suggestionInnerBox).html(errorHtml);
                    }

                    previousRequest = undefined;
                },
                error: function (data) {
                    //check for aborted request
                    if(data.getAllResponseHeaders() !== "")
                        console.error("Failed to fetch search data (most likely due to an internal server error");
                }
            });
        }
    }

});
