0 Comparison tables

// --- 0) Configuration ---
const FOLDER_PATH = '"Main/Comparison tables"'; // Keep your folder path as is, or translate if needed
const WORDS_PER_MINUTE = 200;
const USE_RELATIVE_MODIFIED_TIME = true;
const ITEMS_PER_PAGE = 15; // Number of items per page

// --- 1) Asynchronously load pages and metrics ---
const rawPagesQuery = dv.pages(FOLDER_PATH)
    .sort(p => p.file.mtime, 'desc');

const pageDataPromises = rawPagesQuery.map(async (p) => {
    let content = "", words = 0;
    try {
        content = await dv.io.load(p.file.path);
        words = content.split(/\s+/).filter(w => w.length > 0).length;
    } catch (e) { console.warn(`DataviewJS: Error loading ${p.file.path}:`, e); }
    const minutes = Math.ceil(words / WORDS_PER_MINUTE);
    const created = p.file.ctime.toISODate();
    const modified = p.file.mtime.toISODate();
    return [dv.fileLink(p.file.path, false, p.file.name), words, minutes, created, modified];
});

const pages = await Promise.all(pageDataPromises);
let filteredAndSortedData = [...pages]; // Data after filtering and sorting
let currentPage = 1;

// --- UI Setup ---
const container = dv.container;

const summaryElement = document.createElement('div');
summaryElement.style.cssText = `margin-bottom: 10px; font-size: 0.9em; color: var(--text-muted);`;
container.appendChild(summaryElement);

if (pages.length === 0) {
    summaryElement.textContent = `Files in folder ${FOLDER_PATH.replace(/"/g, '')} not found.`;
    return; // Stop script execution
}

const controlsContainer = document.createElement('div');
controlsContainer.style.cssText = `display: flex; align-items: center; margin-bottom: 15px; gap: 10px; flex-wrap: wrap;`;
container.appendChild(controlsContainer);

const filterInput = document.createElement('input');
filterInput.type = 'text';
filterInput.placeholder = 'Filter by table name…';
filterInput.style.cssText = `
    flex-grow: 1; padding: 8px 10px; box-sizing: border-box; 
    border: 1px solid var(--text-faint); background-color: var(--background-secondary); 
    color: var(--text-normal); border-radius: 5px; font-size: 0.9em; min-width: 200px;`;
controlsContainer.appendChild(filterInput);

const resetButton = document.createElement('button');
resetButton.textContent = "Reset";
resetButton.classList.add('mod-cta');
resetButton.style.padding = "8px 12px";
resetButton.style.fontSize = "0.9em";
controlsContainer.appendChild(resetButton);

const table = document.createElement('table');
table.classList.add('dataview', 'table-view-table');
container.appendChild(table);

const paginationContainer = document.createElement('div');
paginationContainer.style.cssText = `margin-top: 15px; display: flex; justify-content: center; align-items: center; gap: 10px;`;
paginationContainer.classList.add('pagination-controls'); // Add class for styles
container.appendChild(paginationContainer);

const headers = [
    { text: "Table Name", sortable: true, type: "link" },
    { text: "Words", sortable: true, type: "number", align: "right" },
    { text: "🕒 Min", sortable: true, type: "number", align: "right" }, // You might want to change "Min" to "Mins" or "Read Time"
    { text: "Created", sortable: true, type: "date", align: "center" },
    { text: "Modified", sortable: true, type: "date", align: "center" }
];

const thead = table.createTHead();
const headerRow = thead.insertRow();
let currentSort = { columnIndex: 4, asc: false }; // Default sort by "Modified" descending

function updateHeadersAppearance() {
    headerRow.childNodes.forEach((node, idx) => {
        if (node.nodeName === "TH" && headers[idx].sortable) {
            node.classList.remove('sort-active');
            let textContent = headers[idx].text;
            if (idx === currentSort.columnIndex && currentSort.columnIndex !== -1) {
                node.classList.add('sort-active');
                textContent += currentSort.asc ? ' <span class="sort-indicator sort-asc">▲</span>' : ' <span class="sort-indicator sort-desc">▼</span>';
            }
            node.innerHTML = textContent;
        }
    });
}

headers.forEach((h, i) => {
    const th = document.createElement('th');
    th.innerHTML = h.text;
    if (h.align) th.style.textAlign = h.align;
    if (h.sortable) {
        th.style.cursor = 'pointer';
        th.addEventListener('click', () => {
            const newSortAsc = (currentSort.columnIndex === i) ? !currentSort.asc : true;
            currentSort = { columnIndex: i, asc: newSortAsc };
            updateHeadersAppearance();
            applyFiltersAndSort();
        });
    }
    headerRow.appendChild(th);
});

const tbody = table.createTBody();

function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }

function renderCurrentPage() {
    tbody.innerHTML = '';
    const currentFilterTerm = filterInput.value.trim().toLowerCase(); // Ensure filter term is lowercase for matching
    const totalPages = Math.ceil(filteredAndSortedData.length / ITEMS_PER_PAGE);
    
    if (currentPage < 1) currentPage = 1;
    if (currentPage > totalPages && totalPages > 0) currentPage = totalPages;
    if (totalPages === 0 && filteredAndSortedData.length === 0) currentPage = 1;

    const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;
    const endIndex = Math.min(startIndex + ITEMS_PER_PAGE, filteredAndSortedData.length); // Ensure endIndex does not exceed array bounds
    const pageItems = filteredAndSortedData.slice(startIndex, endIndex);

    // Update summary text
    if (filteredAndSortedData.length === 0) {
        if (currentFilterTerm) {
            summaryElement.textContent = `No results for "${currentFilterTerm}" (Total in database: ${pages.length}).`;
        } else {
            summaryElement.textContent = `No data to display. (Total in database: ${pages.length}).`;
        }
    } else {
        let recordsRange = `${startIndex + 1}-${endIndex}`;
        let summaryText = `Showing ${recordsRange} of ${filteredAndSortedData.length}`;
        if (currentFilterTerm) {
             summaryText += ` (filtered). Total in database: ${pages.length}.`;
        } else {
             summaryText += `. Total in database: ${pages.length}.`;
        }
        summaryElement.textContent = summaryText;
    }

    pageItems.forEach(rowData => {
        const tr = tbody.insertRow();
        rowData.forEach((cellData, cellIndex) => {
            const td = tr.insertCell();
            const headerConfig = headers[cellIndex];
            if (headerConfig.align) td.style.textAlign = headerConfig.align;

            if (headerConfig.type === "link" && typeof cellData === 'object' && cellData.path) {
                const linkEl = document.createElement('a');
                let linkText = cellData.display || cellData.path.split('/').pop().replace(/\.md$/, '');
                if (currentFilterTerm && cellIndex === 0) { // Highlight only in "Table Name" column
                    try {
                        const regex = new RegExp(`(${escapeRegExp(currentFilterTerm)})`, 'gi');
                        linkText = linkText.replace(regex, '<span class="filter-highlight">$1</span>');
                    } catch (e) { /* ignore regex errors */ }
                }
                linkEl.innerHTML = linkText; // Use innerHTML for highlighted text
                linkEl.classList.add('internal-link');
                linkEl.dataset.href = cellData.path;
                linkEl.addEventListener('click', (e) => {
                    e.preventDefault();
                    app.workspace.openLinkText(cellData.path, dv.currentFilePath || "", e.ctrlKey || e.metaKey);
                });
                td.appendChild(linkEl);
            } else if (headerConfig.type === "date" && typeof cellData === 'string') {
                try {
                    const dateObj = dv.luxon.DateTime.fromISO(cellData);
                    if (dateObj.isValid) {
                        const modifiedHeaderIndex = headers.findIndex(h => h.text === "Modified"); // Find "Modified" header index
                        if (USE_RELATIVE_MODIFIED_TIME && cellIndex === modifiedHeaderIndex) {
                            td.textContent = dateObj.toRelative({locale: dv.settings.defaultLocale}) || dateObj.toLocaleString(dv.luxon.DateTime.DATE_MED, {locale: dv.settings.defaultLocale});
                        } else {
                            // Example: "May 20, 2025" (omitting weekday for brevity)
                            td.textContent = dateObj.toLocaleString(dv.luxon.DateTime.DATE_MED, {locale: dv.settings.defaultLocale});
                        }
                    } else { td.textContent = cellData; } // Show original string if date is invalid
                } catch (e) { td.textContent = cellData; } // Fallback on error
            } else {
                td.textContent = cellData != null ? cellData.toString() : ""; // Handle null/undefined
            }
        });
    });
    renderPaginationControls();
}

function renderPaginationControls() {
    paginationContainer.innerHTML = '';
    const totalItems = filteredAndSortedData.length;
    const totalPages = Math.ceil(totalItems / ITEMS_PER_PAGE);

    if (totalPages <= 1) return; // Don't show pagination if only one page or no data

    const prevButton = document.createElement('button');
    prevButton.textContent = 'Previous';
    prevButton.disabled = currentPage === 1;
    prevButton.addEventListener('click', () => {
        if (currentPage > 1) {
            currentPage--;
            renderCurrentPage();
        }
    });
    paginationContainer.appendChild(prevButton);

    const pageInfo = document.createElement('span');
    pageInfo.textContent = `Page ${currentPage} of ${totalPages}`;
    pageInfo.style.margin = "0 10px";
    paginationContainer.appendChild(pageInfo);

    const nextButton = document.createElement('button');
    nextButton.textContent = 'Next';
    nextButton.disabled = currentPage === totalPages;
    nextButton.addEventListener('click', () => {
        if (currentPage < totalPages) {
            currentPage++;
            renderCurrentPage();
        }
    });
    paginationContainer.appendChild(nextButton);
}

function applyFiltersAndSort() {
    // 1. Filtering
    const term = filterInput.value.toLowerCase().trim();
    if (term) {
        filteredAndSortedData = pages.filter(row => {
            const linkData = row[0]; // dv.fileLink object
            const fileName = linkData.display ? linkData.display.toLowerCase() : (linkData.path ? linkData.path.toLowerCase() : "");
            return fileName.includes(term);
        });
    } else {
        filteredAndSortedData = [...pages]; // Reset to all pages if filter is empty
    }

    // 2. Sorting
    if (currentSort.columnIndex !== -1) {
        const h = headers[currentSort.columnIndex];
        filteredAndSortedData.sort((a, b) => {
            let va = a[currentSort.columnIndex], vb = b[currentSort.columnIndex];
            if (h.type === "link") { // For file links, sort by display name or path
                va = va.display ? va.display.toString() : (va.path ? va.path.toString() : "");
                vb = vb.display ? vb.display.toString() : (vb.path ? vb.path.toString() : "");
            }
            if (h.type === "number") {
                va = parseFloat(va) || 0; vb = parseFloat(vb) || 0; // Handle NaN
                return currentSort.asc ? va - vb : vb - va;
            } else { // For strings and dates (ISO dates sort correctly as strings)
                return currentSort.asc
                    ? String(va).localeCompare(String(vb), dv.settings.defaultLocale, { sensitivity: 'base', numeric: (h.type !== "link" && h.type !== "date") })
                    : String(vb).localeCompare(String(va), dv.settings.defaultLocale, { sensitivity: 'base', numeric: (h.type !== "link" && h.type !== "date") });
            }
        });
    }
    
    currentPage = 1; // Reset to first page after filtering/sorting
    renderCurrentPage(); // Render the current page
}

// Initial setup
updateHeadersAppearance();
applyFiltersAndSort();

// Event Listeners
filterInput.addEventListener('input', applyFiltersAndSort);

resetButton.addEventListener('click', () => {
    filterInput.value = "";
    currentSort = { columnIndex: 4, asc: false }; // Reset sort to "Modified" desc
    updateHeadersAppearance();
    applyFiltersAndSort();
});

const style = document.createElement('style');
style.textContent = `
    .dataview.table-view-table th { white-space: nowrap; }
    .dataview.table-view-table td { vertical-align: middle; }
    .dataview.table-view-table tbody tr:nth-child(even) { 
        background-color: var(--background-secondary); /* Zebra striping */
    }
    .dataview.table-view-table tbody tr:hover { 
        background-color: var(--background-modifier-hover);
    }
    .sort-indicator { font-size: 0.8em; color: var(--text-muted); margin-left: 4px; }
    .dataview.table-view-table th.sort-active { 
        background-color: var(--background-primary-alt); /* Active sort column header */
        color: var(--text-accent);
    }
    .dataview.table-view-table th.sort-active .sort-indicator {
        color: var(--text-accent);
    }
    .dataview.table-view-table td a.internal-link { cursor: pointer; }
    .filter-highlight { /* Style for highlighted filter term */
        background-color: var(--text-highlight-bg);
        color: var(--text-normal);
        font-weight: bold;
        padding: 0 1px;
        border-radius: 2px;
    }
    /* Styles for pagination controls */
    .pagination-controls button {
        padding: 6px 10px;
        font-size: 0.9em;
        border: 1px solid var(--text-faint);
        background-color: var(--background-secondary);
        color: var(--text-normal);
        border-radius: 4px;
        cursor: pointer;
    }
    .pagination-controls button:disabled {
        opacity: 0.5;
        cursor: default;
    }
    .pagination-controls button:not(:disabled):hover {
        background-color: var(--background-modifier-hover);
    }
`;
container.appendChild(style);