$(function(){
    // The puzzle list.
    var $puzzleList = $('#puzzle-search-list');
    var $puzzleFilterChanged = $('#puzzle-search-filter-changed');

    // The sort component.
    var $sortComponent = $('#puzzle-search-sort button[data-sort-value]');
    var $sortList = $('#puzzle-search-sort a[data-sort-value]');

    // The solved component.
    var $solvedComponent = $('#puzzle-search-solved button[data-solved-value]');
    var $solvedList = $('#puzzle-search-solved a[data-solved-value]');

    // The filter components.
    var $filterTagComponent = $('#puzzle-search-filter-tag');
    var $filterSizeComponent = $('#puzzle-search-filter-size');
    var $filterTitleComponent = $('#puzzle-search-filter-title');
    var $filterAuthorComponent = $('#puzzle-search-filter-author');
    var $filterSeriesComponent = $('#puzzle-search-filter-series');

    // THe filter input components (displayed as modal).
    var $filterTagListComponent = $('#puzzle-search-tags');
    var $filterSizeRangesComponent = $('#puzzle-search-size');
    var $filterTitleInputComponent = $('#puzzle-search-title');
    var $filterAuthorInputComponent = $('#puzzle-search-author').attr('data-search-limit', 10).attr('data-search-offset', 0);
    var $filterSeriesInputComponent = $('#puzzle-search-series').attr('data-search-limit', 10).attr('data-search-offset', 0);

    // The suggestion lists.
    var $allFilterTags = $('button[data-id]', $filterTagListComponent);
    var $filterTitleInput = $('#puzzle-search-title-input');
    var $filterAuthorInput = $('#puzzle-search-author-input');
    var $filterAuthorList = $('#puzzle-search-author-list');
    var $filterSeriesInput = $('#puzzle-search-series-input');
    var $filterSeriesList = $('#puzzle-search-series-list');
    
    // The size inputs.
    var $filterSizeColsMinInput = $('#puzzle-search-size-cols-min');
    var $filterSizeColsMaxInput = $('#puzzle-search-size-cols-max');
    var $filterSizeRowsMinInput = $('#puzzle-search-size-rows-min');
    var $filterSizeRowsMaxInput = $('#puzzle-search-size-rows-max');
    var $filterSizeColsMinValue = $('#puzzle-search-size-cols-min-value');
    var $filterSizeColsMaxValue = $('#puzzle-search-size-cols-max-value');
    var $filterSizeRowsMinValue = $('#puzzle-search-size-rows-min-value');
    var $filterSizeRowsMaxValue = $('#puzzle-search-size-rows-max-value');

    // Reload the puzzle list.
    $puzzleFilterChanged.on('change', _.debounce(function() {
        if (saveSearchParameters()) {
            $puzzleList
            .attr('data-reload-page', 1)
            .reloadData();
        }
    }, 500));

    // The global functions.
    function showModal($source, $target, callback) {
        $source.on('click', function(e) {
            if ($(e.target).is('.filter-delete')) {
                return;
            }
            
            $target.modal('show');

            if (!_.isUndefined(callback)) {
                callback();
            }
        });
    }

    function loadingUI($target, $component) {
        $loading = $('.loading', $component);
        if ($loading.length == 0) {
            var id = $target.attr('id') + '-loading';
            $loading = getLoading().attr('id', id).appendTo($target.parent()).hide();
            $target.attr('data-reload-loading', '#'+id);
        }

        return $loading;
    }

    function saveSort(value, asc, content) {
        var currentValue = $sortComponent.attr('data-sort-value');
        var currentAsc = $sortComponent.attr('data-sort-asc') == 'true';

        if (currentValue == value && currentAsc == asc) {
            return;
        }

        $sortComponent.attr('data-sort-value', value).attr('data-sort-asc', asc ? 'true' : 'false').html(content);
        $puzzleFilterChanged.trigger('change');
    }

    function saveSolvedFilter(value, content) {
        var currentValue = $solvedComponent.attr('data-solved-value');

        if (currentValue == value) {
            return;
        }

        $solvedComponent.attr('data-solved-value', value).html(content);
        $puzzleFilterChanged.trigger('change');
    }

    function updateFilterUI($target, value, displayedValue) {
        $target.attr('data-filter-value', value);
        $('.filter-value', $target).text(displayedValue);

        if (displayedValue.length > 0) {
            $target.removeClass('btn-light').addClass('btn-info');
            $('.filter-add', $target).hide();
            $('.filter-delete', $target).show();
        } else {
            $target.removeClass('btn-info').addClass('btn-light');
            $('.filter-add', $target).show();
            $('.filter-delete', $target).hide();
        }
    }

    function saveFilter($target, value, displayedValue, callback) {
        updateFilterUI($target, value, displayedValue);

        if (!_.isUndefined(callback)) {
            callback();
        }

        $puzzleFilterChanged.trigger('change');
    }

    function search($component, $input, $list) {
        $list.attr('data-last-search', '');

        $input.on('change paste keyup', _.debounce(function() {
            var lastSearch = $list.attr('data-last-search');
            var searchLengthMin = parseInt($list.attr('data-search-length-min'), 10);

            var search = $input.val();
            if (search.length < searchLengthMin || search == lastSearch) {
                return;
            }
            $list.setParameter('s', search);

            $loading = loadingUI($list, $component);

            $list.reloadData({
                complete: function() {
                    $list.attr('data-last-search', search);
                }
            });
        }, 500));
    }

    function saveSearchParameters() {
        var sortValue = $sortComponent.attr('data-sort-value');
        var sortAsc = $sortComponent.attr('data-sort-asc') == 'true';
        var solved = $solvedComponent.attr('data-solved-value');
        var title = $filterTitleComponent.attr('data-filter-value');
        var authorID = $filterAuthorComponent.attr('data-filter-value');
        var seriesID = $filterSeriesComponent.attr('data-filter-value');
        var tagID = $filterTagComponent.attr('data-filter-value');

        var colsMin = '';
        var colsMax = '';
        var rowsMin = '';
        var rowsMax = '';
        var size = $filterSizeComponent.attr('data-filter-value');
        if (!_.isUndefined(size)) {
            size = size.split('-');
            colsMin = size[0];
            colsMax = size[1];
            rowsMin = size[2];
            rowsMax = size[3];
        }

        if (_.isUndefined(title)) {
            title = '';
        }
        if (_.isUndefined(authorID)) {
            authorID = '';
        }
        if (_.isUndefined(seriesID)) {
            seriesID = '';
        }
        if (_.isUndefined(tagID)) {
            tagID = '';
        }

        var savedParams = sortValue + '/' + sortAsc + '/' + title + '/' + authorID + '/' + seriesID + '/' + tagID + '/' + colsMin + '/'+ colsMax + '/' + rowsMin + '/' + rowsMax + '/' + solved;
        if (savedParams == window.puzzleSearchParams) {
            return false;
        }

        window.puzzleSearchParams = savedParams;

        $puzzleList
        .setParameter('sort', sortValue)
        .setParameter('desc', sortAsc ? 'false' : 'true')
        .setParameter('solved', solved)
        .setParameter('title', title)
        .setParameter('author', authorID)
        .setParameter('series', seriesID)
        .setParameter('tag', tagID)
        .setParameter('colsmin', colsMin)
        .setParameter('colsmax', colsMax)
        .setParameter('rowsmin', rowsMin)
        .setParameter('rowsmax', rowsMax)
        .setParameter('save', $puzzleList.attr('data-filters-save'));

        return true;
    }

    // To reset the suggestion states.
    function resetFilterTagsState() {
        $allFilterTags.removeClass('btn-info').addClass('btn-light');
    }

    function resetFilterAuthorsState() {
        $('*[data-id]', $filterAuthorList).removeClass('active');
    }

    function resetFilterSeriesState() {
        $('*[data-id]', $filterSeriesList).removeClass('active');
    }

    // To reset the input components.
    function resetFilterTitleInputComponent() {
        $filterTitleInput.val($filterTitleComponent.attr('data-filter-value'));
    }

    function resetFilterAuthorInputComponent() {
        $filterAuthorInput.val('');
        $filterAuthorList.text('');
    }

    function resetFilterSeriesInputComponent() {
        $filterSeriesInput.val('');
        $filterSeriesList.text('');
    }

    // To update the size range.
    function updateFilterSizeRange() {
        var colsMin = parseInt($filterSizeColsMinInput.val(), 10);
        var colsMax = parseInt($filterSizeColsMaxInput.val(), 10);
        var rowsMin = parseInt($filterSizeRowsMinInput.val(), 10);
        var rowsMax = parseInt($filterSizeRowsMaxInput.val(), 10);

        if (colsMin > colsMax) {
            $filterSizeColsMinInput.val(colsMax);
            colsMin = colsMax;
        }
        if (rowsMin > rowsMax) {
            $filterSizeRowsMinInput.val(rowsMax);
            rowsMin = rowsMax;
        }

        $filterSizeColsMinValue.text(colsMin);
        $filterSizeColsMaxValue.text(colsMax);
        $filterSizeRowsMinValue.text(rowsMin);
        $filterSizeRowsMaxValue.text(rowsMax);
    }

    // To delete the filters.
    function deleteFilterTag() {
        saveFilter($filterTagComponent, '', '', resetFilterTagsState);
    }

    function deleteFilterTitle() {
        saveFilter($filterTitleComponent, '', '', resetFilterTitleInputComponent);
    }

    function deleteFilterAuthor() {
        saveFilter($filterAuthorComponent, '', '', resetFilterAuthorInputComponent);
    }

    function deleteFilterSeries() {
        saveFilter($filterSeriesComponent, '', '', resetFilterSeriesInputComponent);
    }

    function deleteFilterSize() {
        saveFilter($filterSizeComponent, '', '');
    }

    // Show the tags list.
    showModal($filterTagComponent, $filterTagListComponent);

    // Show the title input.
    showModal($filterTitleComponent, $filterTitleInputComponent, function() {
        $filterTitleInput.focus();
    });

    // Show the author input.
    showModal($filterAuthorComponent, $filterAuthorInputComponent, function() {
        resetFilterAuthorInputComponent();
        $filterAuthorList.attr('data-last-search', '').hide();
        $('.invalid-feedback', $filterAuthorInputComponent).hide();
        $('.alert-warning', $filterAuthorInputComponent).hide();
        $('.loading', $filterAuthorInputComponent).hide();
    });

    // Show the series input.
    showModal($filterSeriesComponent, $filterSeriesInputComponent, function() {
        resetFilterSeriesInputComponent();
        $filterSeriesList.attr('data-last-search', '').hide();
        $('.invalid-feedback', $filterSeriesInputComponent).hide();
        $('.alert-warning', $filterSeriesInputComponent).hide();
        $('.loading', $filterSeriesInputComponent).hide();
    });

    // Show the size ranges.
    showModal($filterSizeComponent, $filterSizeRangesComponent);

    // Select the sort.
    $sortList.on('click', function(e){
        e.preventDefault();

        var $target = $(this);
        saveSort($target.attr('data-sort-value'), $target.attr('data-sort-asc') == 'true', $target.text());
    });

    // Select the solved filter.
    $solvedList.on('click', function(e){
        e.preventDefault();

        var $target = $(this);
        saveSolvedFilter($target.attr('data-solved-value'), $target.text());
    });

    // Select a filter: tag.
    $allFilterTags.on('click', function(e) {
        var $target = $(this);
        var wasSelected = $target.is('.btn-info');

        resetFilterTagsState();

        if (!wasSelected) {
            $target.removeClass('btn-light').addClass('btn-info');
        }
    });

    // Save a filter: tag.
    $('button[data-dismiss]', $filterTagListComponent).on('click', function(e) {
        var tagName = '';
        var tagID = '';

        var $target = $('button.btn-info', $filterTagListComponent);
        if ($target.length > 0) {
            tagName = $target.text();
            tagID = $target.attr('data-id');
        }

        saveFilter($filterTagComponent, tagID, tagName);
    });

    // Save a filter: title.
    $('button[data-dismiss]', $filterTitleInputComponent).on('click', function(e) {
        var title = $filterTitleInput.val();
        if (title.length > 0) {
            title = title.split(" ")[0];
            $filterTitleInput.val(title);
        }
        saveFilter($filterTitleComponent, title, title);
    });

    // Load the authors list.
    search($filterAuthorInputComponent, $filterAuthorInput, $filterAuthorList);

    // Load the series list.
    search($filterSeriesInputComponent, $filterSeriesInput, $filterSeriesList);

    // Select a filter: author.
    $(document).on('click', '#puzzle-search-author-list *[data-id]', function(e) {
        if ($(this).is('.active')) {
            $(this).removeClass('active');
        } else {
            resetFilterAuthorsState();
            $(this).addClass('active');
        }
        
        var $target = $('.active[data-id]', $filterAuthorList);
        if ($target.length > 0) {
            var username = $('.content', $target).text();
            var userID = $target.attr('data-id');

            saveFilter($filterAuthorComponent, userID, username);
            deleteFilterSeries();

            $filterAuthorInputComponent.modal('hide');
        }
    });

    // Select a filter: series.
    $(document).on('click', '#puzzle-search-series-list *[data-id]', function(e) {
        if ($(this).is('.active')) {
            $(this).removeClass('active');
        } else {
            resetFilterSeriesState();
            $(this).addClass('active');
        }
        
        var $target = $('.active[data-id]', $filterSeriesList);
        if ($target.length > 0) {
            var series = $('.content', $target).text();
            var seriesID = $target.attr('data-id');

            saveFilter($filterSeriesComponent, seriesID, series);
            deleteFilterAuthor();

            $filterSeriesInputComponent.modal('hide');
        }
    });

    // Select a filter: size range.
    $filterSizeColsMinInput.on('change', function() {
        updateFilterSizeRange();
    });
    $filterSizeColsMaxInput.on('change', function() {
        updateFilterSizeRange();
    });
    $filterSizeRowsMinInput.on('change', function() {
        updateFilterSizeRange();
    });
    $filterSizeRowsMaxInput.on('change', function() {
        updateFilterSizeRange();
    });

    // Save a filter: size range.
    $('button[data-dismiss]', $filterSizeRangesComponent).on('click', function(e) {
        var colsMin =$filterSizeColsMinInput.val();
        var colsMax = $filterSizeColsMaxInput.val();
        var rowsMin = $filterSizeRowsMinInput.val();
        var rowsMax = $filterSizeRowsMaxInput.val();

        saveFilter($filterSizeComponent, colsMin + '-' + colsMax + '-' + rowsMin + '-' + rowsMax, '(' + colsMin + '-' + colsMax + ') x (' + rowsMin + '-' + rowsMax + ')');
    });

    // Delete the filter: tag.
    $('.filter-delete', $filterTagComponent).on('click', function() {
        deleteFilterTag();
    });

    // Delete the filter: title.
    $('.filter-delete', $filterTitleComponent).on('click', function() {
        deleteFilterTitle();
    });

    // Delete the filter: author.
    $('.filter-delete', $filterAuthorComponent).on('click', function() {
        deleteFilterAuthor();
    });

    // Delete the filter: series.
    $('.filter-delete', $filterSeriesComponent).on('click', function() {
        deleteFilterSeries();
    });

    // Delete the filter: size.
    $('.filter-delete', $filterSizeComponent).on('click', function() {
        deleteFilterSize();
    });

    // Initialize the filters.
    $('*[data-filter-value]').each(function(){
        var $this = $(this);
        var value = $this.attr('data-filter-value');
        var displayedValue = $this.attr('data-filter-display-value');

        if (value.length > 0 && displayedValue.length > 0) {
            updateFilterUI($this, value, displayedValue);
        }
    });

    // Initialize the tag filter.
    var initialFilterTagID = $filterTagComponent.attr('data-filter-value');
    if (initialFilterTagID.length > 0) {
        var $initialFilterTagButton = $('.btn[data-id='+initialFilterTagID+']', $filterTagListComponent);
        $initialFilterTagButton.removeClass('btn-light').addClass('btn-info');
        var initialFilterTagName = $initialFilterTagButton.text();
        updateFilterUI($filterTagComponent, initialFilterTagID, initialFilterTagName);
    }

    // Initialize the sort filter.
    var sortValue = $sortComponent.attr('data-sort-value');
    var sortAsc = $sortComponent.attr('data-sort-asc');
    var $selectedSort = $('#puzzle-search-sort a[data-sort-value='+sortValue+'][data-sort-asc='+sortAsc+']');
    $sortComponent.html($selectedSort.first().html());

    // Initialize the solved filter.
    var solvedValue = $solvedComponent.attr('data-solved-value');
    var $selectedSolved = $('#puzzle-search-solved a[data-solved-value='+solvedValue+']');
    $solvedComponent.html($selectedSolved.first().html());

    saveSearchParameters();
});