/* --------------------------------------------------------------
 datatable.js 2016-07-11
 Gambio GmbH
 http://www.gambio.de
 Copyright (c) 2016 Gambio GmbH
 Released under the GNU General Public License (Version 2)
 [http://www.gnu.org/licenses/gpl-2.0.html]
 --------------------------------------------------------------
 */

jse.libs.datatable = jse.libs.datatable || {};

/**
 * ## DataTable Library
 *
 * This is a wrapper library for the manipulation of jQuery DataTables. Use the "create" method with DataTable
 * configuration to initialize a table on your page. All you need when using this library is an empty `<table>`
 * element. Visit the official website of DataTables to check examples and other information about the plugin.
 *
 * {@link http://www.datatables.net Official DataTables Website}
 *
 * Notice: Make sure that you load the DataTables vendor files before using this module.
 *
 * ### Examples
 *
 * **Example - Create A New Instance**
 * ```javascript
 * var tableApi = jse.libs.datatable.create($('#my-table'), {
 *      ajax: 'http://shop.de/table-data.php',
 *      columns: [
 *          { title: 'Name', data: 'name' defaultContent: '...' },
 *          { title: 'Email', data: 'email' },
 *          { title: 'Actions', data: null, orderable: false, defaultContent: 'Add | Edit | Delete' },
 *      ]
 * });
 * ```
 *
 * **Example - Add Error Handler**
 * ```javascript
 * jse.libs.datatable.error($('#my-table'), function(event, settings, techNote, message) {
 *      // Log error in the JavaScript console.
 *      console.log('DataTable Error:', message);
 * });
 * ```
 *
 * @module JSE/Libs/datatable
 * @exports jse.libs.datatable
 * @requires jQuery-DataTables-Plugin
 */
(function (exports) {

    'use strict';

    // ------------------------------------------------------------------------
    // VARIABLES
    // ------------------------------------------------------------------------

    let languages = {
        de: {
            'sEmptyTable': 'Keine Daten in der Tabelle vorhanden',
            'sInfo': '_START_ bis _END_ (von _TOTAL_)',
            'sInfoEmpty': '0 bis 0 von 0 Einträgen',
            'sInfoFiltered': '(gefiltert von _MAX_ Einträgen)',
            'sInfoPostFix': '',
            'sInfoThousands': '.',
            'sLengthMenu': '_MENU_ Einträge anzeigen',
            'sLoadingRecords': 'Wird geladen...',
            'sProcessing': 'Bitte warten...',
            'sSearch': 'Suchen',
            'sZeroRecords': 'Keine Einträge vorhanden.',
            'oPaginate': {
                'sFirst': 'Erste',
                'sPrevious': 'Zurück',
                'sNext': 'Nächste',
                'sLast': 'Letzte'
            },
            'oAria': {
                'sSortAscending': ': aktivieren, um Spalte aufsteigend zu sortieren',
                'sSortDescending': ': aktivieren, um Spalte absteigend zu sortieren'
            }
        },
        en: {
            'sEmptyTable': 'No data available in table',
            'sInfo': '_START_ to _END_ (of _TOTAL_)',
            'sInfoEmpty': 'Showing 0 to 0 of 0 entries',
            'sInfoFiltered': '(filtered from _MAX_ total entries)',
            'sInfoPostFix': '',
            'sInfoThousands': ',',
            'sLengthMenu': 'Show _MENU_ entries',
            'sLoadingRecords': 'Loading...',
            'sProcessing': 'Processing...',
            'sSearch': 'Search:',
            'sZeroRecords': 'No matching records found',
            'oPaginate': {
                'sFirst': 'First',
                'sLast': 'Last',
                'sNext': 'Next',
                'sPrevious': 'Previous'
            },
            'oAria': {
                'sSortAscending': ': activate to sort column ascending',
                'sSortDescending': ': activate to sort column descending'
            }
        }
    };

    // ------------------------------------------------------------------------
    // FUNCTIONALITY
    // ------------------------------------------------------------------------

    /**
     * Reorder the table columns as defined in the active columns array.
     *
     * @param {jQuery} $target Table jQuery selector object.
     * @param {Object} columnDefinitions Array containing the DataTable column definitions.
     * @param {Array} activeColumnNames Array containing the slug-names of the active columns.
     *
     * @return {Array} Returns array with the active column definitions ready to use in DataTable.columns option.
     *
     * @private
     */
    function _reorderColumns($target, columnDefinitions, activeColumnNames) {
        activeColumnNames.unshift('checkbox');
        activeColumnNames.push('actions');

        // Hide the table header cells that are not active.
        $.each(columnDefinitions, (index, columnDefinition) => {
            $target.find('thead tr').each(function () {
                let $headerCell = $(this).find(`[data-column-name="${columnDefinition.name}"]`);

                if (columnDefinition.data !== null && activeColumnNames.indexOf(columnDefinition.name) === -1) {
                    $headerCell.hide();
                }
            });
        });

        // Prepare the active column definitions.
        let finalColumnDefinitions = [],
            columnIndexes = [];

        $.each(activeColumnNames, (index, name) => {
            $.each(columnDefinitions, (index, columnDefinition) => {
                if (columnDefinition.name === name) {
                    // Add the active column definition in the "finalColumnDefinitions" array.
                    finalColumnDefinitions.push(columnDefinition);
                    const headerCellIndex = $target
                        .find(`thead:first tr:first [data-column-name="${columnDefinition.name}"]`)
                        .index();
                    columnIndexes.push(headerCellIndex);
                    return true; // continue
                }
            });
        });

        finalColumnDefinitions.sort((a, b) => {
            const aIndex = activeColumnNames.indexOf(a.name);
            const bIndex = activeColumnNames.indexOf(b.name);

            if (aIndex < bIndex) {
                return -1;
            } else if (aIndex > bIndex) {
                return 1;
            } else {
                return 0;
            }
        });

        // Reorder the table header elements depending the activeColumnNames order.
        $target.find('thead tr').each(function () {
            let activeColumnSelections = [$(this).find('th:first')];

            // Sort the columns in the correct order.
            columnIndexes.forEach((index) => {
                let $headerCell = $(this).find('th').eq(index);
                activeColumnSelections.push($headerCell);
            });

            // Move the columns to their final position.
            activeColumnSelections.forEach(function ($headerCell, index) {
                if (index === 0) {
                    return true;
                }

                $headerCell.insertAfter(activeColumnSelections[index - 1]);
            });
        });

        return finalColumnDefinitions;
    }

    /**
     * Creates a DataTable Instance
     *
     * This method will create a new instance of datatable into a `<table>` element. It enables
     * developers to easily pass the configuration needed for different and more special situations.
     *
     * @param {jQuery} $target jQuery object for the target table.
     * @param {Object} configuration DataTables configuration applied on the new instance.
     *
     * @return {DataTable} Returns the DataTable API instance (different from the jQuery object).
     */
    exports.create = function ($target, configuration) {
        return $target.DataTable(configuration);
    };

    /**
     * Sets the error handler for specific DataTable.
     *
     * DataTables provide a useful mechanism that enables developers to control errors during data parsing.
     * If there is an error in the AJAX response or some data are invalid in the JavaScript code you can use
     * this method to control the behavior of the app and show or log the error messages.
     *
     * {@link http://datatables.net/reference/event/error}
     *
     * @param {jQuery} $target jQuery object for the target table.
     * @param {Object} callback Provide a callback method called with the "event", "settings", "techNote",
     * "message" arguments (see provided link).
     */
    exports.error = function ($target, callback) {
        $.fn.dataTable.ext.errMode = 'none';
        $target
            .on('error.dt', callback)
            .on('xhr.dt', (event, settings, json, xhr) => {
                if (json.exception === true) {
                    callback(event, settings, null, json.message);
                }
            });
    };

    /**
     * Sets the callback method when ajax load of data is complete.
     *
     * This method is useful for checking PHP errors or modifying the data before
     * they are displayed to the server.
     *
     * {@link http://datatables.net/reference/event/xhr}
     *
     * @param {jQuery} $target jQuery object for the target table.
     * @param {Function} callback Provide a callback method called with the "event", "settings", "techNote",
     * "message" arguments (see provided link).
     */
    exports.ajaxComplete = function ($target, callback) {
        $target.on('xhr.dt', callback);
    };

    /**
     * Sets the table column to be displayed as an index.
     *
     * This method will easily enable you to set a column as an index column, used
     * for numbering the table rows regardless of the search, sorting and row count.
     *
     * {@link http://www.datatables.net/examples/api/counter_columns.html}
     *
     * @param {jQuery} $target jQuery object for the target table.
     * @param {Number} columnIndex Zero based index of the column to be indexed.
     */
    exports.indexColumn = function ($target, columnIndex) {
        $target.on('order.dt search.dt', function () {
            $target.DataTable().column(columnIndex, {
                search: 'applied',
                order: 'applied'
            }).nodes().each(function (cell, index) {
                cell.innerHTML = index + 1;
            });
        });
    };

    /**
     * Returns the german translation of the DataTables
     *
     * This method provides a quick way to get the language JSON without having to perform
     * and AJAX request to the server. If you setup your DataTable manually you can set the
     * "language" attribute with this method.
     *
     * @deprecated Since v1.4, use the "getTranslations" method instead.
     *
     * @return {Object} Returns the german translation, must be the same as the "german.lang.json" file.
     */
    exports.getGermanTranslation = function () {
        jse.core.debug.warn('DataTables Library: the getGermanTranslation method is deprecated and will be removed '
            + 'in JSE v1.5, please use the "getTranslations" method instead.');
        return languages.de;
    };

    /**
     * Get the DataTables translation depending the language code parameter.
     *
     * @param {String} languageCode Provide 'de' or 'en' (you can also use the jse.core.config.get('languageCode') to
     * get the current language code).
     *
     * @return {Object} Returns the translation strings in an object literal as described by the official DataTables
     * documentation.
     *
     * {@link https://www.datatables.net/plug-ins/i18n}
     */
    exports.getTranslations = function (languageCode) {
        if (languages[languageCode] === undefined) {
            jse.core.debug.warn('DataTables Library: The requested DataTables translation was not found:', languageCode);
            languageCode = 'en';
        }

        return languages[languageCode];
    };

    /**
     * Prepare table columns.
     *
     * This method will convert the column definitions to a DataTable compatible format and also reorder
     * the table header cells of the "thead" element.
     *
     * @param {jQuery} $target Table jQuery selector object.
     * @param {Object} columnDefinitions Array containing the DataTable column definitions.
     * @param {String[]} activeColumnNames Array containing the slug-names of the active columns.
     *
     * @return {Object[]} Returns array with the active column definitions ready to use in DataTable.columns option.
     */
    exports.prepareColumns = function ($target, columnDefinitions, activeColumnNames) {
        let convertedColumnDefinitions = [];

        for (let columnName in columnDefinitions) {
            let columnDefinition = columnDefinitions[columnName];
            columnDefinition.name = columnName;
            convertedColumnDefinitions.push(columnDefinition);
        }

        return _reorderColumns($target, convertedColumnDefinitions, activeColumnNames);
    };

}(jse.libs.datatable));