"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Headers = void 0;
exports.buildHeaderGroups = buildHeaderGroups;
const utils_1 = require("../utils");
const debug = 'debugHeaders';
//
function createHeader(table, column, options) {
    var _a;
    const id = (_a = options.id) !== null && _a !== void 0 ? _a : column.id;
    const header = {
        id,
        column,
        index: options.index,
        isPlaceholder: !!options.isPlaceholder,
        placeholderId: options.placeholderId,
        depth: options.depth,
        subHeaders: [],
        colSpan: 0,
        rowSpan: 0,
        headerGroup: null,
        getLeafHeaders: () => {
            const leafHeaders = [];
            const recurseHeader = (h) => {
                if (h.subHeaders && h.subHeaders.length) {
                    h.subHeaders.map(recurseHeader);
                }
                leafHeaders.push(h);
            };
            recurseHeader(header);
            return leafHeaders;
        },
        getContext: () => ({
            table,
            header: header,
            column,
        }),
    };
    table._features.forEach((feature) => {
        var _a;
        (_a = feature.createHeader) === null || _a === void 0 ? void 0 : _a.call(feature, header, table);
    });
    return header;
}
exports.Headers = {
    createTable: (table) => {
        // Header Groups
        table.getHeaderGroups = (0, utils_1.memo)(() => [
            table.getAllColumns(),
            table.getVisibleLeafColumns(),
            table.getState().columnPinning.left,
            table.getState().columnPinning.right,
        ], (allColumns, leafColumns, left, right) => {
            var _a, _b;
            const leftColumns = (_a = left === null || left === void 0 ? void 0 : left.map((columnId) => leafColumns.find((d) => d.id === columnId)).filter(Boolean)) !== null && _a !== void 0 ? _a : [];
            const rightColumns = (_b = right === null || right === void 0 ? void 0 : right.map((columnId) => leafColumns.find((d) => d.id === columnId)).filter(Boolean)) !== null && _b !== void 0 ? _b : [];
            const centerColumns = leafColumns.filter((column) => !(left === null || left === void 0 ? void 0 : left.includes(column.id)) && !(right === null || right === void 0 ? void 0 : right.includes(column.id)));
            const headerGroups = buildHeaderGroups(allColumns, [...leftColumns, ...centerColumns, ...rightColumns], table);
            return headerGroups;
        }, (0, utils_1.getMemoOptions)(table.options, debug, 'getHeaderGroups'));
        table.getCenterHeaderGroups = (0, utils_1.memo)(() => [
            table.getAllColumns(),
            table.getVisibleLeafColumns(),
            table.getState().columnPinning.left,
            table.getState().columnPinning.right,
        ], (allColumns, leafColumns, left, right) => {
            leafColumns = leafColumns.filter((column) => !(left === null || left === void 0 ? void 0 : left.includes(column.id)) && !(right === null || right === void 0 ? void 0 : right.includes(column.id)));
            return buildHeaderGroups(allColumns, leafColumns, table, 'center');
        }, (0, utils_1.getMemoOptions)(table.options, debug, 'getCenterHeaderGroups'));
        table.getLeftHeaderGroups = (0, utils_1.memo)(() => [
            table.getAllColumns(),
            table.getVisibleLeafColumns(),
            table.getState().columnPinning.left,
        ], (allColumns, leafColumns, left) => {
            var _a;
            const orderedLeafColumns = (_a = left === null || left === void 0 ? void 0 : left.map((columnId) => leafColumns.find((d) => d.id === columnId)).filter(Boolean)) !== null && _a !== void 0 ? _a : [];
            return buildHeaderGroups(allColumns, orderedLeafColumns, table, 'left');
        }, (0, utils_1.getMemoOptions)(table.options, debug, 'getLeftHeaderGroups'));
        table.getRightHeaderGroups = (0, utils_1.memo)(() => [
            table.getAllColumns(),
            table.getVisibleLeafColumns(),
            table.getState().columnPinning.right,
        ], (allColumns, leafColumns, right) => {
            var _a;
            const orderedLeafColumns = (_a = right === null || right === void 0 ? void 0 : right.map((columnId) => leafColumns.find((d) => d.id === columnId)).filter(Boolean)) !== null && _a !== void 0 ? _a : [];
            return buildHeaderGroups(allColumns, orderedLeafColumns, table, 'right');
        }, (0, utils_1.getMemoOptions)(table.options, debug, 'getRightHeaderGroups'));
        // Footer Groups
        table.getFooterGroups = (0, utils_1.memo)(() => [table.getHeaderGroups()], (headerGroups) => {
            return [...headerGroups].reverse();
        }, (0, utils_1.getMemoOptions)(table.options, debug, 'getFooterGroups'));
        table.getLeftFooterGroups = (0, utils_1.memo)(() => [table.getLeftHeaderGroups()], (headerGroups) => {
            return [...headerGroups].reverse();
        }, (0, utils_1.getMemoOptions)(table.options, debug, 'getLeftFooterGroups'));
        table.getCenterFooterGroups = (0, utils_1.memo)(() => [table.getCenterHeaderGroups()], (headerGroups) => {
            return [...headerGroups].reverse();
        }, (0, utils_1.getMemoOptions)(table.options, debug, 'getCenterFooterGroups'));
        table.getRightFooterGroups = (0, utils_1.memo)(() => [table.getRightHeaderGroups()], (headerGroups) => {
            return [...headerGroups].reverse();
        }, (0, utils_1.getMemoOptions)(table.options, debug, 'getRightFooterGroups'));
        // Flat Headers
        table.getFlatHeaders = (0, utils_1.memo)(() => [table.getHeaderGroups()], (headerGroups) => {
            return headerGroups
                .map((headerGroup) => {
                return headerGroup.headers;
            })
                .flat();
        }, (0, utils_1.getMemoOptions)(table.options, debug, 'getFlatHeaders'));
        table.getLeftFlatHeaders = (0, utils_1.memo)(() => [table.getLeftHeaderGroups()], (left) => {
            return left
                .map((headerGroup) => {
                return headerGroup.headers;
            })
                .flat();
        }, (0, utils_1.getMemoOptions)(table.options, debug, 'getLeftFlatHeaders'));
        table.getCenterFlatHeaders = (0, utils_1.memo)(() => [table.getCenterHeaderGroups()], (left) => {
            return left
                .map((headerGroup) => {
                return headerGroup.headers;
            })
                .flat();
        }, (0, utils_1.getMemoOptions)(table.options, debug, 'getCenterFlatHeaders'));
        table.getRightFlatHeaders = (0, utils_1.memo)(() => [table.getRightHeaderGroups()], (left) => {
            return left
                .map((headerGroup) => {
                return headerGroup.headers;
            })
                .flat();
        }, (0, utils_1.getMemoOptions)(table.options, debug, 'getRightFlatHeaders'));
        // Leaf Headers
        table.getCenterLeafHeaders = (0, utils_1.memo)(() => [table.getCenterFlatHeaders()], (flatHeaders) => {
            return flatHeaders.filter((header) => { var _a; return !((_a = header.subHeaders) === null || _a === void 0 ? void 0 : _a.length); });
        }, (0, utils_1.getMemoOptions)(table.options, debug, 'getCenterLeafHeaders'));
        table.getLeftLeafHeaders = (0, utils_1.memo)(() => [table.getLeftFlatHeaders()], (flatHeaders) => {
            return flatHeaders.filter((header) => { var _a; return !((_a = header.subHeaders) === null || _a === void 0 ? void 0 : _a.length); });
        }, (0, utils_1.getMemoOptions)(table.options, debug, 'getLeftLeafHeaders'));
        table.getRightLeafHeaders = (0, utils_1.memo)(() => [table.getRightFlatHeaders()], (flatHeaders) => {
            return flatHeaders.filter((header) => { var _a; return !((_a = header.subHeaders) === null || _a === void 0 ? void 0 : _a.length); });
        }, (0, utils_1.getMemoOptions)(table.options, debug, 'getRightLeafHeaders'));
        table.getLeafHeaders = (0, utils_1.memo)(() => [
            table.getLeftHeaderGroups(),
            table.getCenterHeaderGroups(),
            table.getRightHeaderGroups(),
        ], (left, center, right) => {
            var _a, _b, _c, _d, _e, _f;
            return [
                ...((_b = (_a = left[0]) === null || _a === void 0 ? void 0 : _a.headers) !== null && _b !== void 0 ? _b : []),
                ...((_d = (_c = center[0]) === null || _c === void 0 ? void 0 : _c.headers) !== null && _d !== void 0 ? _d : []),
                ...((_f = (_e = right[0]) === null || _e === void 0 ? void 0 : _e.headers) !== null && _f !== void 0 ? _f : []),
            ]
                .map((header) => {
                return header.getLeafHeaders();
            })
                .flat();
        }, (0, utils_1.getMemoOptions)(table.options, debug, 'getLeafHeaders'));
    },
};
function buildHeaderGroups(allColumns, columnsToGroup, table, headerFamily) {
    // Find the max depth of the columns:
    // build the leaf column row
    // build each buffer row going up
    //    placeholder for non-existent level
    //    real column for existing level
    var _a, _b;
    let maxDepth = 0;
    const findMaxDepth = (columns, depth = 1) => {
        maxDepth = Math.max(maxDepth, depth);
        columns
            .filter((column) => column.getIsVisible())
            .forEach((column) => {
            var _a;
            if ((_a = column.columns) === null || _a === void 0 ? void 0 : _a.length) {
                findMaxDepth(column.columns, depth + 1);
            }
        }, 0);
    };
    findMaxDepth(allColumns);
    const headerGroups = [];
    const createHeaderGroup = (headersToGroup, depth) => {
        // The header group we are creating
        const headerGroup = {
            depth,
            id: [headerFamily, `${depth}`].filter(Boolean).join('_'),
            headers: [],
        };
        // The parent columns we're going to scan next
        const pendingParentHeaders = [];
        // Scan each column for parents
        headersToGroup.forEach((headerToGroup) => {
            // What is the latest (last) parent column?
            const latestPendingParentHeader = [...pendingParentHeaders].reverse()[0];
            const isLeafHeader = headerToGroup.column.depth === headerGroup.depth;
            let column;
            let isPlaceholder = false;
            if (isLeafHeader && headerToGroup.column.parent) {
                // The parent header is new
                column = headerToGroup.column.parent;
            }
            else {
                // The parent header is repeated
                column = headerToGroup.column;
                isPlaceholder = true;
            }
            if (latestPendingParentHeader && (latestPendingParentHeader === null || latestPendingParentHeader === void 0 ? void 0 : latestPendingParentHeader.column) === column) {
                // This column is repeated. Add it as a sub header to the next batch
                latestPendingParentHeader.subHeaders.push(headerToGroup);
            }
            else {
                // This is a new header. Let's create it
                const header = createHeader(table, column, {
                    id: [headerFamily, depth, column.id, headerToGroup === null || headerToGroup === void 0 ? void 0 : headerToGroup.id].filter(Boolean).join('_'),
                    isPlaceholder,
                    placeholderId: isPlaceholder
                        ? `${pendingParentHeaders.filter((d) => d.column === column).length}`
                        : undefined,
                    depth,
                    index: pendingParentHeaders.length,
                });
                // Add the headerToGroup as a subHeader of the new header
                header.subHeaders.push(headerToGroup);
                // Add the new header to the pendingParentHeaders to get grouped
                // in the next batch
                pendingParentHeaders.push(header);
            }
            headerGroup.headers.push(headerToGroup);
            headerToGroup.headerGroup = headerGroup;
        });
        headerGroups.push(headerGroup);
        if (depth > 0) {
            createHeaderGroup(pendingParentHeaders, depth - 1);
        }
    };
    const bottomHeaders = columnsToGroup.map((column, index) => createHeader(table, column, {
        depth: maxDepth,
        index,
    }));
    createHeaderGroup(bottomHeaders, maxDepth - 1);
    headerGroups.reverse();
    // headerGroups = headerGroups.filter(headerGroup => {
    //   return !headerGroup.headers.every(header => header.isPlaceholder)
    // })
    const recurseHeadersForSpans = (headers) => {
        const filteredHeaders = headers.filter((header) => header.column.getIsVisible());
        return filteredHeaders.map((header) => {
            let colSpan = 0;
            let rowSpan = 0;
            let childRowSpans = [0];
            if (header.subHeaders && header.subHeaders.length) {
                childRowSpans = [];
                recurseHeadersForSpans(header.subHeaders).forEach(({ colSpan: childColSpan, rowSpan: childRowSpan }) => {
                    colSpan += childColSpan;
                    childRowSpans.push(childRowSpan);
                });
            }
            else {
                colSpan = 1;
            }
            const minChildRowSpan = Math.min(...childRowSpans);
            rowSpan += minChildRowSpan;
            header.colSpan = colSpan;
            header.rowSpan = rowSpan;
            return { colSpan, rowSpan };
        });
    };
    recurseHeadersForSpans((_b = (_a = headerGroups[0]) === null || _a === void 0 ? void 0 : _a.headers) !== null && _b !== void 0 ? _b : []);
    return headerGroups;
}
