/*jslint nomen: true, plusplus: true, passfail: true, browser: true, devel: true, white: true */
/*global $ */

$(document).ready(function () {
    "use strict";

    var
        body = $('body'),
        recordsTable = $('#list'),
        filtersContainer = $('#filters'),
        filterTab = $('#tabfilters'),
        savedFiltersTab = $('#tabsaved'),
        toggle = $('#filter-toggle'),
        fastFiltersContainer = $('.filter-status'),
        searchFormContainer = $('.pongho-search-form-wrapper'),
        operators = filtersContainer.data('operators'),
        enums = filtersContainer.data('enums'),
        destroyChosen = function (field) {
            if (field.data('chosen')) {
                field.removeData('chosen');
                field.next().remove();
            }
        },
        filterHelpers = {
            autocomplete: {
                apply: function (valueField, selectedOption) {
                    valueField.autocomplete({
                        source: selectedOption.data('source')
                    });
                },
                update: function (valueField, selectedOption) {},
                destroy: function (valueField) {
                    if (valueField.hasClass('ui-autocomplete-input')) {
                        valueField.autocomplete('destroy');
                    }
                }
            },
            chosen: {
                apply: function (valueField, selectedOption) {
                    valueField.chosen(Pongho.chosen);
                },
                update: function (valueField, selectedOption) {
                    valueField.chosen().trigger('chosen:updated');
                },
                destroy: destroyChosen
            }
        };

    /**
     * AJAX per paginazione e filtri rapidi
     */
    body.on('click', '.filter-status a, .pagination a, a.orderable, #list a.delete, #list a.restore, #list a.trash', function (event) {
        event.preventDefault();

        startTableLoading();

        $.ajax({
            'url': this.href,
            'dataType ': 'json',
            'success': function (response) {
                fillFastFilters(response.fast_filters);
                fillSearchForm(response.search_form);

                stopTableLoading(response.list);
            }
        });
    });

    /**
     * Apre e chiude il contenitore dei filtri
     */
    toggle.on('click', function (event) {
        event.preventDefault();

        toggle.toggleClass('open');
        filtersContainer.toggleClass('open');
    });

    /**
     * Serializza la form
     */
    function serializeFilterForm(form) {
        var filters = {};

        form.find('.repeater-row').each(function () {
            var $row = $(this),
                selector = 'filters[filter][' + $row.data('id') + ']',
                fieldName = $row.find('[name="' + selector + '[field]"]').val(),
                fieldOperator,
                fieldValueField,
                fieldValue;

            if (fieldName) {
                fieldOperator = $row.find('[name="' + selector + '[operator]"]').val();
                fieldValueField = $row.find('[name="' + selector + '[valuefield]"]').val();

                if (fieldValueField) {
                    fieldValue = $row.find('[name^="' + selector + '[' + fieldValueField + ']"]').val();

                    if (fieldOperator === 'DATEBETWEEN') {
                        fieldValue = [fieldValue];
                        fieldValue.push($row.find('[name^="' + selector + '[' + fieldValueField + '_to]"]').val());
                    }
                }
            }

            if (fieldName && fieldOperator) {
                if (filters[fieldName] === undefined) {
                    filters[fieldName] = {};
                }

                filters[fieldName][fieldOperator] = fieldValue;
            }
        });

        return $.param({"f": filters});
    }

    /**
     * Crea il codice HTML per le opzioni <option> di una select.
     */
    function getSelectOptions(options) {
        if (typeof options === 'string') {
            return options;
        }

        var code = '';
        $.each(options, function (val, lbl) {
            if (typeof lbl === 'object') {
                var innercode = '';
                $.each(lbl.optgroup, function (optval, optlbl) {
                    innercode += '<option value="' + optval + '">' + optlbl + '</option>';
                });

                code += '<optgroup label="' + val + '">' + innercode + '</optgroup>';
            } else {
                code += '<option value="' + val + '">' + lbl + '</option>';
            }
        });

        return code;
    }

    /**
     * Prepara gli eventi nella singola condizione del filtro.
     */
    function initFilterConditionRow(row, reloadFilterValue) {
        var
            filterField = row.find('.filter-field'),
            operatorField = row.find('.filter-operator'),
            valuesFields = row.find('.filter-value'),
            valueNameField = row.find('.filter-valuefield'),
            typeField = row.find('.filter-type'),
            selectedOption,
            filterType;

        setOperator(reloadFilterValue);

        // Al cambio della select principale, visualizzo l’operatore appropriato.
        filterField.on('change', function () {
            setOperator(true);
        });

        // Al cambio dell’operatore, visualizzo il campo valore appropriato.
        operatorField.on('change', function () {
            setValueField(true);
        });

        /**
         * Imposta il campo operatore in base al tipo di filtro.
         */
        function setOperator(reloadFilterValue) {
            // Nascondo tutto
            operatorField.hide();
            valuesFields.hide();

            // Imposto le variabili che cambiano con il cambio del tipo di filtro
            selectedOption = filterField.find('option:selected');
            filterType = selectedOption.data('type');

            // Esco se non ho un tipo di filtro valido
            if (!filterType) {
                if (console && console.info) {
                    console.info('The filter type is not defined.');
                }

                return;
            }

            if (!operators[filterType]) {
                if (console && console.info) {
                    console.info('The filter "' + filterType + '" type does not exist.');
                }

                return;
            }

            // Popolo la select dell'operatore
            if (reloadFilterValue) {
                operatorField.html(getSelectOptions(operators[filterType]));
            }

            // Visualizzo la select dell'operatore solo se contiene più di una opzione
            if (operatorField.find('option').length > 1) {
                operatorField.show();
            }

            setValueField(reloadFilterValue);
        }

        /**
         * Imposta il campo valore (.filter-value) in base al tipo di filtro ed all'operatore impostato.
         */
        function setValueField(reloadFilterValue) {
            // Nascondo tutto
            valuesFields.hide().each(function () {
                var valueField = $(this);

                $.each(filterHelpers, function (helperName, helper) {
                    helper.destroy(valueField);
                });
            });

            // Mi assicuro di avere le variabili corrette
            selectedOption = filterField.find('option:selected');
            filterType = selectedOption.data('type');

            typeField.val(filterType);

            if (!filterType) {
                if (console && console.info) {
                    console.info('The filter type is not defined.');
                }

                return;
            }

            var
                operatorValue = operatorField.val(),
                valueField;

            switch (filterType) {
                case 'string':
                case 'numeric':
                case 'foreign-string':
                    if (operatorValue === 'IN' || operatorValue === 'NOTIN') {
                        valueField = row.find('.filter-value-multiple');
                        valueNameField.val('value_multiple');
                    } else {
                        valueField = row.find('.filter-value-text');
                        valueNameField.val('value_text');
                    }

                    break;

                case 'foreign':
                case 'foreign-enum':
                    if (operatorValue === 'IN' || operatorValue === 'NOTIN') {
                        valueField = row.find('.filter-value-multiple');
                        valueNameField.val('value_multiple');
                    } else {
                        valueField = row.find('.filter-value-select');
                        valueNameField.val('value_select');
                    }

                    break;

                case 'enum':
                    valueField = row.find('.filter-value-select');
                    valueNameField.val('value_select');
                    break;

                case 'date':
                case 'datetime':
                case 'time':
                    valueField = row.find('.filter-value-' + filterType);
                    valueNameField.val('value_' + filterType);

                    if (operatorValue === 'DATEBETWEEN') {
                        valueField = row.find('.filter-value-' + filterType + '-between');
                    }

                    break;

                default:
                    console.log('The filter "' + filterType + '" type does not exist.');
                    break;
            }

            // Se scelgo di testare sul null, non devo specificare un valore (è null), quindi lo resetto in tutti
            // i campi value per evitare di salvare dati inutili
            if (operatorValue === 'NULL' || operatorValue === 'NOTNULL') {
                valuesFields.val('');
            } else {
                valueField.show();
            }

            switch (filterType) {
                case 'foreign':

                    // Nel caso l’operatore sia impostato su "Compilato" o "Non compilato",
                    // il campo del valore non esiste e non ha quindi senso popolarlo.
                    if (operatorValue === 'NULL' || operatorValue === 'NOTNULL') {
                        return;
                    }

                    if (operatorValue === 'IN' || operatorValue === 'NOTIN') {
                        valueField = row.find('.filter-value-multiple');
                    } else {
                        valueField = row.find('.filter-value-select');
                    }

                    applyHelper(valueField, selectedOption);

                    if (reloadFilterValue) {
                        // Svuoto il campo per evitare che cambiando il tipo di filtro, non restino le
                        // vecchie opzioni finché non termina la chiamata AJAX.
                        valueField.html('');

                        $.ajax({
                            'url': filterField.data('ref'),
                            'data': {
                                f: selectedOption.val()
                            },
                            'success': function (data) {
                                valueField.html(getSelectOptions(data));

                                updateHelper(valueField, selectedOption);
                            }
                        });
                    }

                    break;

                case 'foreign-enum':

                    // Nel caso l’operatore sia impostato su "Compilato" o "Non compilato",
                    // il campo del valore non esiste e non ha quindi senso popolarlo.
                    if (operatorValue === 'NULL' || operatorValue === 'NOTNULL') {
                        return;
                    }

                    if (operatorValue === 'IN' || operatorValue === 'NOTIN') {
                        valueField = row.find('.filter-value-multiple');
                    } else {
                        valueField = row.find('.filter-value-select');
                    }

                    if (reloadFilterValue) {
                        valueField.html(getSelectOptions(enums[selectedOption.val()]));
                    }

                    applyHelper(valueField, selectedOption);

                    break;

                case 'enum':

                    valueField = row.find('.filter-value-select');

                    if (reloadFilterValue) {
                        valueField.html(getSelectOptions(enums[selectedOption.val()]));
                    }

                    applyHelper(valueField, selectedOption);

                    break;
            }
        }
    }

    /**
     * Applica un helper al campo value.
     */
    function applyHelper(valueField, selectedOption) {
        var helperName = selectedOption.data('helper');

        if (!filterHelpers[helperName]) {
            return;
        }

        filterHelpers[helperName].apply(valueField, selectedOption);
    }

    /**
     * Applica un helper al campo value.
     */
    function updateHelper(valueField, selectedOption) {
        var helperName = selectedOption.data('helper');

        if (!filterHelpers[helperName]) {
            return;
        }

        filterHelpers[helperName].update(valueField, selectedOption);
    }

    /**
     * Aggancia gli eventi per le condizioni del filtro (le righe del repeater).
     */
    function initFilterConditions(reloadFilterValue) {
        $('.repeater-fields').each(function () {
            initFilterConditionRow($(this), reloadFilterValue);
        });
    }

    /**
     * Collega il repeater nella tab del filtro.
     */
    function attachFilterTabRepeater() {
        filterTab.find('.repeater')
            .repeater()
            .on('row_add', function (event, row) {
                initFilterConditionRow(row, true);
                initFilterFieldChosen(row);
            });
    }

    /**
     * Corregge il target dei link nella tabella dei risultati.
     */
    function fixTargetOfEditOnRecordsTable() {
        recordsTable.find('.edit').each(function () {
            $(this).attr('target', this.href);
        });
    }

    /**
     * Inizia il loading della tabella.
     */
    function startTableLoading() {
        recordsTable.empty().addClass('loading');
    }

    /**
     * Ferma il loading della tabella e sostituisce il contenuto.
     */
    function stopTableLoading(newContent) {
        recordsTable.removeClass('loading');

        if (newContent) {
            recordsTable
                .html(newContent)
                .find('table.pongho-table').tableManager();

            fixTargetOfEditOnRecordsTable();
        }
    }

    /**
     * Sostituisce il contenuto HTML nella tab del filtro e ricollega il repeater.
     */
    function fillFilterTab(newContent, reloadFilterValue) {
        filterTab.html(newContent);

        attachFilterTabRepeater();
        initFilterConditions(reloadFilterValue);
    }

    /**
     * Ripopola la tabella dei filtri salvati.
     */
    function fillSavedFiltersTab(newContent) {
        savedFiltersTab
            .html(newContent)
            .find('table.pongho-table').tableManager();
    }

    /**
     * Ripopola i filtri veloci.
     */
    function fillFastFilters(newContent) {
        fastFiltersContainer.html(newContent);
    }

    /**
     * Rigenera la form di ricerca.
     */
    function fillSearchForm(newContent) {
        searchFormContainer.html(newContent);
    }

    /**
     * Inizializza il Chosen del selettore dei campi
     */
    function initFilterFieldChosen(row) {
        var filterField;

        // Se ho la riga ricollego solo quello della riga passata, altrimenti tutti quelli della tab
        if (row) {
            filterField = row.find('.filter-field');
        } else {
            filterField = filterTab.find('.filter-field');
        }

        destroyChosen(filterField);

        filterField.chosen(Pongho.chosen);
    }

    /**
     * Inizializza la tab del filtro.
     */
    function initFilterTab() {
        attachFilterTabRepeater();

        /**
         * Azzera le condizioni impostate per creare un nuovo filtro
         */
        filterTab.on('click', '#new-filter', function (event) {
            event.preventDefault();

            startTableLoading();

            var form = filterTab.find('#form-filters');

            $.ajax({
                'url': form.attr('action'),
                'success': function (response) {
                    fillFastFilters(response.fast_filters);
                    fillSearchForm(response.search_form);
                    fillFilterTab(response.form, true);

                    stopTableLoading(response.list);
                }
            });
        });

        /**
         * Esegue il filtro impostato
         */
        filterTab.on('submit', '#form-filters', function (event) {
            event.preventDefault();

            startTableLoading();

            var form = $(this);

            $.ajax({
                'url': form.attr('action'),
                'data': serializeFilterForm(form),
                'success': function (response) {
                    fillFastFilters(response.fast_filters);
                    fillSearchForm(response.search_form);

                    stopTableLoading(response.list);
                }
            });
        });

        /**
         * Invio della form per il salvataggio del filtro
         * Ricarica l'elenco dei filtri salvati quando salvo un filtro
         */
        filterTab.on('click', '#save-filter', function (event) {
            event.preventDefault();

            var form = filtersContainer.find('#form-filters');

            $.ajax({
                'url': form.data('save'),
                'data': form.serialize(),
                'type': 'POST',
                'success': function (response) {
                    fillFilterTab(response.form, false);
                    fillSavedFiltersTab(response.filters);
                }
            });
        });
    }

    /**
     * Inizializza la tab dei filtri salvati.
     */
    function initSavedFiltersTab() {

        /**
         * Esegue un filtro salvato e passa alla tab del filtro
         */
        savedFiltersTab.on('click', '.actions .run', function (event) {
            event.preventDefault();

            startTableLoading();

            $.ajax({
                'url': this.href,
                'success': function (response) {
                    fillFastFilters(response.fast_filters);
                    fillSearchForm(response.search_form);
                    fillFilterTab(response.form, false);

                    stopTableLoading(response.list);

                    // Attivo la tab del filtro
                    filtersContainer.find('.tabs a[href="#tabfilters"]').trigger('click');

                    initFilterFieldChosen();
                }
            });
        });

        /**
         * Gestisce l'eliminazione di un filtro salvato
         */
        savedFiltersTab.on('click', '.actions .delete', function (event) {
            event.preventDefault();

            $.ajax({
                'url': this.href,
                'success': function (response) {
                    if (!response.error) {
                        $(this).closest('tr').fadeOut(function () {
                            $(this).remove();
                        });
                    } else {
                        alert(response.message);
                    }
                }
            });
        });
    }

    /**
     * Inizializza il tutto.
     */
    function init() {
        initFilterTab();
        initSavedFiltersTab();
    }

    init();

});
