/**
 * Get a reference to the search box and some of its components.
 */
var search_form = document.getElementById("search");
var search_box  = search_form.querySelector("input[name=q]");
var suggestions = search_form.querySelector(".suggestions");

// Path of the route for retrieving search suggestions while typing.
var dokk_suggest_path = document.getElementById("dokk.js").getAttribute("data-suggest");

/**
 * Remember the user input query. This is needed because
 * - when user clicks arrow up/down, we change the search box text to the
 *   text of the selected item
 * - when user reaches the end of the list and clicks down arrow again,
 *   we show the original query again before restarting from item 1
 */
var search_query = "";

/**
 * This is just a pointer to the "suggestions item" that is currently
 * highlighted. We use this pointer to find the next or previous
 * item to navigate when up/down arrow is pressed.
 */
var highlighted_suggestion = null;

/**
 * Keep track of the current AJAX request that is waiting for a response.
 * We use this instance to abort old requests so that the search box
 * displays always the results of the last typed string.
 */
var current_request = null;

/**
 * Send Ajax request to find suggestions.
 */
function search(event)
{
    if (current_request !== null)
        current_request.abort();
    
    /**
     * Remember user query.
     * Used to restore original value because it could have been changed
     * when highlighting a suggestion item. This value is used to restore the
     * original query when no suggestion is selected.
     */
    search_query = search_box.value;
    
    // Empty search?
    if (search_box.value.length == 0)
    {
        hide_suggestions();
        suggestions.innerHTML = "";
        return;
    }
    
    // Send XMLHttpRequest request
    var xhr = new XMLHttpRequest();
    
    // xhr.addEventListener("loadstart", ...);
    // xhr.addEventListener("loadend", ...);
    // xhr.addEventListener("progress", ...);
    // xhr.addEventListener("error", ...);
    // xhr.addEventListener("abort", ...);
    
    xhr.addEventListener("load", function(event) {
        
        switch (xhr.status)
        {
            case 200: // OK
                
                // Retrieve response (should contain items as HTML)
                suggestions.innerHTML = xhr.response;
                
                // No suggestions found?
                if (xhr.response.length == 0)
                {
                    hide_suggestions();
                    break;
                }
                
                /**
                 * Bind a "mouseenter" event to all suggestions items.
                 * Highlight suggestion when mouse "enters" the <li> scope (as
                 * opposed to using up/down arrows).
                 * Also bind a "click" event to replace the text in the search
                 * box. This is used mostly as a feedback to the user, because
                 * when a suggestion is clicked, it links (<a>) to the search
                 * page.
                 * 
                 * DOCS: "currentTarget" refers to the element that the event
                 *       listener directly attached to while the target still
                 *       refers to the specific item (possibly a child down
                 *       the chain) that was clicked. For example if we attach
                 *       a "click" event to the <li> node:
                 *       <li>      currentTarget
                 *           <a>   target
                 */
                suggestions
                    .querySelectorAll("li")
                    .forEach(function(item, index) {
                        item.addEventListener("mouseenter", function(event) {
                            highlight_suggestion(event.currentTarget);
                        });
                        
                        item.addEventListener("click", function(event) {
                            search_box.value = event.currentTarget.textContent;
                        });
                    });
                
                show_suggestions();
                
                break;
            
            default:
                break;
        }
    });
    
    // XMLHttpRequest.open(method, url, async)
    xhr.open("GET", dokk_suggest_path + "?q=" + search_box.value, true);
    xhr.send();
    
    current_request = xhr;
}

/**
 * Handler for "click" events on the entire page.
 * This is created when the list of suggestions is visible, and it's
 * meant to hide the list when users click anywhere else on the page.
 * This is removed once the list is no longer visible.
 */
function document_click_handler(event)
{
    hide_suggestions();
}

/**
 * Handler for "click" events on the search form.
 * The only reason for this event, is to stop bubbling up of the "click"
 * event when users click inside the search form, when the list of
 * suggestions is visible. This is required because if the list of suggestions is
 * visible and the user clicks on the search box, the list gets hidden.
 * In other words, this event is used to prevent the execution of document_click_handler().
 */
function searchform_click_handler(event)
{
    event.stopPropagation();
}

/**
 * Show the list of suggestions.
 */
function show_suggestions()
{
    suggestions.style.display = "block";
    
    // Attach "click" events to hide list when user clicks
    // outside of the list.
    document.addEventListener("click", document_click_handler);
    search_form.addEventListener("click", searchform_click_handler);
}

/**
 * Hide the list of suggestions.
 */
function hide_suggestions()
{
    suggestions.style.display = "none";
    
    // Remove all "click" events attached by show_suggestions().
    document.removeEventListener("click", document_click_handler);
    search_form.removeEventListener("click", searchform_click_handler);
}

/**
 * Highlight a single item in the list of suggestions.
 */
function highlight_suggestion(suggestion)
{
    /**
     * Remove highlight class from previous selected item and add selection
     * class to new item.
     * The .highlight class is added to the <li> node.
     * 
     *     <li><a>suggestion text</a></li>
     */
    if (highlighted_suggestion)
        highlighted_suggestion.classList.remove("highlight");
    
    if (suggestion)
        suggestion.classList.add("highlight");
    
    // Update reference to selected item
    highlighted_suggestion = suggestion;
}

/**
 * When pressing arrow down, select the next item in the list.
 */
function select_next_suggestion()
{
    // Is the list of suggestions hidden?
    if (suggestions.style.display == "none")
    {
        show_suggestions();
        return;
    }
    
    var next_item = null;
    
    // Find next item to select
    if (highlighted_suggestion)
        next_item = highlighted_suggestion.nextElementSibling;
    else
        next_item = suggestions.firstElementChild;
    
    highlight_suggestion(next_item);
    highlighted_suggestion = next_item;
    
    // Replace searchbox text with item text
    search_box.value = highlighted_suggestion
                     ? highlighted_suggestion.textContent
                     : search_query;
}

/**
 * When pressing arrow up, select the previous item in the list.
 */
function select_previous_suggestion()
{
    // Is the list of suggestions hidden?
    if (suggestions.style.display == "none")
    {
        show_suggestions();
        return;
    }
    
    var previous_item = null;
    
    // Find previous item to select
    if (highlighted_suggestion)
        previous_item = highlighted_suggestion.previousElementSibling;
    else
        previous_item = suggestions.lastElementChild;
    
    highlight_suggestion(previous_item);
    highlighted_suggestion = previous_item;
    
    // Replace searchbox text with item text
    search_box.value = highlighted_suggestion
                     ? highlighted_suggestion.textContent
                     : search_query;
}

// Wait when DOM ready. Fired before images and other assets are loaded.
document.addEventListener("DOMContentLoaded", function(event) {
    
    // Add event listener to searchbox to find suggestions
    search_box.addEventListener("input", search);
    
    // Hide search suggestions when user hits Esc
    document.addEventListener("keydown", function(event) {
        switch (event.keyCode)
        {
            case 27: // Esc key
                hide_suggestions();
                
                // Reset text of the search box to user's query
                search_box.value = search_query;
                
                break;
        }
    });
    
    // Arrow events for searchbox
    search_box.addEventListener("keydown", function(event) {
        switch (event.keyCode)
        {
            case 37:    // Left arrow
                break;
            case 38:    // Up arrow
                select_previous_suggestion();
                break;
            case 39:    // Right arrow
                break;
            case 40:    // Down arrow
                select_next_suggestion();
                break;
        }
    });

});
