jQuery(document).ready(function ($) {

    var css_handles = {
        widget: '.webien-search-addon-container',
        form: ' .search-form',
        input: ' input[type="text"]',
        button: ' button',
        searching: ' .searching',
        resContainer: ' .results-container',
        results: ' .results .inner',
        resItem: ' a.item',
        postTypes: ' .post-types',
        postType: ' .post-types a',
        loading: ' .loading',
        backdrop: ' .backdrop'
    };


    function setupSearchWidget(widget){

        var hasResults = false;
        var searching = false;
        var offset = 0;
        var resultTabCount = 0;
        var currentMax = 0;
        var xhr; // ajax object
        var xhrCache;
        var typeTimer;
        var xhrTimer;

        // Add event if we use js object
        if(useWebienSearchToJs) {
            var webienSearchToJsEvent = new Event('webienSearchToJsEvent');
        }

        function getVariables() {
            // Detect variables that should be appended to post type links
            var variablesObj = widget.data('variables');
            var vars = '';
            var urlParam = '';
            for (var key in variablesObj) {
                var value = variablesObj[key];
                vars += key + '=' + value + '|'; // builds url params: var=regionID=11705|city=gbg
            }
            if (vars) {
                urlParam = '&var=' + vars.slice(0, -1); // Remove last delimiter
            }
            return urlParam;
        }

        function getSearchUrl(val, pt) {
            var url = widget.attr('data-base-url') + '/?s=' + encodeURIComponent(val);
            url += '&webien_pt=' + encodeURIComponent(widget.data('post-types'));

            if (pt) {
                url += '&pt=' + encodeURIComponent(pt);
            }

            return url + getVariables();
        }

        function delay(callback, ms) {
            var timer = 0;
            return function () {
                var context = this, args = arguments;
                clearTimeout(timer);
                timer = setTimeout(function () {
                    callback.apply(context, args);
                }, ms || 0);
            };
        }


        function doSearch(typechange, val) {
            if (widget.data('ajax') !== 1) {
                return false;
            }

            // Check if we have another ajax trying to get stuff already, abort those in that case,
            // we don't want multiple ajax going at the same time..
            if (xhr && xhr.readyState != 4) { // 4 is ready state
                xhr.abort();
            }
            if (xhrCache && xhrCache.readyState != 4) {
                xhrCache.abort();
            }

            if(!val){ // val is only set when pasting
                val = widget.find(css_handles.input).val();
            }
            searching = true; // this is for not colliding when using cached results

            hasResults = false;

            // Trim white spaces
            val = val.trim();

            if (val === '') {
                return false;
            }


            // Dispatch event for JS solution
            if(useWebienSearchToJs){
                webienSearchSuggestions.event = 'search';
                window.dispatchEvent(webienSearchToJsEvent);
            }


            if (typechange) {

                // Dispatch event for JS solution
                if(!useWebienSearchToJs){
                    widget.find(css_handles.resContainer).show();
                    widget.find(css_handles.backdrop).show();
                    widget.find(css_handles.searching).addClass('show');
                    widget.find(css_handles.results).css({'opacity': 0});
                    widget.find(css_handles.postTypes).css({'opacity': 0});
                }

                // If cache is enabled, a global js variable has been written out in head with the path to cache folder
                if (typeof window.webienSearchCache !== 'undefined') {
                    // If active, do a call to the matching json file to see if it exists.
                    // This will be way faster than going through wp. If no cached file exists, the normal wp ajax will get the result.
                    // Append random number to avoid client cache of prior redirect if cache was empty earlier
                    // If S3 is used, go directly to json file, if self hosted go through cacheApi file for better control if not found

                    // Clear any waiting operations
                    clearTimeout(xhrTimer);

                    // Delay on uncached searches to minimize requests on incomplete words
                    var xhrDelay = 400;
                    if(is_touch_enabled()){
                        xhrDelay = 800; // Longer wait on mobile
                    }

                    if (typeof window.webienSearchCacheS3 !== 'undefined') { // if set, files are on AWS

                        var postTypes = widget.data('post-types').split(',');
                        postTypes.sort();
                        var host = window.location.hostname;
                        host = host.replace('www.', ''); // remove www
                        var filename = val + '___' + postTypes.join(',') + '___' + host;
                        filename = b64EncodeUnicode(filename);
                        //Replace slashes to match replacement made by php to make paths work
                        filename = filename.replace("/", "-");
                        // append random string to filename to avoid browser cache
                        var cacheUrl = window.webienSearchCache + filename + ".json?" + randomString(5);
                        xhrCache = $.get({
                            url: cacheUrl,
                            dataType: 'json',
                            success: function (response, textStatus, xhr) {
                                if (xhr.status === 200) {
                                    if (response.data.posts !== 'undefined' && response.data.posts.length) {
                                        display_results(response, val, searching);
                                        searching = false;
                                        hasResults = true;
                                    }
                                }
                            },
                        })
                            .fail(function (jqXHR, textStatus) {
                                // This is were we end up if the json file won't exists, do a full search instead.
                                if (jqXHR.status === 403 || jqXHR.status === 404) {

                                    // If the filter that blocks any ajax request is active, return here
                                    if (typeof window.cachedSuggestionsOnly !== 'undefined') {
                                        display_results('', val, searching);
                                        searching = false;
                                        return;
                                    }

                                    // Wait with uncached searches so we dont make to many ajax requests on incomplete words
                                    xhrTimer = setTimeout(function (){
                                        doSearch(false, false);
                                    }, xhrDelay);

                                }
                            });
                    } else { // If not set, files are on local server and we can parse them with php
                        $.get({
                            url: webien_search_ajax_object.ajax_search_cache_api,
                            data: {
                                action: 'get_cache',
                                value: val,
                                post_type: widget.data('post-types'),
                                active_folder: webienSearchCacheFolder
                            },
                            success: function (response) {
                                if (response.data.posts !== 'undefined' && response.data.posts.length) {
                                    display_results(response, val, searching);
                                    searching = false;
                                    hasResults = true;
                                } else {
                                    xhrTimer = setTimeout(function (){
                                        doSearch(false, false);
                                    }, xhrDelay);
                                }
                            },
                            error: function () {
                            }
                        });
                    }
                    // if type change, we only do cache search, if cache returns empty, do full search
                    return;
                }
            }

            var quantity = widget.data('quantity');
            if (offset !== 0) {
                quantity = 10; // If we are doing endless scroll, get 10/scroll
            }

            xhr = $.get({
                url: webien_search_ajax_object.ajax_url,
                data: {
                    action: 'webien_relevanssi_search',
                    value: val,
                    type: 'suggestion',
                    quantity: quantity,
                    post_type: widget.data('post-types'),
                    variables: widget.data('variables'),
                    offset: offset
                },
                success: function (response) {
                    // if result returnes nothing since cache has been used, do nothing here.
                    if (response.data.posts !== 'undefined' && response.data.cache === true) {
                        return;
                    }
                    hasResults = true;
                    if (response.success === true) {
                        display_results(response, val, searching);
                        searching = false;
                    }
                },
                error: function () {
                }
            });

        }

        function display_results(response, val, searching) {

            if(useWebienSearchToJs){
                webienSearchSuggestions.results = response;
                webienSearchSuggestions.event = 'result';
                window.dispatchEvent(webienSearchToJsEvent);
            }


            if (!searching) {
                return false;
            }


            if (offset === 0) {
                widget.find(css_handles.results).html('');
                widget.find(css_handles.postTypes).html('');
                widget.find(css_handles.searching).removeClass('show');
                widget.find(css_handles.resContainer + ' .results').scrollTop(0);
            }


            if(response.hasOwnProperty('data') && response.data.hasOwnProperty('posts') && response.data.posts.length > 0) {
                response.data.posts.forEach(function (post) {
                    if(post.link){
                        widget.find(css_handles.results).append('<a href="' + post.link + '" class="item">' + post.html + '</a>');
                    } else {
                        widget.find(css_handles.results).append('<span class="item">' + post.html + '</span>');
                    }
                });

                if (offset === 0) {
                    widget.find(css_handles.postTypes).append('<span class="more">' + widget.find(css_handles.postTypes).attr('data-txt') + '</span>');
                    for (var name in response.data.post_types) {
                        var title = response.data.post_types[name].title;
                        var amount = response.data.post_types[name].amount;
                        var slug = response.data.post_types[name].slug;

                        if (slug === 'any') {
                            currentMax = amount;
                        }


                        widget.find(css_handles.postTypes).append('<a href="' + getSearchUrl(val, slug) + '" tabindex="0">' + title + '&nbsp;(' + amount + ')</a>');
                    }
                    widget.find(css_handles.results).css({'opacity': 1});
                    widget.find(css_handles.postTypes).css({'opacity': 1});
                }

                offset += response.data.posts.length;

                widget.find(css_handles.results).removeClass('no-res');
                widget.find(css_handles.results).removeClass('loading');
                widget.find(css_handles.results).removeClass('done');

            } else {
                widget.find(css_handles.results).addClass('no-res');
                widget.find(css_handles.results).append('<h3 class="no-result" style="text-align: center;margin:0;">' + widget.find(css_handles.results).attr('data-no-res-txt') + '</h3>');
                widget.find(css_handles.results).css({'opacity': 1});
            }
        }

        function init_events() {

            widget.unbind();
            widget.find(
                css_handles.widget + css_handles.form + ',' +
                css_handles.widget + css_handles.input + ',' +
                css_handles.widget + css_handles.button + ',' +
                css_handles.widget + css_handles.results + ',' +
                css_handles.widget + css_handles.resItem + ',' +
                css_handles.widget + css_handles.postType
            ).unbind();


            widget.find(css_handles.resContainer + ' .results').on('scroll', function () {
                if (!searching && !$(css_handles.results).is(':empty')) {
                    if (endOfList()) {
                        if (offset >= currentMax) {
                            $(css_handles.results).addClass('done');
                            return false;
                        } else {
                            $(css_handles.results).addClass('loading');
                        }
                        doSearch(false, false);
                    }
                }
            });


            // Listen for event that can also trigger a search if for some reason the scroll cant trigger it
            $(window).on('webienSearchListEnd', function (e) {
                if (offset >= currentMax) {
                    $(css_handles.results).addClass('done');
                    return false;
                } else {
                    $(css_handles.results).addClass('loading');
                }
                doSearch(false, false);
            });


            widget.find(css_handles.input).focus(function () {
                widget.find(css_handles.form).addClass('focused');

                /*
                // Check for takeover
                if($(css_handles.widget + css_handles.form).hasClass('takeover-mobile-yes')){
                    var offset = $(css_handles.widget + css_handles.form).offset();
                    var left = offset.left*-1;
                    $(css_handles.widget + css_handles.resContainer).css({'margin-left':left});
                }
                */


                if (hasResults && !useWebienSearchToJs) {
                    widget.find(css_handles.resContainer).show();
                    widget.find(css_handles.backdrop).show();
                }

                // If on mobile, scroll into position
                if (is_touch_enabled()) {
                    $("html, body").animate({scrollTop: widget.find(css_handles.form).offset().top - 15});
                }


            });
            widget.find(css_handles.input).blur(function () {
                widget.find(css_handles.form).removeClass('focused');
            });


            widget.on('keydown', function (e) {

                //console.log(e.keyCode);

                var keycode = e.keyCode;
                if (keycode === 38) { // Arrow up
                    if (widget.find(css_handles.resItem).length) {
                        if (resultTabCount > -1) {
                            resultTabCount--;
                        }
                        if (resultTabCount === -1) { // If on top, focus back into input field
                            widget.find(css_handles.input).focus();
                        } else {
                            widget.find(css_handles.resItem).eq(resultTabCount).focus();
                        }
                    }
                    return false;
                }
                if (keycode === 40) { // Arrow down
                    if (widget.find(css_handles.resItem).length) {

                        if (resultTabCount < widget.find(css_handles.resItem).length - 1) {
                            resultTabCount++;
                        }
                        widget.find(css_handles.resItem).eq(resultTabCount).focus();

                    }
                    return false;
                }
                if (keycode === 27) { // escape
                    widget.find(css_handles.resContainer).hide();
                    widget.find(css_handles.backdrop).hide();
                }

            });


            function checkInput(keycode){
                var keyDelay = 200;
                if(is_touch_enabled()){
                    keyDelay = 400; // Longer wait on mobile
                }

                var minInput = parseInt(widget.data('min-input'));

                // Clear any waiting operations
                clearTimeout(typeTimer);


                // Dispatch event for JS solution
                if(useWebienSearchToJs){
                    webienSearchSuggestions.event = 'key';
                    window.dispatchEvent(webienSearchToJsEvent);
                }

                if (keycode === 13) { // Enter
                    if (widget.find(css_handles.form).hasClass('no-submit')) { // If option not redirect is set
                        return false;
                    }
                    if (widget.find(css_handles.input).val()) {
                        location.href = getSearchUrl(widget.find(css_handles.input).val());
                    }
                    return false;
                }

                if (keycode === 8 || keycode === 46) { // Backspace
                    if (widget.find(css_handles.input).val().length <= minInput) {
                        widget.find(css_handles.resContainer).hide();
                        widget.find(css_handles.backdrop).hide();
                    }
                }

                var valid =
                    (keycode > 47 && keycode < 58) || // number keys
                    (keycode === 8 || keycode === 46) || // backspace / delete
                    keycode === 32 || keycode === 13 || // spacebar & return key(s) (if you want to allow carriage returns)
                    (keycode > 64 && keycode < 91) || // letter keys
                    (keycode > 95 && keycode < 112) || // numpad keys
                    (keycode > 185 && keycode < 193) || // ;=,-./` (in order)
                    (keycode > 218 && keycode < 223) || // [\]' (in order)
                    keycode === 229; // every letter key on android (samsung)


                if (valid && widget.find(css_handles.input).val().length >= minInput) {

                    typeTimer = setTimeout(function(){
                        offset = 0;
                        currentMax = 0;
                        resultTabCount = -1;

                        doSearch(true, false);
                    }, keyDelay);  // wait a bit..

                    // Dispatch event for JS solution
                    if(useWebienSearchToJs){
                        webienSearchSuggestions.event = 'search';
                        window.dispatchEvent(webienSearchToJsEvent);
                    }

                }
            }

            // detect keyboard events and touch events on mobile
            widget.find(css_handles.input).on('keyup touchend', function (e) {
                var keycode = e.keyCode;
                checkInput(keycode);
            });

            widget.find(css_handles.input).on('paste', function (e) {
                // Simulate space (will be trimmed out)
                var content = '';
                if (isIE()) {
                    //IE allows to get the clipboard data of the window object.
                    content = window.clipboardData.getData('text');
                } else {
                    //This works for Chrome and Firefox.
                    content = e.originalEvent.clipboardData.getData('text/plain');
                }
                //console.log(content);

                offset = 0;
                currentMax = 0;
                resultTabCount = -1;

                doSearch(true, content);

            });


            widget.find(css_handles.button).click(function () {
                if (widget.find(css_handles.form).hasClass('no-submit')) { // If option not redirect is set
                    widget.find(css_handles.input).focus().select();
                    return false;
                }
                if (widget.find(css_handles.input).val()) {
                    location.href = getSearchUrl(widget.find(css_handles.input).val());
                }
            });

            $(document).on('initSearch', function (e) {
                var content =  $('input.placeholder').val();
                offset = 0;
                currentMax = 0;
                resultTabCount = -1;
                doSearch(true, content);
            });

            widget.find(
                css_handles.form + ',' +
                css_handles.results
            ).click(function (e) {
                e.stopPropagation();
            });


            widget.find(
                css_handles.backdrop
            ).on('click touch', function () {
                widget.find(css_handles.resContainer).hide();
                widget.find(css_handles.backdrop).hide();
            });


        }

        // Endless scroll
        function endOfList() {
            var el = widget.find(css_handles.resContainer + ' .results');
            if (el.scrollTop() + el.innerHeight() >= el[0].scrollHeight - 50) {
                return true;
            }
            return false;
        }

        init_events();

    }; // End main function


    // Run setup for each instance
    $('.webien-search-addon-container').each(function(e) {
        setupSearchWidget($(this));
    });


    function b64EncodeUnicode(str) {
        // first we use encodeURIComponent to get percent-encoded UTF-8,
        // then we convert the percent encodings into raw bytes which
        // can be fed into btoa.
        return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
            function toSolidBytes(match, p1) {
                return String.fromCharCode('0x' + p1);
            }));
    }

    function is_touch_enabled() {
        return ('ontouchstart' in window) ||
            (navigator.maxTouchPoints > 0) ||
            (navigator.msMaxTouchPoints > 0);
    }




    // Fix scroll on popups
    $(document).on('elementor/popup/show', function (event, popupId, popupDocument) {
        // Find instance in popup
        var widget = $('#elementor-popup-modal-'+popupId).find(css_handles.widget);

        if(widget.length){

            widget.closest('.dialog-message').css('overflow', 'visible');
            var popup = widget.closest('.elementor-'+popupId + ' > div');

            // Fix when scroll container is bigger than viewport
            if(popup.outerHeight() > $(window).height()){
                widget.closest('.dialog-message').find('.elementor-'+popupId).css('overflow', 'auto');
            }

            if(widget.hasClass('popup-focus')){
                widget.find(css_handles.input).focus();
            }

            setupSearchWidget(widget);
        }
    });



    // Simulate keypress
    $.fn.simulateKeyPress = function (character) {
        // Internally calls jQuery.event.trigger with arguments (Event, data, elem).
        // That last argument, 'elem', is very important!
        $(this).trigger({ type: 'keypress', which: character.charCodeAt(0) });
    };

    function isIE(){
        var ua = window.navigator.userAgent;
        return ua.indexOf('MSIE ') > 0 || ua.indexOf('Trident/') > 0 || ua.indexOf('Edge/') > 0
    }


    function randomString(length) {
        var result           = '';
        var characters       = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        var charactersLength = characters.length;
        for ( var i = 0; i < length; i++ ) {
            result += characters.charAt(Math.floor(Math.random() *
                charactersLength));
        }
        return result;
    }



});
