(function(compodoc) {
    var usePushState = (typeof history.pushState !== 'undefined'),

    // DOM Elements
        $body = $('body'),
        $searchResults,
        $searchInput,
        $searchList,
        $searchTitle,
        $searchResultsCount,
        $searchQuery,
        $mainContainer,
        $xsMenu;

    // Throttle search
    function throttle(fn, wait) {
        var timeout;

        return function() {
            var ctx = this, args = arguments;
            if (!timeout) {
                timeout = setTimeout(function() {
                    timeout = undefined;
                    fn.apply(ctx, args);
                }, wait);
            }
        };
    }

    function displayResults(res) {
        var noResults = res.count == 0;
        var groups = {};
        $searchResults.toggleClass('no-results', noResults);

        // Clear old results
        $searchList.empty();

        // Display title for research
        $searchResultsCount.text(res.count);
        $searchQuery.text(res.query);

        // Group result by context
        res.results.forEach(function(res) {
            var context = res.title.split(' - ')[0];
            if (typeof groups[context] === 'undefined') {
                groups[context] = {
                    results: [res]
                }
            } else {
                groups[context].results.push(res)
            }
        });

        var sortedGroups = Object.keys(groups).sort();

        for (var i = 0; i < sortedGroups.length; i++) {
            var property = sortedGroups[i];
            
            var $li = $('<li>', {
                'class': 'search-results-group'
            });
            var finalPropertyLabel = '';
            var propertyLabels = property.split('-');
            
            if (propertyLabels.length === 2 && propertyLabels[0] !== 'miscellaneous' && propertyLabels[0] !== 'additional') {
                finalPropertyLabel = propertyLabels[0].charAt(0).toUpperCase() + propertyLabels[0].substring(1) + ' - ' + propertyLabels[1].charAt(0).toUpperCase() + propertyLabels[1].substring(1) + ' (' + groups[property].results.length + ')';
            } else if (propertyLabels[0] === 'additional') {
                finalPropertyLabel = 'Additional pages' + ' (' + groups[property].results.length + ')'
            } else {
                finalPropertyLabel = propertyLabels[0].charAt(0).toUpperCase() + propertyLabels[0].substring(1) + ' (' + groups[property].results.length + ')'
            }
            var $groupTitle = $('<h3>', {
                'text': finalPropertyLabel
            });
            $groupTitle.appendTo($li);

            var $ulResults = $('<ul>', {
                'class': 'search-results-list'
            })

            groups[property].results.forEach(function(res) {
                var link = '';
                var $liResult = $('<li>', {
                    'class': 'search-results-item'
                });
                switch (COMPODOC_CURRENT_PAGE_DEPTH) {
                    case 0:
                        link = './';
                        break;
                    case 1:
                    case 2:
                    case 3:
                    case 4:
                    case 5:
                        link = '../'.repeat(COMPODOC_CURRENT_PAGE_DEPTH);
                        break;
                };
                var finalResLabel = res.title.split(' - ')[1].charAt(0).toUpperCase() + res.title.split(' - ')[1].substring(1);
                var $link = $('<a>', {
                    'href': link + res.url,
                    'text': finalResLabel
                });
                $link.appendTo($liResult);
                $liResult.appendTo($ulResults);
            });
            $ulResults.appendTo($li);

            $li.appendTo($searchList);
        }
    }

    function launchSearch(q) {
        $body.addClass('with-search');

        if ($xsMenu.css('display') === 'block') {
            $mainContainer.css('height', 'calc(100% - 100px)');
            $mainContainer.css('margin-top', '100px');
        }

        throttle(compodoc.search.query(q, 0, MAX_SEARCH_RESULTS)
        .then(function(results) {
            displayResults(results);
        }), 1000);
    }

    function closeSearch() {
        $body.removeClass('with-search');
        if ($xsMenu.css('display') === 'block') {
            $mainContainer.css('height', 'calc(100% - 50px)');
            $mainContainer.css('margin-top', '50px');
        }
    }

    function bindMenuButton() {
        document.getElementById('btn-menu').addEventListener('click', function() {
            if ($xsMenu.css('display') === 'none') {
                $body.removeClass('with-search');
                $mainContainer.css('height', 'calc(100% - 50px)');
                $mainContainer.css('margin-top', '50px');
            }
            $.each($searchInputs, function(index, item){
                var item = $(item);
                item.val('');
            });
        });
    }

    function bindSearch() {
        // Bind DOM
        $searchInputs        = $('#book-search-input input');

        $searchResults = $('.search-results');
        $searchList         = $searchResults.find('.search-results-list');
        $searchTitle        = $searchResults.find('.search-results-title');
        $searchResultsCount = $searchTitle.find('.search-results-count');
        $searchQuery        = $searchTitle.find('.search-query');
        $mainContainer      = $('.container-fluid');
        $xsMenu             = $('.xs-menu');

        // Launch query based on input content
        function handleUpdate(item) {
            var q = item.val();

            if (q.length == 0) {
                closeSearch();
                window.location.href = window.location.href.replace(window.location.search, '');
            } else {
                launchSearch(q);
            }
        }

        // Detect true content change in search input
        var propertyChangeUnbound = false;

        $.each($searchInputs, function(index, item){
            var item = $(item);
            // HTML5 (IE9 & others)
            item.on('input', function(e) {
                // Unbind propertychange event for IE9+
                if (!propertyChangeUnbound) {
                    $(this).unbind('propertychange');
                    propertyChangeUnbound = true;
                }

                handleUpdate($(this));
            });
            // Workaround for IE < 9
            item.on('propertychange', function(e) {
                if (e.originalEvent.propertyName == 'value') {
                    handleUpdate($(this));
                }
            });
            // Push to history on blur
            item.on('blur', function(e) {
                // Update history state
                if (usePushState) {
                    var uri = updateQueryString('q', $(this).val());
                    if ($(this).val() !== '') {
                        history.pushState({ path: uri }, null, uri);
                    }
                }
            });
        });
    }

    function launchSearchFromQueryString() {
        var q = getParameterByName('q');
        if (q && q.length > 0) {
            // Update search inputs
            $.each($searchInputs, function(index, item){
                var item = $(item);
                item.val(q)
            });
            // Launch search
            launchSearch(q);
        }
    }

    compodoc.addEventListener(compodoc.EVENTS.SEARCH_READY, function(event) {
        bindSearch();

        bindMenuButton();

        launchSearchFromQueryString();
    });

    function getParameterByName(name) {
        var url = window.location.href;
        name = name.replace(/[\[\]]/g, '\\$&');
        var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)', 'i'),
            results = regex.exec(url);
        if (!results) return null;
        if (!results[2]) return '';
        return decodeURIComponent(results[2].replace(/\+/g, ' '));
    }

    function updateQueryString(key, value) {
        value = encodeURIComponent(value);

        var url = window.location.href;
        var re = new RegExp('([?&])' + key + '=.*?(&|#|$)(.*)', 'gi'),
            hash;

        if (re.test(url)) {
            if (typeof value !== 'undefined' && value !== null)
                return url.replace(re, '$1' + key + '=' + value + '$2$3');
            else {
                hash = url.split('#');
                url = hash[0].replace(re, '$1$3').replace(/(&|\?)$/, '');
                if (typeof hash[1] !== 'undefined' && hash[1] !== null)
                    url += '#' + hash[1];
                return url;
            }
        }
        else {
            if (typeof value !== 'undefined' && value !== null) {
                var separator = url.indexOf('?') !== -1 ? '&' : '?';
                hash = url.split('#');
                url = hash[0] + separator + key + '=' + value;
                if (typeof hash[1] !== 'undefined' && hash[1] !== null)
                    url += '#' + hash[1];
                return url;
            }
            else
                return url;
        }
    }
})(compodoc);
