import { __decorate } from "tslib";
import { observable } from '@ni/fast-element';
/**
 * This class manages the layout of columns within a Table.
 * @interal
 */
export class TableLayoutManager {
    constructor(table) {
        this.table = table;
        this.isColumnBeingSized = false;
        this.visibleColumns = [];
        this.currentTotalDelta = 0;
        this.dragStart = 0;
        this.initialColumnWidths = [];
        this.onDividerPointerMove = (event) => {
            for (let i = 0; i < this.visibleColumns.length; i++) {
                this.visibleColumns[i].columnInternals.currentPixelWidth = this.initialColumnWidths[i]?.initialPixelWidth;
            }
            this.currentTotalDelta = this.getAllowedSizeDelta(event.clientX - this.dragStart);
            this.performCascadeSizeLeft(this.leftColumnIndex, this.currentTotalDelta);
            this.performCascadeSizeRight(this.rightColumnIndex, this.currentTotalDelta);
            const totalColumnWidthDelta = this.getTotalColumnFixedWidth() - this.initialColumnTotalWidth;
            if (totalColumnWidthDelta > 0) {
                this.table.tableScrollableMinWidth = this.initialTableScrollableWidth + totalColumnWidthDelta;
            }
            else {
                this.table.tableScrollableMinWidth = this.initialTableScrollableMinWidth;
            }
        };
        this.onDividerPointerUp = () => {
            this.activeColumnDividerElement.removeEventListener('pointermove', this.onDividerPointerMove);
            this.activeColumnDividerElement.removeEventListener('pointerup', this.onDividerPointerUp);
            this.resetGridSizedColumns();
            this.isColumnBeingSized = false;
            this.activeColumnIndex = undefined;
            this.activeColumnDivider = undefined;
            this.activeColumnDividerElement = undefined;
            this.visibleColumns = [];
        };
    }
    getGridTemplateColumns() {
        return this.getVisibleColumns()
            .map(column => {
            const { minPixelWidth, currentPixelWidth, currentFractionalWidth } = column.columnInternals;
            if (currentPixelWidth !== undefined) {
                const coercedPixelWidth = Math.max(minPixelWidth, currentPixelWidth);
                return `${coercedPixelWidth}px`;
            }
            return `minmax(${minPixelWidth}px, ${currentFractionalWidth}fr)`;
        })
            .join(' ');
    }
    /**
     * Sets up state related to interactively sizing a column.
     * @param activeColumnDividerElement The divider element that was clicked on
     * @param pointerId The pointerId of the pointer that started the drag
     * @param dragStart The x-position from which a column size was started
     * @param activeColumnDivider The 1-based index of the divider that was clicked on
     */
    beginColumnInteractiveSize(activeColumnDividerElement, pointerId, dragStart, activeColumnDivider) {
        this.activeColumnDivider = activeColumnDivider;
        this.activeColumnDividerElement = activeColumnDividerElement;
        this.leftColumnIndex = this.getLeftColumnIndexFromDivider(this.activeColumnDivider);
        this.rightColumnIndex = this.leftColumnIndex + 1;
        this.activeColumnIndex = this.leftColumnIndex + (this.activeColumnDivider % 2);
        this.dragStart = dragStart;
        this.currentTotalDelta = 0;
        this.visibleColumns = this.getVisibleColumns();
        this.setColumnsToFixedSize();
        this.initialTableScrollableWidth = this.table.viewport.scrollWidth;
        this.initialTableScrollableMinWidth = this.table.tableScrollableMinWidth;
        this.initialColumnTotalWidth = this.getTotalColumnFixedWidth();
        this.isColumnBeingSized = true;
        // pointerId of -1 indicates source was synthetic PointerEvent: https://w3c.github.io/pointerevents/#dom-pointerevent-pointerid
        if (pointerId !== -1) {
            activeColumnDividerElement.setPointerCapture(pointerId);
        }
        activeColumnDividerElement.addEventListener('pointermove', this.onDividerPointerMove);
        activeColumnDividerElement.addEventListener('pointerup', this.onDividerPointerUp);
    }
    /**
     * Determines if the specified column or any columns to the left are resizable.
     */
    hasResizableColumnToLeft(columnIndex) {
        return this.getFirstLeftResizableColumnIndex(columnIndex) !== -1;
    }
    /**
     * Determines if the specified column or any columns to the right are resizable.
     */
    hasResizableColumnToRight(columnIndex) {
        return this.getFirstRightResizableColumnIndex(columnIndex) !== -1;
    }
    getTotalColumnFixedWidth() {
        let totalColumnFixedWidth = 0;
        for (const column of this.visibleColumns) {
            totalColumnFixedWidth
                += column.columnInternals.currentPixelWidth ?? 0;
        }
        return totalColumnFixedWidth;
    }
    setColumnsToFixedSize() {
        this.cacheGridSizedColumns();
        const headers = this.table.getHeaderContainerElements();
        for (let i = 0; i < headers.length; i++) {
            this.visibleColumns[i].columnInternals.currentPixelWidth = headers[i].getBoundingClientRect().width;
        }
        this.cacheColumnInitialPixelWidths();
    }
    getAllowedSizeDelta(requestedResizeAmount) {
        let availableSpace = 0;
        if (requestedResizeAmount > 0) {
            // size right
            return this.hasResizableColumnToLeft(this.leftColumnIndex)
                ? requestedResizeAmount
                : 0;
        }
        // size left
        if (!this.hasResizableColumnToRight(this.rightColumnIndex)) {
            return 0;
        }
        for (let i = this.leftColumnIndex; i >= 0; i--) {
            const columnInitialWidths = this.initialColumnWidths[i];
            const column = this.visibleColumns[i];
            if (!column.columnInternals.resizingDisabled) {
                availableSpace
                    += columnInitialWidths.initialPixelWidth
                        - column.columnInternals.minPixelWidth;
            }
        }
        return Math.max(requestedResizeAmount, -availableSpace);
    }
    /**
     * Gets the index of the first resizable column starting with
     * `columnIndex` and moving to the left. If no resizable column
     * is found, returns -1.
     */
    getFirstLeftResizableColumnIndex(columnIndex) {
        const visibleColumns = this.visibleColumns.length === 0
            ? this.getVisibleColumns()
            : this.visibleColumns;
        for (let i = columnIndex; i >= 0; i--) {
            const column = visibleColumns[i];
            if (!column) {
                return -1;
            }
            if (!column.columnInternals.resizingDisabled) {
                return i;
            }
        }
        return -1;
    }
    /**
     * Gets the index of the first resizable column starting with
     * `columnIndex` and moving to the right. If no resizable column
     * is found, returns -1.
     */
    getFirstRightResizableColumnIndex(columnIndex) {
        const visibleColumns = this.visibleColumns.length === 0
            ? this.getVisibleColumns()
            : this.visibleColumns;
        for (let i = columnIndex; i < visibleColumns.length; i++) {
            const column = visibleColumns[i];
            if (!column) {
                return -1;
            }
            if (!column.columnInternals.resizingDisabled) {
                return i;
            }
        }
        return -1;
    }
    performCascadeSizeLeft(leftColumnIndex, delta) {
        const firstLeftResizableColumn = this.getFirstLeftResizableColumnIndex(leftColumnIndex);
        if (firstLeftResizableColumn === -1) {
            return;
        }
        let currentDelta = delta;
        const leftColumn = this.visibleColumns[firstLeftResizableColumn];
        const leftColumnInitialWidths = this.initialColumnWidths[firstLeftResizableColumn];
        const allowedDelta = delta < 0
            ? Math.max(leftColumn.columnInternals.minPixelWidth
                - leftColumnInitialWidths.initialPixelWidth, currentDelta)
            : delta;
        const actualDelta = allowedDelta;
        leftColumn.columnInternals.currentPixelWidth += actualDelta;
        if (actualDelta > currentDelta
            && firstLeftResizableColumn > 0
            && delta < 0) {
            currentDelta -= allowedDelta;
            this.performCascadeSizeLeft(firstLeftResizableColumn - 1, currentDelta);
        }
    }
    performCascadeSizeRight(rightColumnIndex, delta) {
        const firstRightResizableColumn = this.getFirstRightResizableColumnIndex(rightColumnIndex);
        if (firstRightResizableColumn === -1) {
            return;
        }
        let currentDelta = delta;
        const rightColumn = this.visibleColumns[firstRightResizableColumn];
        const rightColumnInitialWidths = this.initialColumnWidths[firstRightResizableColumn];
        let allowedDelta;
        if (rightColumn.columnInternals.resizingDisabled) {
            allowedDelta = 0;
        }
        else if (delta > 0) {
            allowedDelta = Math.min(rightColumnInitialWidths.initialPixelWidth
                - rightColumn.columnInternals.minPixelWidth, currentDelta);
        }
        else {
            allowedDelta = delta;
        }
        const actualDelta = allowedDelta;
        rightColumn.columnInternals.currentPixelWidth -= actualDelta;
        if (actualDelta < currentDelta
            && firstRightResizableColumn < this.visibleColumns.length - 1
            && delta > 0) {
            currentDelta -= allowedDelta;
            this.performCascadeSizeRight(firstRightResizableColumn + 1, currentDelta);
        }
    }
    cacheGridSizedColumns() {
        this.gridSizedColumns = [];
        for (const column of this.visibleColumns) {
            if (column.columnInternals.currentPixelWidth === undefined) {
                this.gridSizedColumns.push(column);
            }
        }
    }
    cacheColumnInitialPixelWidths() {
        this.initialColumnWidths = [];
        for (const column of this.visibleColumns) {
            this.initialColumnWidths.push({
                initalColumnFractionalWidth: column.columnInternals.currentFractionalWidth,
                initialPixelWidth: column.columnInternals.currentPixelWidth
            });
        }
    }
    resetGridSizedColumns() {
        if (!this.gridSizedColumns || this.gridSizedColumns.length === 0) {
            return;
        }
        const gridSizeColumnTotal = this.gridSizedColumns.reduce((sum, col) => sum + col.columnInternals.currentPixelWidth, 0);
        const gridSizeColumnAverage = gridSizeColumnTotal / this.gridSizedColumns.length;
        let gridColumnIndex = 0;
        for (let i = 0; i < this.visibleColumns.length
            && gridColumnIndex < this.gridSizedColumns.length; i++) {
            const column = this.visibleColumns[i];
            if (column === this.gridSizedColumns[gridColumnIndex]) {
                gridColumnIndex += 1;
                column.columnInternals.currentFractionalWidth = column.columnInternals.currentPixelWidth
                    / gridSizeColumnAverage;
                column.columnInternals.currentPixelWidth = undefined;
            }
        }
    }
    getVisibleColumns() {
        return this.table.columns.filter(column => !column.columnHidden);
    }
    getLeftColumnIndexFromDivider(dividerIndex) {
        return Math.floor(dividerIndex / 2);
    }
}
__decorate([
    observable
], TableLayoutManager.prototype, "isColumnBeingSized", void 0);
__decorate([
    observable
], TableLayoutManager.prototype, "activeColumnIndex", void 0);
__decorate([
    observable
], TableLayoutManager.prototype, "activeColumnDivider", void 0);
//# sourceMappingURL=table-layout-manager.js.map