let activeHref = location.href;
function updatePageToc(elem = undefined) {
    let selectedPageTocElem = elem;
    const pagetoc = document.getElementById("pagetoc");

    function getRect(element) {
        return element.getBoundingClientRect();
    }

    function overflowTop(container, element) {
        return getRect(container).top - getRect(element).top;
    }

    function overflowBottom(container, element) {
        return getRect(container).bottom - getRect(element).bottom;
    }

    // We've not selected a heading to highlight, and the URL needs updating
    // so we need to find a heading based on the URL
    if (selectedPageTocElem === undefined && location.href !== activeHref) {
        activeHref = location.href;
        for (const pageTocElement of pagetoc.children) {
            if (pageTocElement.href === activeHref) {
                selectedPageTocElem = pageTocElement;
            }
        }
    }

    // We still don't have a selected heading, let's try and find the most
    // suitable heading based on the scroll position
    if (selectedPageTocElem === undefined) {
        const margin = window.innerHeight / 3;

        const headers = document.getElementsByClassName("header");
        for (let i = 0; i < headers.length; i++) {
            const header = headers[i];
            if (selectedPageTocElem === undefined && getRect(header).top >= 0) {
                if (getRect(header).top < margin) {
                    selectedPageTocElem = header;
                } else {
                    selectedPageTocElem = headers[Math.max(0, i - 1)];
                }
            }
            // a very long last section's heading is over the screen
            if (selectedPageTocElem === undefined && i === headers.length - 1) {
                selectedPageTocElem = header;
            }
        }
    }

    // Remove the active flag from all pagetoc elements
    for (const pageTocElement of pagetoc.children) {
        pageTocElement.classList.remove("active");
    }

    // If we have a selected heading, set it to active and scroll to it
    if (selectedPageTocElem !== undefined) {
        for (const pageTocElement of pagetoc.children) {
            if (selectedPageTocElem.href.localeCompare(pageTocElement.href) === 0) {
                pageTocElement.classList.add("active");
                if (overflowTop(pagetoc, pageTocElement) > 0) {
                    pagetoc.scrollTop = pageTocElement.offsetTop;
                }
                if (overflowBottom(pagetoc, pageTocElement) < 0) {
                    pagetoc.scrollTop -= overflowBottom(pagetoc, pageTocElement);
                }
            }
        }
    }
}

if (document.getElementsByClassName("header").length <= 1) {
    // There's one or less headings, we don't need a page table of contents
    document.getElementById("sidetoc").remove();
} else {
    // Populate sidebar on load
    window.addEventListener("load", () => {
        for (const header of document.getElementsByClassName("header")) {
            const link = document.createElement("a");
            link.appendChild(document.createTextNode(header.text));
            link.href = header.hash;
            link.classList.add("pagetoc-" + header.parentElement.tagName);
            document.getElementById("pagetoc").appendChild(link);
            link.onclick = () => updatePageToc(link);
        }
        updatePageToc();
    });

    // Update page table of contents selected heading on scroll
    window.addEventListener("scroll", () => updatePageToc());
}
