"use strict";

import "datatables.net";
import "datatables.net-bs";
import "datatables.net-buttons";
import "datatables.net-buttons-bs";
import "datatables.net-responsive";
import "datatables.net-responsive-bs";
import "datatables.net-select";
import "datatables.net-select-bs";
import "../../libs/datatables.sorting.datetime-moment/datatables.sorting.datetime-moment";
import "moment";
import DropdownModule from "../dropdown/dropdownModule";

/**
 * Table module options
 */
export interface ITableModuleOptions {

    /**
     * Selector or HTML element for the table you want to initialize
     */
    tableSelector: string | HTMLElement;

    /**
     * Optional: Which moment.js format to use for datetimes, if any. It is possible to add multiple by supplying an array. Default is [ "DD/MM/YYYY", "DD/MM/YYYY HH:mm" ].
     */
    datetimeFormat?: string[] | string;

    /**
     * Whether to enable paging or not. Default is true.
     */
    paging?: boolean;

    /**
     * Whether to enable lengthChange or not. Default is false.
     */
    lengthChange?: boolean;

    /**
     * Optional: Selector or HTML element to a <h3> title element for your table, if any.
     */
    titleSelector?: string | HTMLElement;

    /**
     * Optional: Selector or HTML element to a <div> element which contains filters for your table, if any.
     */
    filterSelector?: string | HTMLElement;

    /**
     * Optional: Base url to embla icons. Used for pagination arrows. Default is "/dist/icons"
     */
    iconBaseUrl?: string;

     /**
      * Optional: Override default language settings (default is danish)
      */
    language?: DataTables.LanguageSettings;

     /**
      * Optional: Responsive behavior(default is false)
      */
     responsive?: boolean;

    /**
     * Optional: This settings enables checkbox on rows the actions is supplied with HTML (default is false)
     */
     actions?: {
        actionsSelector: string | HTMLElement;
        selectedTextSingle: string;
        selectedTextPlural: string;
     };

    /**
     * Optional: Override additional datatable settings. Does not override datatable settings defined in ITableModuleOptions.
     */
    additionalDatatableSettings?: DataTables.Settings;
}

/**
 * Embla table modules which is based upon datatables
 */
export class TableModule {

    private datatablesApi: DataTables.Api;

    /**
     * Constructor for table, which initialises the module
     */
    constructor(options: ITableModuleOptions) {
        this.initModule(options);
    }

    public getDatatablesApi() {
        return this.datatablesApi;
    }

    /**
     * Filter the table by matching a given column's data with the given filter value
     * @param columnIndex 0 based index of the column to filter
     * @param filterValue Value to filter by
     * @param matchEntireColumn If the value should match the entire column
     * @param caseSensitive If the filter should be case sensitive
     */
    public filterColumn(columnIndex: number, filterValue: string, matchEntireColumn = true, caseSensitive = true) {

        const column =  this.datatablesApi.columns([columnIndex]);
        column.every(() => {
            if (filterValue === undefined || filterValue === null || filterValue === "") {
                column.search("");
            } else if (matchEntireColumn === true) {
                column.search("^" + filterValue + "$", true, false, caseSensitive === false);
            } else {
                column.search("^(.*)" + filterValue + "(.*)$", true, caseSensitive === false);
            }

            column.draw();
        });
    }

    public getAllSelectedRows() {
        return this.datatablesApi.rows( { selected: true } ).nodes();
    }

    private initModule(options: ITableModuleOptions) {

        const $table = $(options.tableSelector as any);
        const language = this.getLanguage(options);
        const dom = this.getDom(options);

        let settings: DataTables.Settings = {
            language: language,
            dom: dom,
            autoWidth: true,
            order: [] as any[],
            paging: options.paging,
            lengthChange: options.lengthChange,
            columnDefs: this.generateColumnDef(options),
        };

        if (options.responsive === true) {
            const responsiveLayout = this.getResponsiveSettings();
            const responsiveSetting = {
                responsive: responsiveLayout,
                columnDefs: this.generateColumnDef(options)
            };
            settings = $.extend(true, {}, settings, responsiveSetting);
            $table.width("100%");
            $table.addClass("responsive-table");
        }

        if (options.actions !== undefined && options.actions !== null) {
            const selectSettings = {
                select: {
                    style: "api",
                    selector: "td:first-child"
                }
            };
            
            $table.find("> thead > tr").prepend("<th class=\"checkbox-header-th\"><input class=\"datatable-select-all-checkbox\" type=\"checkbox\"/><label class=\"datatable-select-all-label\">&nbsp;</label></th>");
            $table.find("> tbody > tr").prepend("<td></td>");

            settings = $.extend(true, {}, settings, selectSettings);
        }
      
        if (options.datetimeFormat !== undefined) {
            if (options.datetimeFormat instanceof Array) {
                options.datetimeFormat.forEach((format) => {
                    ($.fn as any).dataTable.moment(format);
                });
            } else {
                ($.fn as any).dataTable.moment(options.datetimeFormat as string);
            }
        } else {
            ($.fn as any).dataTable.moment("DD/MM/YYYY"); // Uses moment.js format http://momentjs.com/docs/#/displaying/
            ($.fn as any).dataTable.moment("DD/MM/YYYY HH:mm");
        }

        $table.on("preInit.dt", (event) => {
                const $tableWrapper = $(event.target).closest(".dataTables_wrapper");

                let $filterColumn;
                let $titleColumn;

                let $customFilter;
                let $customTitle;

                if (options.filterSelector !== undefined) {
                    $filterColumn = $tableWrapper.find("div.filter-column");
                    $customFilter = $(options.filterSelector as any);
                }
                if (options.titleSelector !== undefined) {
                    $titleColumn = $tableWrapper.find("div.title-column");
                    $customTitle = $(options.titleSelector as any);
                }

                if ($customFilter !== undefined) {
                    $filterColumn.html($("<div>").append($customFilter).html());
                    $customFilter.remove();
                }

                if ($customTitle !== undefined) {
                    $titleColumn.html($("<div>").append($customTitle).html());
                    $customTitle.remove();
                }

                if (options.actions !== undefined && options.actions !== null) {
                    const $actionsDiv = $(options.actions.actionsSelector as any);
                    $tableWrapper.find(".selected-rows-actions").append($("<div>").append($actionsDiv).html());
                }

                if (options.lengthChange === true) {
                    new DropdownModule().init($tableWrapper.find(".dataTables_length select")[0], {
                        searchable: false,
                        width: "auto",
                    });
                }
        });

        if (options.additionalDatatableSettings !== undefined) {
            settings = $.extend(true, {}, settings, options.additionalDatatableSettings);
        }

        this.datatablesApi = $table.DataTable(settings);

        if (options.actions !== undefined && options.actions !== null) {
            this.initActions(options);
        }

        if (options.responsive !== undefined && options.responsive === true) {
            if ((this.datatablesApi as any).responsive.hasHidden() ) {
                $table.addClass("responsive-state");
            } else {
                $table.removeClass("responsive-state");
            }
           
            this.datatablesApi.on("responsive-resize", ( e, datatable, columns ) => {

                if (datatable.responsive.hasHidden() ) {
                    $table.addClass("responsive-state");
                } else {
                    $table.removeClass("responsive-state");
                }
            });
        }
    }

    private getLanguage(options: ITableModuleOptions) {

        let iconBaseUrl = options.iconBaseUrl;
        if (iconBaseUrl === undefined || iconBaseUrl == null) {
            iconBaseUrl = "/dist/icons";
        }

        const defaultLanguageOptions = {
            info: "Viser _START_ til _END_ af _TOTAL_ linjer",
            infoEmpty: "&nbsp;",
            infoFiltered: " (filtreret fra _MAX_ linjer)",
            infoPostFix: "",
            lengthMenu: "Vis _MENU_ linjer",
            loadingRecords: "Henter...",
            processing: "Henter",
            search: "S&oslash;g",
            zeroRecords: "Der blev ikke fundet nogen resultater",
            paginate: {
                first: "Første",
                last: "Sidste",
                next: "<svg class=\"embla-icon table-pagination-icon\" aria-hidden=\"true\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\"><use xlink:href=\"" + iconBaseUrl + "/sprite.symbol.svg#arrow-right\"></use></svg>",
                previous: "<svg class=\"embla-icon table-pagination-icon\" aria-hidden=\"true\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\"><use xlink:href=\"" + iconBaseUrl + "/sprite.symbol.svg#arrow-left\"></use></svg>"
            },
            url: "",
            searchPlaceholder: "Evt. søgekriterier",
        };

        if (options.paging === false) {
            defaultLanguageOptions.info = "&nbsp;";
            defaultLanguageOptions.infoFiltered = "Viser _TOTAL_ af _MAX_ linjer";
        }

        const extendedOptions = $.extend(true, {}, defaultLanguageOptions, options.language);
        
        return extendedOptions;
    }

    private initActions(options: ITableModuleOptions) {
        if (this.datatablesApi.tables().length > 1) {
            throw Error("If using actions only one table can be used with the selector");
        }

        const selectAllCheckbox = $(this.datatablesApi.tables().nodes()).find(".datatable-select-all-checkbox");

        $(this.datatablesApi.tables().nodes()).find(".datatable-select-all-label").on("click", () => {
            selectAllCheckbox.click();
        });

        this.datatablesApi.on("select", ( e, dt: DataTables.Api, type, indexes ) =>  {
            $(dt.rows(indexes).nodes()).find(".datatable-row-select-checkbox").prop("checked", true);
            
            const allSelected = dt.rows( { selected: false } ).any() === false;
            selectAllCheckbox.prop("checked", allSelected);

            this.updateActionsRow(options, dt);
        });

        this.datatablesApi.on("deselect", ( e, dt: DataTables.Api, type, indexes ) => {
            $(dt.rows(indexes).nodes()).find(".datatable-row-select-checkbox").prop("checked", false);
            selectAllCheckbox.prop("checked", false);

            this.updateActionsRow(options, dt);
        });

        selectAllCheckbox.on("change", () => {
            const checkAllIsChecked = selectAllCheckbox.is(":checked");
            
            if (checkAllIsChecked === true) {
                this.datatablesApi.rows().select();
            } else {
                this.datatablesApi.rows().deselect();
            }
        });

        $(this.datatablesApi.tables().nodes()).on("click", ".datatable-row-select-label", (e) => {
            e.stopPropagation();

            const tr = $(e.target).closest("tr");

            if (tr.hasClass("selected")) {
                this.datatablesApi.row(tr).deselect();
            } else {
                this.datatablesApi.row(tr).select();
            }
        });
    }

    private updateActionsRow(options: ITableModuleOptions, datatablesApi: DataTables.Api) {
        const selectedCount = datatablesApi.rows({ selected: true }).count();
        const actionsRow = $(datatablesApi.tables().containers()).find(".actions-row");
        const panelBodyActionRow = $(datatablesApi.tables().containers()).find(".panel-body-actions-row");

        if (selectedCount === 0) {
            panelBodyActionRow.removeClass("visible");
        } else if (selectedCount === 1) {
            panelBodyActionRow.addClass("visible");
            actionsRow.find(".selected-rows-text").html(selectedCount + " " + options.actions.selectedTextSingle);
        } else {
            panelBodyActionRow.addClass("visible");
            actionsRow.find(".selected-rows-text").html(selectedCount + " " + options.actions.selectedTextPlural);
        }
    }

    private getResponsiveSettings() {
        const responsive = {
            details: {
                renderer: function( api: any, rowIdx: any ) {
                    const data = api.cells( rowIdx, ":hidden" ).eq(0).map( function( cell: any ) {
                        const header = $( api.column( cell.column ).header() );
 
                        return '<tr class="embla-responsive-tr-border">' +
                                "<td>" +
                                    header.text() + " " +
                                "</td> " +
                                "<td>" +
                                    api.cell( cell ).data() +
                                "</td>" +
                            "</tr>";
                    } ).toArray().join("");
 
                    return data ?
                        $('<table class="embla-responsive-child-table margin-left-l"/>').append( data ) :
                        false;
                },
                type: "column",
                target: ".details-button"
            }
        };

        return responsive;
    }

    private getDom(options: ITableModuleOptions) {
        let dom = "";

        if (options.titleSelector !== undefined) {
            if (options.filterSelector !== undefined) {
                dom += "<'panel-body' <'row title-row'<'col-sm-12 title-column no-search-column'>>>";
            } else {
                dom += "<'panel-body' <'row title-row'<'col-sm-8 title-column'><'col-sm-4 search-column'f>>>";
            }
        }

        if (options.filterSelector !== undefined) {
            dom += "<'panel-body' <'row filter-row'<'col-sm-8 filter-column'><'col-sm-4 search-column'f>>>";
        }
        
        if (options.filterSelector === undefined && options.titleSelector === undefined) {
            dom += "<'panel-body' <'row filter-row'<'col-sm-12 search-column'f>>>";
        }

        if (options.actions !== undefined && options.actions !== null) {
            dom += "<'panel-body panel-body-actions-row' <'actions-row'<'action-column'<'selected-rows-text'><'selected-rows-actions'>>>>";
        }

        if (options.paging !== false && options.lengthChange === true) {
            dom += "<'row table-row' <'col-sm-12'<'embla-table-wrapper'tr<'embla-pagination-wrapper'pl>>>>";
        } else if (options.paging !== false) {
            dom += "<'row table-row' <'col-sm-12'<'embla-table-wrapper'tr<'embla-pagination-wrapper'p>>>>";
        } else {
            dom += "<'row table-row' <'col-sm-12'<'embla-table-wrapper'tr>>>";
        }
        
        const panel = "<'panel panel-default'" + dom + ">";

        return panel;
    }

    private generateColumnDef(options: ITableModuleOptions) {
        if (options.responsive === true && options.actions !== undefined && options.actions !== null) {
            return ([{
                    className: "details-button actions-enabled",
                    targets: 0,
                    responsivePriority: 1,
                    orderable: false,
                    render: (data: any, type: any, row: any, meta: any) => {
                        return "<input class=\"datatable-row-select-checkbox\" type=\"checkbox\"/><label class=\"datatable-row-select-label\">&nbsp;</label>";
                    }
                }
            ]);
        } else if (options.responsive === true) {
            return ([
                {
                    className: "details-button responsive-enabled",
                    orderable: true,
                    responsivePriority: 1,
                    targets:   0
                },
            ]);
        } else if (options.actions !== undefined && options.actions !== null) {
                return ([ {
                    className: "actions-enabled",
                    orderable: false,
                    targets:   0,
                    render: (data: any, type: any, row: any, meta: any) => {
                        return "<input class=\"datatable-row-select-checkbox\" type=\"checkbox\"/><label class=\"datatable-row-select-label\">&nbsp;</label>";
                        }
                }
            ]);
        }

        return undefined;
    }
}
