export const getId = (idPrefix = '', name = '') => idPrefix ? idPrefix + name : undefined;
export const calculateWidth = (totalPages) => `${totalPages.toString().length}em`;
export const measureAndSetRootMinWidth = (compNode, paginationMode, idPrefix = '') => {
    compNode.style.minWidth = '';
    compNode.style.minHeight = '';
    const getById = (id) => compNode.querySelector(`#${getId(idPrefix, id)}`);
    const getMinWidth = (elmnt) => elmnt ? parseInt(window.getComputedStyle(elmnt).minWidth, 10) : 0;
    const getWidthWithMargins = (element) => {
        if (!element) {
            return 0;
        }
        const style = window.getComputedStyle(element);
        return (parseInt(style.marginRight, 10) +
            element.offsetWidth +
            parseInt(style.marginLeft, 10));
    };
    const getHeightWithMargins = (element) => {
        if (!element) {
            return 0;
        }
        const style = window.getComputedStyle(element);
        return (parseInt(style.marginBottom, 10) +
            element.offsetHeight +
            parseInt(style.marginTop, 10));
    };
    const navButtonsMinWidth = getWidthWithMargins(getById('navButtonNext')) +
        getWidthWithMargins(getById('navButtonPrevious')) +
        getWidthWithMargins(getById('navButtonFirst')) +
        getWidthWithMargins(getById('navButtonLast'));
    let selectionMinWidth = 0;
    let minHeight = 0;
    if (paginationMode === 'pages') {
        // means we're in "pages" pagination mode
        selectionMinWidth = getMinWidth(getById('pageStrip'));
        minHeight = Math.max(getHeightWithMargins(getById('pageStrip')), getHeightWithMargins(getById('navButtonNext')));
    }
    else if (paginationMode === 'input') {
        // means we're in "input" pagination mode
        selectionMinWidth =
            getWidthWithMargins(getById('totalPages')) +
                getWidthWithMargins(getById('slash')) +
                getWidthWithMargins(getById('pageInput'));
        minHeight = Math.max(getHeightWithMargins(getById('pageInput')), getHeightWithMargins(getById('navButtonNext')));
    }
    else if (paginationMode === 'compact') {
        // means we're in "compact" pagination mode
        selectionMinWidth =
            getWidthWithMargins(getById('totalPages')) +
                getWidthWithMargins(getById('slash')) +
                getWidthWithMargins(getById('currentPage'));
        minHeight = Math.max(getHeightWithMargins(getById('currentPage')), getHeightWithMargins(getById('navButtonNext')));
    }
    compNode.style.minWidth = navButtonsMinWidth + selectionMinWidth + 'px';
    compNode.style.minHeight = minHeight + 'px';
};
export function createStaticLayout({ totalPages, currentPage, maxPagesToShow, showFirstPage, showLastPage, }) {
    return createLayout({
        totalPages,
        currentPage,
        lowerBound: 1,
        upperBound: totalPages,
        pageRangeCost: (a, b) => b - a + 1,
        showFirstPage,
        showLastPage,
        rewindToFirstCost: 2,
        rewindToLastCost: 2,
        budget: maxPagesToShow,
    });
}
function rangeToPreRenderForResponsiveLayout(totalPages, currentPage, maxPagesToShow) {
    return [
        Math.max(currentPage - maxPagesToShow, 1),
        Math.min(currentPage + maxPagesToShow, totalPages),
    ];
}
export function createResponsiveLayoutTemplate({ totalPages, currentPage, maxPagesToShow, }) {
    const [lowerBound, upperBound] = rangeToPreRenderForResponsiveLayout(totalPages, currentPage, maxPagesToShow);
    return [1, 0, ...closedRange(lowerBound, upperBound), 0, totalPages];
}
// Takes a container with children rendered using createResponsiveLayoutTemplate,
// measures the children, and decides how many can be shown without overflowing the container.
// For measurements to work correctly the pages must not have any dynamic spacing between them
// such as justify-content: space-evenly, but they can have static spacing such as margins.
// As long as we're using flexbox with centered pages we don't need to worry about the outer
// margins of the first and last page, they will be trimmed by flexbox automatically.
// maxPagesToShow is not really taken into account, it's used to derive the range of pages
// that was pre-rendered by createResponsiveLayoutTemplate().
export function createResponsiveLayout({ container, totalPages, currentPage, maxPagesToShow, showFirstPage, showLastPage, }) {
    const children = Array.from(container.children);
    const pages = children.slice(2, -2);
    const containerWidth = container.getBoundingClientRect().width;
    const firstRect = children[0].getBoundingClientRect();
    const lastRect = children[children.length - 1].getBoundingClientRect();
    const lowerRect = pages[0].getBoundingClientRect();
    const upperRect = pages[pages.length - 1].getBoundingClientRect();
    const rewindToFirstCost = mergeBoundingRects(firstRect, lowerRect).width - lowerRect.width;
    const rewindToLastCost = mergeBoundingRects(lastRect, upperRect).width - upperRect.width;
    const [lowerBound, upperBound] = rangeToPreRenderForResponsiveLayout(totalPages, currentPage, maxPagesToShow);
    const pageRangeCost = (a, b) => {
        const aRect = pages[a - lowerBound].getBoundingClientRect();
        const bRect = pages[b - lowerBound].getBoundingClientRect();
        return mergeBoundingRects(aRect, bRect).width;
    };
    return createLayout({
        totalPages,
        currentPage,
        lowerBound,
        upperBound,
        pageRangeCost,
        showFirstPage,
        showLastPage,
        rewindToFirstCost,
        rewindToLastCost,
        budget: containerWidth,
    });
}
function createLayout({ totalPages, currentPage, lowerBound, upperBound, pageRangeCost, showFirstPage, showLastPage, rewindToFirstCost, rewindToLastCost, budget, }) {
    const prevOrLowerBound = Math.max(currentPage - 1, lowerBound);
    const nextOrUpperBound = Math.min(currentPage + 1, upperBound);
    const expand = (low, high, showRewindToFirst, showRewindToLast) => createLayoutByExpandingPageRange({
        totalPages,
        low,
        high,
        lowerBound,
        upperBound,
        pageRangeCost,
        budget,
        showRewindToFirst,
        showRewindToLast,
        rewindToFirstCost,
        rewindToLastCost,
    });
    return (
    // Try to show the entire range.
    ((lowerBound === 1 || !showFirstPage) &&
        (upperBound === totalPages || !showLastPage) &&
        expand(lowerBound, upperBound, false, false)) ||
        // Ellipsis only in the end. Show at least one page after the current.
        (showLastPage &&
            lowerBound === 1 &&
            expand(lowerBound, nextOrUpperBound, false, true)) ||
        // Ellipsis only in the beginning. Show at least one page before the current.
        (showFirstPage &&
            upperBound === totalPages &&
            expand(prevOrLowerBound, upperBound, true, false)) ||
        // Ellipses on both sides. Show at least one page before the current and one after.
        (showFirstPage &&
            showLastPage &&
            expand(prevOrLowerBound, nextOrUpperBound, true, true)) ||
        // Ellipsis only in the end. Don't try to include the next page.
        (showLastPage &&
            lowerBound === 1 &&
            expand(lowerBound, currentPage, false, true)) ||
        // Ellipsis only in the beginning. Don't try to include the previous page.
        (showFirstPage &&
            upperBound === totalPages &&
            expand(currentPage, upperBound, true, false)) ||
        // Ellipses on both sides. Don't try to include the previous and the next page.
        (showFirstPage &&
            showLastPage &&
            expand(currentPage, currentPage, true, true)) ||
        // Cut off both sides without adding ellipses.
        expand(currentPage, currentPage, false, false) || [currentPage] // If there's not enough space even for the current page, still show it.
    );
}
function createLayoutByExpandingPageRange({ totalPages, low, high, lowerBound, upperBound, pageRangeCost, budget, showRewindToFirst, showRewindToLast, rewindToFirstCost, rewindToLastCost, }) {
    const safeLowerBound = showRewindToFirst
        ? Math.max(lowerBound, 4)
        : lowerBound;
    const safeUpperBound = showRewindToLast
        ? Math.min(upperBound, totalPages - 3)
        : upperBound;
    if (!isNondecreasing([
        lowerBound,
        safeLowerBound,
        low,
        high,
        safeUpperBound,
        upperBound,
    ])) {
        return null;
    }
    lowerBound = safeLowerBound;
    upperBound = safeUpperBound;
    budget -=
        (showRewindToFirst ? rewindToFirstCost : 0) +
            (showRewindToLast ? rewindToLastCost : 0);
    let acceptableLow = 0;
    let acceptableHigh = 0;
    while (lowerBound <= low &&
        high <= upperBound &&
        pageRangeCost(low, high) <= budget) {
        acceptableLow = low;
        acceptableHigh = high;
        if (low === lowerBound && high === upperBound) {
            break;
        }
        low = Math.max(low - 1, lowerBound);
        high = Math.min(high + 1, upperBound);
    }
    return acceptableLow && acceptableHigh
        ? [
            ...(showRewindToFirst ? [1, 0] : []),
            ...closedRange(acceptableLow, acceptableHigh),
            ...(showRewindToLast ? [0, totalPages] : []),
        ]
        : null;
}
function closedRange(start, stop, step = 1) {
    const result = [];
    for (let i = start; i <= stop; i += step) {
        result.push(i);
    }
    return result;
}
function isNondecreasing(sequence) {
    for (let i = 1; i < sequence.length; i++) {
        if (sequence[i] < sequence[i - 1]) {
            return false;
        }
    }
    return true;
}
function mergeBoundingRects(a, b) {
    const top = Math.min(a.top, b.top);
    const right = Math.max(a.right, b.right);
    const bottom = Math.max(a.bottom, b.bottom);
    const left = Math.min(a.left, b.left);
    const width = right - left;
    const height = bottom - top;
    return { top, right, bottom, left, width, height };
}
//# sourceMappingURL=utils.js.map