import { applyColumnsTemplate } from './nile-grid.layout';
import { getHead } from './nile-grid.utils';
/** computeColumnWidths
 * @param nileGrid Grid instance (reads minColumnWidth, columnWidths)
 * @param colCount Total columns
 * @param columnWidths Mutable widths array to fill/update
 * @param slotEl Slot element (to read head for desired widths)
 * @returns void
 */
export function computeColumnWidths(nileGrid, colCount, columnWidths, slotEl, lockedWidthColumns, headerChanged) {
    const minCol = nileGrid.minColumnWidth;
    const containerWidth = getContainerWidth(nileGrid);
    const desired = readDesiredFromHead(slotEl, colCount, containerWidth, nileGrid, lockedWidthColumns, headerChanged, columnWidths);
    const isSeeded = seedWidthsFromDesired(desired, columnWidths, minCol);
    distributeFlex(columnWidths, isSeeded, containerWidth, minCol);
    if (!nileGrid.enableScroll) {
        normalizeToContainer(columnWidths, containerWidth, minCol);
    }
    clampAndRound(columnWidths, minCol);
    applyColumnsTemplate(nileGrid, columnWidths);
}
/** getContainerWidth
 * @param nileGrid Grid root element
 * @returns Numeric container width in pixels (accounting for padding/offset)
 */
function getContainerWidth(nileGrid) {
    return Math.max(0, (nileGrid.getBoundingClientRect().width || 0) - 4);
}
/**
 * @param slotEl The slot element containing head rows
 * @param colCount Number of columns
 * @param containerWidth Width of the parent container
 * @param prevWidths Previously computed column widths
 * @returns Array of desired column widths
 */
function readDesiredFromHead(slotEl, colCount, containerWidth, nileGrid, lockedWidthColumns, headerChanged, prevWidths) {
    const desired = Array(colCount).fill(null);
    if (headerChanged) {
        const head = getHead(slotEl);
        if (head)
            fillDesiredFromHeadRows(head, desired, colCount, containerWidth, lockedWidthColumns);
    }
    const hasPrevWidths = !!prevWidths?.length && prevWidths.some(w => w > 0);
    if (hasPrevWidths && prevWidths?.length === colCount) {
        fillFromPrevWidths(desired, prevWidths, nileGrid);
    }
    return desired;
}
/**
 * @param head Head element containing grid header rows
 * @param desired Desired widths array to fill
 * @param colCount Column count
 * @param containerWidth Grid container width
 * @param fixedWidthColumns Fixed width columns
 */
function fillDesiredFromHeadRows(head, desired, colCount, containerWidth, lockedWidthColumns) {
    const headRows = [...head.children].filter(el => el.tagName.toLowerCase() === 'nile-grid-row');
    for (const row of headRows) {
        fillFromHeadRow(row, desired, colCount, containerWidth, lockedWidthColumns);
    }
}
/**
 * @param row A single header row element
 * @param desired Desired widths array
 * @param colCount Total number of columns
 * @param containerWidth Container width
 * @param lockedWidthColumns Locked width columns
 * @returns void
 */
function fillFromHeadRow(row, desired, colCount, containerWidth, lockedWidthColumns) {
    const items = [...row.children].filter(el => el.tagName.toLowerCase() === 'nile-grid-head-item');
    const parsed = items
        .map(h => parseHeadItemWidth(h, containerWidth, lockedWidthColumns))
        .filter(x => x.cw != null);
    parsed.sort((a, b) => a.colspan - b.colspan);
    parsed.forEach(({ cStart, colspan, cw }) => {
        assignWidth(desired, cStart, colspan, cw, colCount);
    });
}
/**
 * @param h Header item element
 * @param containerWidth Width of parent container
 * @param lockedWidthColumns Locked width columns
 * @returns Parsed object containing start, colspan, and computed width
 */
function parseHeadItemWidth(h, containerWidth, lockedWidthColumns) {
    const cStart = Number(h.dataset?.cStart) || 1;
    const colspan = Math.max(1, Number(h.getAttribute('colspan')) || 1);
    const raw = h.width ?? h.getAttribute('width') ?? null;
    if (h.hasAttribute('lockWidth')) {
        lockedWidthColumns[cStart - 1] = true;
    }
    const cw = parseCustomWidth(raw, containerWidth);
    return { cStart, colspan, cw };
}
/**
 * @param desired Array of desired widths
 * @param cStart Start column index
 * @param colspan Column span value
 * @param cw Computed width value
 * @param colCount Total column count
 */
function assignWidth(desired, cStart, colspan, cw, colCount) {
    const perCol = Math.round(cw / colspan);
    for (let k = 0; k < colspan; k++) {
        const idx = cStart - 1 + k;
        if (idx >= 0 && idx < colCount && desired[idx] == null) {
            desired[idx] = perCol;
        }
    }
}
/**
 * @param desired Desired widths array
 * @param prevWidths Previous column widths
 */
function fillFromPrevWidths(desired, prevWidths, nileGrid) {
    for (let i = 0; i < prevWidths.length; i++) {
        if (desired[i] == null && Number.isFinite(prevWidths[i])) {
            desired[i] = Math.max(nileGrid.minColumnWidth, Math.round(prevWidths[i]));
        }
    }
}
/** parseCustomWidth
 * @param raw User-specified width (number | string like "120px" or "20%")
 * @param containerWidth Container width for % resolution
 * @returns Width in pixels or null if invalid
 */
function parseCustomWidth(raw, containerWidth) {
    if (raw == null)
        return null;
    if (typeof raw === 'number' && Number.isFinite(raw))
        return raw;
    const s = String(raw).trim().toLowerCase();
    if (!s)
        return null;
    if (s.endsWith('px')) {
        const v = parseFloat(s);
        return Number.isFinite(v) ? v : null;
    }
    if (s.endsWith('%')) {
        const v = parseFloat(s);
        return Number.isFinite(v) ? (v / 100) * containerWidth : null;
    }
    const v = parseFloat(s);
    return Number.isFinite(v) ? v : null;
}
/** seedWidthsFromDesired
 * @param desired Array of desired widths (px) or nulls
 * @param columnWidths Output array to seed (mutated)
 * @param minCol Minimum per-column width
 * @returns Boolean[] flags indicating which columns were seeded
 */
function seedWidthsFromDesired(desired, columnWidths, minCol) {
    const isSeeded = Array(desired.length).fill(false);
    for (let i = 0; i < desired.length; i++) {
        const w = desired[i] ?? null;
        columnWidths[i] =
            w != null && !Number.isNaN(w) ? Math.max(minCol, Number(w)) : 0;
        isSeeded[i] = w != null && !Number.isNaN(w);
    }
    return isSeeded;
}
/** distributeFlex
 * @param columnWidths Mutable widths array
 * @param isSeeded Boolean[] indicating fixed/seeded columns
 * @param containerWidth Total width to fill
 * @param minCol Minimum width per flexible column
 * @returns void
 */
function distributeFlex(columnWidths, isSeeded, containerWidth, minCol) {
    const sumSeed = columnWidths.reduce((a, b) => a + b, 0);
    const remaining = containerWidth - sumSeed;
    const flexIdxs = Array.from({ length: columnWidths.length }, (_, i) => i).filter(i => !isSeeded[i]);
    if (flexIdxs.length === 0)
        return;
    const each = remaining / flexIdxs.length;
    for (const i of flexIdxs)
        columnWidths[i] = Math.max(minCol, each);
}
// Normalization (container fitting): adjust total column widths to match the container width proportionally.
/**
 * @param columnWidths Array of current column widths
 * @param containerWidth Total grid container width
 * @param minCol Minimum allowed column width
 */
function normalizeToContainer(columnWidths, containerWidth, minCol) {
    let total = sumWidths(columnWidths);
    if (total < containerWidth) {
        expandWidths(columnWidths, containerWidth, total);
    }
    else if (total > containerWidth) {
        shrinkWidths(columnWidths, containerWidth, total, minCol);
    }
}
/**
 * @param widths Array of column widths
 * @returns Sum of all column widths
 */
function sumWidths(widths) {
    return widths.reduce((a, b) => a + b, 0);
}
/**
 * @param columnWidths Current column widths
 * @param containerWidth Container total width
 * @param total Current total width
 */
function expandWidths(columnWidths, containerWidth, total) {
    if (!columnWidths.length)
        return;
    const add = containerWidth - total;
    const weight = total || 1;
    for (let i = 0; i < columnWidths.length; i++) {
        columnWidths[i] += add * (columnWidths[i] / weight);
    }
}
/**
 * @param columnWidths Current widths
 * @param containerWidth Container width
 * @param total Current total
 * @param minCol Minimum width constraint
 */
function shrinkWidths(columnWidths, containerWidth, total, minCol) {
    let need = total - containerWidth;
    for (let iter = 0; iter < 8 && need > 0.5; iter++) {
        const { candidates, weightSum } = collectReducible(columnWidths, minCol);
        if (!candidates.length || weightSum <= 0)
            break;
        const reduced = reduceWidths(columnWidths, candidates, weightSum, need, minCol);
        need -= reduced;
        if (reduced < 0.5)
            break;
    }
}
/**
 * @param columnWidths Current widths
 * @param minCol Minimum column width
 * @returns Reducible column indexes and weight sum
 */
function collectReducible(columnWidths, minCol) {
    const candidates = [];
    let weightSum = 0;
    for (let i = 0; i < columnWidths.length; i++) {
        const reducible = Math.max(0, columnWidths[i] - minCol);
        if (reducible > 0) {
            candidates.push(i);
            weightSum += columnWidths[i];
        }
    }
    return { candidates, weightSum };
}
/**
 * @param columnWidths Current widths array
 * @param candidates Reducible column indexes
 * @param weightSum Sum of reducible widths
 * @param need Total amount to reduce
 * @param minCol Minimum width
 * @returns Total reduced width
 */
function reduceWidths(columnWidths, candidates, weightSum, need, minCol) {
    let reduced = 0;
    for (const i of candidates) {
        const reducible = Math.max(0, columnWidths[i] - minCol);
        const share = columnWidths[i] / weightSum;
        const cut = Math.min(reducible, need * share);
        columnWidths[i] -= cut;
        reduced += cut;
    }
    return reduced;
}
function clampAndRound(columnWidths, minCol) {
    for (let i = 0; i < columnWidths.length; i++) {
        columnWidths[i] = Math.max(minCol, Math.round(columnWidths[i]));
    }
}
/**
 * @param columnWidths Current widths array
 * @param minCol Minimum column width
 * @returns Total reducible pixels
 */
export function totalReducibleRight(w, start, floor, fixed) {
    let sum = 0;
    for (let i = start; i < w.length; i++) {
        if (fixed?.[i])
            continue;
        sum += Math.max(0, w[i] - floor);
    }
    return sum;
}
/**
 * @param columnWidths Current widths
 * @param amount Pixels to remove from rightmost columns
 */
export function takeFromRight(widths, start, needInit, floor, fixed) {
    let need = needInit;
    for (let i = start; i < widths.length && need > 0; i++) {
        if (fixed?.[i])
            continue;
        const room = widths[i] - floor;
        if (room <= 0)
            continue;
        const cut = Math.min(room, need);
        widths[i] -= cut;
        need -= cut;
    }
    return need;
}
/**
 * @param columnWidths Current widths
 * @param amount Pixels to add to rightmost columns
 */
export function giveToRight(widths, start, giveInit) {
    let give = giveInit;
    if (start < widths.length && give > 0) {
        widths[start] += give;
        give = 0;
    }
    return give;
}
export function addVariablesValue(nileGrid) {
    const columnWidths = nileGrid.columnWidths;
    const leftStickyColumns = nileGrid.stickyLeftIndexes;
    const rightStickyColumns = nileGrid.stickyRightIndexes;
    const prefixColumns = [];
    const root = nileGrid;
    // --- LEFT STICKY ---
    let totalLeft = 0;
    for (let i = 0; i < leftStickyColumns.length; i++) {
        const colIndex = leftStickyColumns[i];
        prefixColumns.push({ colNumber: colIndex, left: totalLeft, right: 0 });
        root.style.setProperty(`--sticky-left-${colIndex}`, `${totalLeft}px`);
        totalLeft += columnWidths[colIndex];
    }
    // --- RIGHT STICKY ---
    let totalRight = 0;
    for (let i = rightStickyColumns.length - 1; i >= 0; i--) {
        const colIndex = rightStickyColumns[i];
        prefixColumns.push({ colNumber: colIndex, left: 0, right: totalRight });
        root.style.setProperty(`--sticky-right-${colIndex}`, `${totalRight}px`);
        totalRight += columnWidths[colIndex];
    }
    nileGrid.prefixSumColumnsWidth = prefixColumns;
}
//# sourceMappingURL=nile-grid.width.js.map