
async function initTable(containingSelector, tableId, loadDataFunc) {
    let pageSizeId = tableId + "PageSize";
    createTable(containingSelector, tableId);
    createTableControls(containingSelector, pageSizeId);

    await bindTablePagination(`${containingSelector} .table-pagination`, loadDataFunc);
    await bindTablePaginationResizer(`${containingSelector} .table-page-sizer`, loadDataFunc);
}

function createTable(containingSelector, tableId) {
    $(containingSelector).append(`
    <div class="table-responsive my-3 px-3">
        <table id="${tableId}" class="table table-hover align-middle"></table>
    </div>
    `);
}

function createTableControls(containingSelector, pageSizeId) {
    $(containingSelector).append(`
    <div class="table-controls row mb-3 px-3">
        <div class="col-lg-2">
            <div class="table-page-info d-flex justify-content-start align-items-center mx-auto">
                <span class="pageinfo-total"></span>&nbsp;Results
            </div>
        </div>
        <div class="col-lg-8">
            <nav class="table-pagination d-flex justify-content-center">
                <ul class="pagination mb-0">
                    <li class="page-item">
                        <button type="button" class="page-link page-prev rounded-start-1">
                        <i class="bi bi-chevron-left"></i>
                        </button>
                    </li>
                    <li class="page-item">
                        <button type="button" class="page-link page-next rounded-end-1">
                            <i class="bi bi-chevron-right"></i>
                        </button>
                    </li>
                </ul>
            </nav>
        </div>
        <div class="col-lg-2">
            <div class="d-flex justify-content-end">
                <label for="${pageSizeId}" class="form-label align-self-center mb-0 me-2">Per Page</label>
                <select name="${pageSizeId}" id="${pageSizeId}" class="select-2 table-page-sizer">
                    <option value="10" selected>10&emsp;</option>
                    <option value="15">15&emsp;</option>
                    <option value="20">20&emsp;</option>
                    <option value="25">25&emsp;</option>
                </select>
            </div>
        </div>
    </div>
    `);

    $("#" + pageSizeId).select2({
        theme: "bootstrap",
        minimumResultsForSearch: 10,
    });
}

// Updates the pagination text for a given pageInfoId
function updateTablePaginationInfo(pageInfoId, showing, total) {
    $(`${pageInfoId} .pageinfo-showing`).text(showing);
    $(`${pageInfoId} .pageinfo-total`).text(total);
}

// Updates the pagination buttons for a given pageControlsId
function updateTablePagination(pageControlsId, currentPage, pageSize, totalItems, nextExists, prevExists) {
    $(pageControlsId).attr("data-page", currentPage);

    // Remove existing page specific buttons
    $(`${pageControlsId} .page-pick`).remove();

    // Determine states of 'previous page' 'next page' buttons
    $(`${pageControlsId} .page-prev`).toggleClass("disabled", !prevExists).attr("tabindex", prevExists ? "" : "-1");
    $(`${pageControlsId} .page-next`).toggleClass("disabled", !nextExists).attr("tabindex", nextExists ? "" : "-1");

    // Calculate amount of pages to account for
    const pages = Math.max(Math.ceil(totalItems / pageSize), 1);
    const maxVisiblePages = 10;

    let startPage, endPage;

    if (pages <= maxVisiblePages) {
        // If total pages are less than or equal to max visible pages, show all
        startPage = 1;
        endPage = pages;
    } else {
        // Determine the start and end pages
        const halfVisible = Math.floor(maxVisiblePages / 2);

        if (currentPage <= halfVisible) {
            startPage = 1;
            endPage = maxVisiblePages;
        } else if (currentPage + halfVisible >= pages) {
            startPage = pages - maxVisiblePages + 1;
            endPage = pages;
        } else {
            startPage = currentPage - halfVisible;
            endPage = currentPage + halfVisible;
        }
    }

    if (startPage > 1) {
        addPageButton(pageControlsId, 1);
        if (startPage > 2) {
            addEllipsis(pageControlsId);
        }
    }

    for (let i = startPage; i <= endPage; i++) {
        addPageButton(pageControlsId, i, currentPage);
    }

    if (endPage < pages) {
        if (endPage < pages - 1) {
            addEllipsis(pageControlsId);
        }
        addPageButton(pageControlsId, pages);
    }
}

function addPageButton(pageControlsId, pageNumber, currentPage) {
    let pageItem = $("<li>").addClass("page-item");
    let pageLink = $("<button>")
        .attr("type", "button")
        .attr("data-page", pageNumber)
        .addClass("page-link page-pick")
        .text(pageNumber);

    if (pageNumber === currentPage) {
        pageLink.addClass("disabled").attr("tabindex", -1);
    }

    pageItem.append(pageLink);
    $(`${pageControlsId} .pagination .page-next`).parent().before(pageItem);
}

function addEllipsis(pageControlsId) {
    let ellipsisItem = $("<li>").addClass("page-item disabled");
    let ellipsisLink = $("<span>").addClass("page-link page-pick").text("...");
    ellipsisItem.append(ellipsisLink);
    $(`${pageControlsId} .pagination .page-next`).parent().before(ellipsisItem);
}

// Bind the table pagination buttons to control the table pagination
async function bindTablePagination(pageControlsId, dataLoadFunc) {
    $(`${pageControlsId} .pagination`).on("click", ".page-link", async function() {
        let wantedPage;
        let currentPage = parseInt($(pageControlsId).attr("data-page"));

        if ($(this).hasClass("page-prev"))
            wantedPage = currentPage - 1;
        else if ($(this).hasClass("page-next"))
            wantedPage = currentPage + 1;
        else
            wantedPage = $(this).attr("data-page");

        await dataLoadFunc(getCurrentlyActiveServer().guild_id, wantedPage)
    });
}

// Bind the table pagination page resizer control
async function bindTablePaginationResizer(resizerControlId, dataLoadFunc) {
    $(resizerControlId).on("change", async function() {
        const page = 1;
        const pageSize = $(this).val();
    
        await dataLoadFunc(getCurrentlyActiveServer().guild_id, page, pageSize);
    });
}

function bindTableCheckboxes(tableSelector, tableObject, deleteButtonSelector) {
    // Select a row via checkbox
    $(tableSelector).on("change", "tbody tr .table-select-row", function() {
    
        var selected = $(this).prop("checked");
            rowIndex = $(this).closest("tr").index();
            row = tableObject.row(rowIndex);
    
        if (selected) row.select();
        else row.deselect();
    
        determineSelectAllState(tableSelector, tableObject, deleteButtonSelector);
    });
    
    // Select all rows checkbox
    $(tableSelector).on("change", "thead .table-select-all", function() {
        var selected = $(this).prop("checked");
        $(`${tableSelector} tbody tr`).each(function(rowIndex) {
            var row = tableObject.row(rowIndex);
    
            if (selected) row.select();
            else row.deselect();
    
            $(this).find('.table-select-row').prop("checked", selected);
        });
    
        determineSelectAllState(tableSelector, tableObject, deleteButtonSelector);
    });
}

function determineSelectAllState(tableSelector, tableObject, deleteButtonSelector) {
    var selectedRowsCount = tableObject.rows(".selected").data().toArray().length;
        allRowsCount = tableObject.rows().data().toArray().length;

        selectAllCheckbox = $(`${tableSelector} thead .table-select-all`);

        checked = selectedRowsCount === allRowsCount;
        indeterminate = !checked && selectedRowsCount > 0;

    selectAllCheckbox.prop("checked", checked);
    selectAllCheckbox.prop("indeterminate", indeterminate);

    $(deleteButtonSelector).prop("disabled", !(checked || indeterminate) || !(allRowsCount > 0));
}
