/* --------------------------------------------------------------
 datatable_checkbox_mapping.js 2016-10-18
 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]
 --------------------------------------------------------------
 */

/**
 * ## DataTable Checkbox Mapping Extension
 *
 * This extension maps the bulk actions from a datatable dropdown to the selected checkbox carets. Bind this
 * extension into a datatable element which has a first.
 *
 * ### Options
 *
 * **Bulk Action Selector | `data-datatable_checkbox_mapping-bulk-action` | String | Optional**
 *
 * Provide a selector for the bulk action dropdown widget. Default value is '.bulk-action'.
 *
 * **Bulk Selection Checkbox Selector | `data-datatable_checkbox_mapping-bulk-selection` | String | Optional**
 *
 * Provide a selector for the bulk selection checkbox in the table header. Default value is '.bulk-selection'.
 *
 * **Row Selection Checkbox Selector | `data-datatable_checkbox_mapping-row-selection` | String | Optional**
 *
 * Provide a selector for the row selection checkbox in the table body. Default value is 'tbody tr input:checkbox'.
 *
 * **Caret Icon Class | `data-datatable_checkbox_mapping-caret-icon-class` | String | Optional**
 *
 * Provide a FontAwesome icon class for the checkbox caret. Default value is 'fa-caret-down'. Provide only the class
 * name without dots or the "fa" class.
 *
 * @module Admin/Extensions/datatable_checkbox_mapping
 */
gx.extensions.module(
    'datatable_checkbox_mapping',

    [],

    function (data) {

        'use strict';

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

        /**
         * Module Selector
         *
         * @type {jQuery}
         */
        const $this = $(this);

        /**
         * Default Options
         *
         * @type {Object}
         */
        const defaults = {
            bulkAction: '.bulk-action',
            bulkSelection: '.bulk-selection',
            caretIconClass: 'fa-caret-down',
            rowSelection: 'tbody tr input:checkbox'
        };

        /**
         * Final Options
         *
         * @type {Object}
         */
        const options = $.extend(true, {}, defaults, data);

        /**
         * Bulk Action Selector
         *
         * @type {jQuery}
         */
        const $bulkAction = $(options.bulkAction);

        /**
         * Bulk Selection Selector
         *
         * @type {jQuery}
         */
        const $bulkSelection = $this.find(options.bulkSelection).last();

        /**
         * Module Instance
         *
         * @type {Object}
         */
        const module = {};

        // ------------------------------------------------------------------------
        // FUNCTIONS
        // ------------------------------------------------------------------------

        /**
         * Toggle the dropdown menu under the caret.
         *
         * @param {jQuery.Event} event Triggered event.
         */
        function _toggleDropdownMenu(event) {
            event.stopPropagation();
            event.preventDefault();

            if ($bulkAction.hasClass('open')) {
                $bulkAction.removeClass('open');
                return;
            }

            const caretPosition = $(event.target).offset();
            const $dropdownMenu = $bulkAction.find('.dropdown-menu');

            // Open the dropdown menu.
            $bulkAction.addClass('open');

            // Reposition the dropdown menu near the clicked caret.
            $dropdownMenu.offset({
                top: caretPosition.top + 16,
                left: caretPosition.left
            });

            // Don't show the long empty dropdown menu box when it is repositioned.
            $dropdownMenu.css({bottom: 'initial'});

            // Show the dropdown menu under or above the caret, depending on the viewport.
            if (_dropdownIsOutOfView($dropdownMenu)) {
                $dropdownMenu.offset({
                    top: caretPosition.top - $dropdownMenu.outerHeight(),
                    left: caretPosition.left
                });
            }
        }

        /**
         * Reset the dropdown position to its original state.
         */
        function _resetDropdownPosition() {
            $bulkAction.find('.dropdown-menu').css({
                top: '',
                left: '',
                bottom: ''
            });
        }

        /**
         * Add a caret to the table head checked checkbox.
         */
        function _addCaretToBulkSelection() {
            const $th = $bulkSelection.parents('th');

            if ($th.find('.' + options.caretIconClass).length === 0) {
                $th.append(`<i class="fa ${options.caretIconClass}"></i>`);
            }
        }

        /**
         * Remove the caret from the bulk selection checkbox.
         */
        function _removeCaretFromBulkSelection() {
            $bulkSelection.parents('th').find('.' + options.caretIconClass).remove();
        }

        /**
         * Add a caret to the checked checkbox.
         *
         * @param {jQuery.Event} event Triggered event.
         */
        function _addCaretToActivatedCheckbox(event) {
            $(event.target).parents('td').append(`<i class="fa ${options.caretIconClass}"></i>`);
        }

        /**
         * Remove the caret from the checkbox if the checkbox is unchecked.
         *
         * @param {jQuery.Event} event Triggered event.
         */
        function _removeCaretFromCheckbox(event) {
            $(event.target).parents('tr').find('.' + options.caretIconClass).remove();
        }

        /**
         * Start listening for click events for the caret symbol.
         *
         * When the caret symbol gets clicked, show the dropdown menu.
         */
        function _listenForCaretClickEvents() {
            $this.find('tr .' + options.caretIconClass).off('click').on('click', _toggleDropdownMenu);
        }

        /**
         * Set the bulk selection state.
         *
         * @param {Boolean} isChecked Whether the checkbox will be checked or not.
         */
        function _setBulkSelectionState(isChecked) {
            $bulkSelection.prop('checked', isChecked);

            if (isChecked) {
                $bulkSelection.parents('.single-checkbox').addClass('checked');
                _addCaretToBulkSelection();
                _listenForCaretClickEvents();
            } else {
                $bulkSelection.parents('.single-checkbox').removeClass('checked');
                _removeCaretFromBulkSelection();
            }
        }

        /**
         * Checks if the provided dropdown is outside of the viewport (in height).
         *
         * @param {jQuery} $dropdownMenu Dropdown menu selector.
         *
         * @return {Boolean}
         */
        function _dropdownIsOutOfView($dropdownMenu) {
            const dropDownMenuOffset = $dropdownMenu.offset().top + $dropdownMenu.outerHeight() + 50;
            const windowHeight = window.innerHeight + $(window).scrollTop();
            return dropDownMenuOffset > windowHeight;
        }

        /**
         * On Single Checkbox Ready Event
         *
         * Bind the checkbox mapping functionality on the table. We need to wait for the "single_checkbox:ready",
         * that will be triggered with every table re-draw. Whenever a row checkbox is clicked the bulk-action
         * caret icon will be added next to it.
         */
        function _onSingleCheckboxReady() {
            // Find all checkboxes table body checkboxes.
            const $tableBodyCheckboxes = $this.find(options.rowSelection);

            // Table data checkbox event handling.
            $tableBodyCheckboxes.on('change', event => {
                // Close any open dropdown menus.
                $bulkAction.removeClass('open');

                if ($(event.target).prop('checked')) {
                    _addCaretToActivatedCheckbox(event);
                    _listenForCaretClickEvents();
                } else {
                    _removeCaretFromCheckbox(event);
                }

                // Activate the table head checkbox if all checkboxes are activated. Otherwise deactivate it.
                _setBulkSelectionState($tableBodyCheckboxes.not(':checked').length === 0);
            });
        }

        /**
         * Add or remove the caret from the table head checkbox.
         *
         * @param {jQuery.Event} event
         */
        function _onBulkSelectionChange(event) {
            if ($bulkSelection.parents('.single-checkbox').length === 0) {
                return; // Do not proceed with the function if the thead single-checkbox is not ready yet.
            }

            if ($bulkSelection.prop('checked')) {
                _addCaretToBulkSelection();
                _listenForCaretClickEvents();
            } else {
                _removeCaretFromBulkSelection(event);
            }
        }

        /**
         *  Event handling for the original dropdown button click.
         */
        function _onBulkActionDropdownToggleClick() {
            _resetDropdownPosition();
        }

        // ------------------------------------------------------------------------
        // INITIALIZATION
        // ------------------------------------------------------------------------

        module.init = function (done) {
            $this.on('single_checkbox:ready', _onSingleCheckboxReady);
            $bulkSelection.on('change', _onBulkSelectionChange);
            $bulkAction.find('.dropdown-toggle').on('click', _onBulkActionDropdownToggleClick);
            done();
        };
        return module;
    });