import { getHead, getBody } from './nile-grid.utils';
import { addVariablesValue, computeColumnWidths } from './nile-grid.width';
import { NILE_GRID_HEAD_ITEM } from './types/nile-grid.enums';
/**
 * @param nileGrid Parent NileGrid instance
 * @param slotEl <slot> element hosting head/body
 * @param ensureWidths Function to ensure columnWidths length matches colCount
 * @param stickyLeftIndexes Mutable list of left-sticky column indexes
 * @param stickyRightIndexes Mutable list of right-sticky column indexes
 * @returns void
 */
export const layout = (nileGrid, slotEl, ensureWidths, stickyLeftIndexes, stickyRightIndexes) => {
    const head = getHead(slotEl);
    const body = getBody(slotEl);
    if (!head && !body)
        return;
    let colCount = nileGrid.colCount || 0;
    let headerChanged = false;
    if (nileGrid.needsStructureLayout) {
        headerChanged = true;
        nileGrid.headRows = head ? Array.from(head.querySelectorAll('nile-grid-row')) : [];
        colCount = 0;
        if (nileGrid.headRows.length) {
            colCount = computeHeadLayout(nileGrid, nileGrid.headRows, [], colCount, stickyLeftIndexes, stickyRightIndexes);
        }
        if (body) {
            nileGrid.bodyRows = Array.from(body.querySelectorAll('nile-grid-row'));
            if (colCount) {
                computeBodyLayout(nileGrid, nileGrid.bodyRows, [], colCount, stickyLeftIndexes, stickyRightIndexes);
            }
            else {
                colCount = computeBodyLayout(nileGrid, nileGrid.bodyRows, [], colCount, stickyLeftIndexes, stickyRightIndexes);
            }
        }
        if (nileGrid.stickyLeftIndexes.length || nileGrid.stickyRightIndexes.length) {
            cacheStickyEls(nileGrid);
        }
        else {
            nileGrid.stickyLeftHeadEls = [];
            nileGrid.stickyRightHeadEls = [];
            nileGrid.stickyLeftBodyEls = [];
            nileGrid.stickyRightBodyEls = [];
            nileGrid.shadowLeftVisibleSticky = false;
            nileGrid.shadowRightVisibleSticky = false;
        }
        nileGrid.colCount = colCount;
        nileGrid.needsStructureLayout = false;
        nileGrid.needsSpanLayout = false;
        nileGrid.needsWidthLayout = true;
    }
    else if (nileGrid.needsSpanLayout && body) {
        const bodyRows = nileGrid.bodyRows;
        colCount = nileGrid.colCount || 0;
        if (colCount) {
            computeBodyLayout(nileGrid, bodyRows, [], colCount, stickyLeftIndexes, stickyRightIndexes);
        }
        nileGrid.needsSpanLayout = false;
    }
    if (nileGrid.needsWidthLayout) {
        colCount = nileGrid.colCount || colCount;
        if (!colCount)
            return;
        ensureWidths(colCount);
        computeColumnWidths(nileGrid, colCount, nileGrid.columnWidths, slotEl, nileGrid.lockedWidthColumns, headerChanged);
        addVariablesValue(nileGrid);
        applyColumnsTemplate(nileGrid, nileGrid.columnWidths);
        nileGrid.needsWidthLayout = false;
        if (!nileGrid.isGridReady) {
            nileGrid.isGridReady = true;
            nileGrid.emit('nile-grid-ready');
        }
    }
};
export function cacheStickyEls(nileGrid) {
    const { head, body } = getHeadAndBody(nileGrid);
    nileGrid.stickyLeftHeadEls = head ? Array.from(head.querySelectorAll('nile-grid-head-item[data-sticky="left"]')) : [];
    nileGrid.stickyRightHeadEls = head ? Array.from(head.querySelectorAll('nile-grid-head-item[data-sticky="right"]')) : [];
    nileGrid.stickyLeftBodyEls = body ? Array.from(body.querySelectorAll('nile-grid-cell-item[data-sticky="left"]')) : [];
    nileGrid.stickyRightBodyEls = body ? Array.from(body.querySelectorAll('nile-grid-cell-item[data-sticky="right"]')) : [];
}
/**
 * @param nileGrid The parent NileGrid instance
 * @param rows Array of grid row elements
 * @param carry Array tracking rowspan, colspan continuation
 * @param colCount Total column count
 * @param stickyLeftIndexes List of sticky-left column indexes
 * @param stickyRightIndexes List of sticky-right column indexes
 * @param columnWidths Computed column widths
 * @param opts Options controlling layout behavior
 * @returns Final computed column count
 */
function layoutRows(nileGrid, rows, carry, colCount, stickyLeftIndexes, stickyRightIndexes, opts) {
    rows.forEach((row, rIdx) => {
        if (nileGrid.hoverable)
            row.setAttribute('hover', 'true');
        colCount = layoutSingleRow(nileGrid, row, rIdx, carry, colCount, stickyLeftIndexes, stickyRightIndexes, opts);
    });
    return colCount;
}
/**
 * @param nileGrid The grid element being processed
 * @param row Single row element to layout
 * @param rIdx Row index in the grid
 * @param carry Rowspan tracking array
 * @param colCount Current column count
 * @param stickyLeftIndexes Sticky-left indexes
 * @param stickyRightIndexes Sticky-right indexes
 * @param opts Layout options
 * @returns Updated column count
 */
function layoutSingleRow(nileGrid, row, rIdx, carry, colCount, stickyLeftIndexes, stickyRightIndexes, opts) {
    let c = 1;
    const rStart = rIdx + 1;
    const tag = opts.itemSelector.toLowerCase();
    const items = Array.from(row.children).filter((child) => child.tagName.toLowerCase() === tag);
    for (const el of items) {
        while ((carry[c - 1] || 0) > 0)
            c++;
        ({ c, colCount } = layoutCell(nileGrid, el, c, rStart, carry, colCount, stickyLeftIndexes, stickyRightIndexes, opts));
    }
    decrementCarry(carry);
    return colCount;
}
/**
 * @param nileGrid The parent grid
 * @param el Cell element being laid out
 * @param c Current column index
 * @param rStart Row start index
 * @param carry Rowspan carry array
 * @param colCount Current max column count
 * @param stickyLeftIndexes Left sticky indexes
 * @param stickyRightIndexes Right sticky indexes
 * @param opts Sticky and layout options
 * @returns Updated column index and column count
 */
function layoutCell(nileGrid, el, c, rStart, carry, colCount, stickyLeftIndexes, stickyRightIndexes, opts) {
    const colspan = Math.max(1, +el.getAttribute('colspan') || 1);
    const rowspan = Math.max(1, +el.getAttribute('rowspan') || 1);
    handleRowspan(nileGrid, el, rowspan);
    const cStart = c;
    const cEnd = c + colspan;
    setGridPosition(el, cStart, cEnd, rStart, rowspan);
    if (opts.useStickyAttr || stickyLeftIndexes.length || stickyRightIndexes.length) {
        handleSticky(el, cStart, opts, stickyLeftIndexes, stickyRightIndexes);
    }
    for (let k = 0; k < colspan; k++) {
        carry[c - 1 + k] = Math.max(carry[c - 1 + k] || 0, rowspan);
    }
    return {
        c: cEnd,
        colCount: Math.max(colCount, cEnd - 1),
    };
}
/**
 * @param nileGrid The grid container
 * @param el Element that spans multiple rows
 * @param rowspan Number of rows to span
 */
function handleRowspan(nileGrid, el, rowspan) {
    if (rowspan <= 1)
        return;
    el.style.position = "absolute";
    el.style.zIndex = "1";
    if (el.tagName === NILE_GRID_HEAD_ITEM) {
        if (!nileGrid.headHeight)
            nileGrid.headHeight = el.getBoundingClientRect().height;
        el.style.height = `${(nileGrid.headHeight * rowspan) + (rowspan - 1)}px`;
        el.style.backgroundColor = "var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary))";
        el.style.backgroundColor = "var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary))";
    }
    else {
        if (!nileGrid.cellHeight)
            nileGrid.cellHeight = el.getBoundingClientRect().height;
        el.style.height = `${(nileGrid.cellHeight * rowspan) + (rowspan - 1)}px`;
        el.style.backgroundColor = "var(--nile-colors-white-base, var(--ng-colors-bg-primary))";
        el.style.backgroundColor = "var(--nile-colors-white-base, var(--ng-colors-bg-primary))";
    }
}
/**
 * @param el Grid item element
 * @param cStart Starting column index
 * @param cEnd Ending column index
 * @param rStart Starting row index
 * @param rowspan Row span value
 */
function setGridPosition(el, cStart, cEnd, rStart, rowspan) {
    const rEnd = rStart + rowspan;
    el.style.gridColumn = `${cStart} / ${cEnd}`;
    el.style.gridRow = `${rStart} / ${rEnd}`;
    el.dataset.cStart = String(cStart);
}
/**
 * @param el Grid item element
 * @param cStart Column start index
 * @param opts Sticky configuration options
 * @param stickyLeftIndexes Left sticky indexes
 * @param stickyRightIndexes Right sticky indexes
 */
function handleSticky(el, cStart, opts, stickyLeftIndexes, stickyRightIndexes) {
    if (opts.useStickyAttr && el.getAttribute('sticky') === "left" && !stickyLeftIndexes.includes(cStart - 1)) {
        stickyLeftIndexes.push(cStart - 1);
    }
    if (opts.useStickyAttr && el.getAttribute('sticky') === "right" && !stickyRightIndexes.includes(cStart - 1)) {
        stickyRightIndexes.push(cStart - 1);
    }
    applySticky(el, cStart - 1, stickyLeftIndexes, stickyRightIndexes);
}
/**
 * @param carry Rowspan tracking array
 */
function decrementCarry(carry) {
    for (let i = 0; i < carry.length; i++) {
        if (carry[i] > 0) {
            carry[i]--;
        }
    }
}
/**
 * @param el Target grid cell element
 * @param colIndex Column index
 * @param stickyLeftIndexes Sticky-left indexes
 * @param stickyRightIndexes Sticky-right indexes
 */
function applySticky(el, colIndex, stickyLeftIndexes, stickyRightIndexes) {
    const onLeft = stickyLeftIndexes.includes(colIndex);
    const onRight = stickyRightIndexes.includes(colIndex);
    if (!onLeft && !onRight)
        return;
    prepareStickyElement(el);
    if (onLeft && !onRight) {
        applyLeftSticky(el, colIndex);
    }
    else {
        applyRightSticky(el, colIndex);
    }
}
/**
 * @param el Grid item element to prepare for sticky positioning
 */
function prepareStickyElement(el) {
    el.style.position = 'sticky';
    el.style.left = '';
    el.style.right = '';
    el.removeAttribute('data-sticky');
}
/**
 * @param el Target element
 * @param colIndex Column index
 * @param stickyLeftIndexes Sticky-left column indexes
 */
function applyLeftSticky(el, colIndex) {
    el.setAttribute('data-sticky', 'left');
    if (el.tagName === NILE_GRID_HEAD_ITEM) {
        el.style.backgroundColor = "var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary))";
    }
    else {
        el.style.backgroundColor = 'var(--nile-colors-white-base, var(--ng-colors-bg-primary))';
    }
    el.style.zIndex = '1';
    el.style.left = `var(--sticky-left-${colIndex})`;
}
/**
 * @param el Target element
 * @param colIndex Column index
 * @param stickyRightIndexes Sticky-right indexes
 */
function applyRightSticky(el, colIndex) {
    el.setAttribute('data-sticky', 'right');
    if (el.tagName === NILE_GRID_HEAD_ITEM) {
        el.style.backgroundColor = "var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary))";
    }
    else {
        el.style.backgroundColor = 'var(--nile-colors-white-base, var(--ng-colors-bg-primary))';
    }
    el.style.zIndex = '1';
    el.style.right = `var(--sticky-right-${colIndex})`;
}
/**
 * @param nileGrid Grid instance to sync shadows for
 */
export function syncStickyShadows(nileGrid) {
    if (!nileGrid.stickyLeftIndexes.length && !nileGrid.stickyRightIndexes.length) {
        return;
    }
    const { showRightOnLeftStickies, showLeftOnRightStickies } = getShadowStates(nileGrid);
    if (nileGrid.shadowLeftVisibleSticky === showRightOnLeftStickies &&
        nileGrid.shadowRightVisibleSticky === showLeftOnRightStickies) {
        return;
    }
    nileGrid.shadowLeftVisibleSticky = showRightOnLeftStickies;
    nileGrid.shadowRightVisibleSticky = showLeftOnRightStickies;
    for (const el of nileGrid.stickyLeftHeadEls) {
        el.classList.toggle('shadow-left', showRightOnLeftStickies);
    }
    for (const el of nileGrid.stickyLeftBodyEls) {
        el.classList.toggle('shadow-left', showRightOnLeftStickies);
    }
    for (const el of nileGrid.stickyRightHeadEls) {
        el.classList.toggle('shadow-right', showLeftOnRightStickies);
    }
    for (const el of nileGrid.stickyRightBodyEls) {
        el.classList.toggle('shadow-right', showLeftOnRightStickies);
    }
}
/**
 * @param nileGrid Grid instance
 * @returns Object containing booleans for left/right shadow visibility
 */
function getShadowStates(nileGrid) {
    const max = Math.max(0, nileGrid.scrollWidth - nileGrid.clientWidth);
    return {
        showRightOnLeftStickies: nileGrid.scrollLeft > 0,
        showLeftOnRightStickies: nileGrid.scrollLeft < max
    };
}
/** computeHeadLayout
 * @param nileGrid Grid instance
 * @param headRows Header rows (<nile-grid-row>[])
 * @param carryHead Rowspan carry tracking array
 * @param colCount Current column count
 * @param stickyLeftIndexes Left-sticky indexes
 * @param stickyRightIndexes Right-sticky indexes
 * @returns Final column count after header layout
 */
function computeHeadLayout(nileGrid, headRows, carryHead, colCount, stickyLeftIndexes, stickyRightIndexes) {
    return layoutRows(nileGrid, headRows, carryHead, colCount, stickyLeftIndexes, stickyRightIndexes, {
        itemSelector: 'nile-grid-head-item',
        useStickyAttr: true,
        handleRowBg: false,
    });
}
/** computeBodyLayout
 * @param nileGrid Grid instance
 * @param bodyRows Body rows (<nile-grid-row>[])
 * @param carryBody Rowspan carry tracking array
 * @param colCount Current column count
 * @param stickyLeftIndexes Left-sticky indexes
 * @param stickyRightIndexes Right-sticky indexes
 * @returns Final column count after body layout
 */
function computeBodyLayout(nileGrid, bodyRows, carryBody, colCount, stickyLeftIndexes, stickyRightIndexes) {
    return layoutRows(nileGrid, bodyRows, carryBody, colCount, stickyLeftIndexes, stickyRightIndexes, {
        itemSelector: 'nile-grid-cell-item',
        useStickyAttr: false,
        handleRowBg: true,
    });
}
/**
 * @param nileGrid Grid container element
 * @param colCount Total number of columns
 * @param columnWidths Column width array
 */
export function applyColumnsTemplate(nileGrid, columnWidths) {
    const cols = columnWidths
        .map((w, idx) => {
        if (idx === columnWidths.length - 1) {
            return `minmax(${Math.round(w)}px, auto)`;
        }
        return `${Math.max(0, Math.round(w))}px`;
    })
        .join(' ');
    nileGrid.style.setProperty('--nile-grid-columns', cols);
}
/**
 * @param nileGrid Grid instance
 * @returns Head and body elements from the grid slot
 */
function getHeadAndBody(nileGrid) {
    if (!nileGrid.shadowRoot)
        return { head: undefined, body: undefined };
    const assigned = nileGrid.slotEl.assignedElements();
    const find = (tag) => assigned.find(n => n.tagName.toLowerCase() === tag);
    return { head: find('nile-grid-head'), body: find('nile-grid-body') };
}
//# sourceMappingURL=nile-grid.layout.js.map