import {
    computedStyle
} from '#core/dom/style';

/**
 * ViewportBindingDef is an interface that defines an underlying technology
 * behind the {@link ViewportInterface}.
 * @interface
 */
export class ViewportBindingDef {
    /**
     * Called before a first AMP element is added to resources. The final
     * preparations must be completed here. Called in the mutate context.
     */
    ensureReadyForElements() {}

    /**
     * Add listeners for global resources.
     */
    connect() {}

    /**
     * Remove listeners for global resources.
     */
    disconnect() {}

    /**
     * Returns the width of top border if this type of viewport needs border
     * offsetting. This is currently only needed for iOS to avoid scroll freeze.
     * @return {number}
     */
    getBorderTop() {}

    /**
     * Whether the binding requires fixed elements to be transfered to a
     * independent fixed layer.
     * @return {boolean}
     */
    requiresFixedLayerTransfer() {}

    /**
     * Whether the binding requires the global window's `scrollTo` to be
     * indirected via methods of this binding.
     * @return {boolean}
     */
    overrideGlobalScrollTo() {}

    /**
     * Whether the binding supports fix-positioned elements.
     * @return {boolean}
     */
    supportsPositionFixed() {}

    /**
     * Register a callback for scroll events.
     * @param {function()} unusedCallback
     */
    onScroll(unusedCallback) {}

    /**
     * Register a callback for resize events.
     * @param {function()} unusedCallback
     */
    onResize(unusedCallback) {}

    /**
     * Updates binding with the new padding.
     * @param {number} unusedPaddingTop
     */
    updatePaddingTop(unusedPaddingTop) {}

    /**
     * Updates binding with the new padding when hiding viewer header.
     * @param {boolean} unusedTransient
     * @param {number} unusedLastPaddingTop
     */
    hideViewerHeader(unusedTransient, unusedLastPaddingTop) {}

    /**
     * Updates binding with the new padding when showing viewer header.
     * @param {boolean} unusedTransient
     * @param {number} unusedPaddingTop
     */
    showViewerHeader(unusedTransient, unusedPaddingTop) {}

    /**
     * Disable the scrolling by setting overflow: hidden.
     * Should only be used for temporarily disabling scroll.
     */
    disableScroll() {}

    /**
     * Reset the scrolling by removing overflow: hidden.
     */
    resetScroll() {}

    /**
     * Updates the viewport whether it's currently in the lightbox or a normal
     * mode.
     * @param {boolean} unusedLightboxMode
     * @return {!Promise}
     */
    updateLightboxMode(unusedLightboxMode) {}

    /**
     * Returns the size of the viewport.
     * @return {!{width: number, height: number}}
     */
    getSize() {}

    /**
     * Returns the top scroll position for the viewport.
     * @return {number}
     */
    getScrollTop() {}

    /**
     * Sets scroll top position to the specified value or the nearest possible.
     * @param {number} unusedScrollTop
     */
    setScrollTop(unusedScrollTop) {}

    /**
     * Returns the left scroll position for the viewport.
     * @return {number}
     */
    getScrollLeft() {}

    /**
     * Returns the scroll width of the content of the document.
     * @return {number}
     */
    getScrollWidth() {}

    /**
     * Returns the scroll height of the content of the document, including the
     * padding top for the viewer header.
     * The scrollHeight will be the viewport height if there's not enough content
     * to fill up the viewport.
     * @return {number}
     */
    getScrollHeight() {}

    /**
     * Returns the height of the content of the document, including the
     * padding top for the viewer header.
     * contentHeight will match scrollHeight in all cases unless the viewport is
     * taller than the content.
     * @return {number}
     */
    getContentHeight() {}

    /**
     * Resource manager signals to the viewport that content height is changed
     * and some action may need to be taken.
     * @restricted Use is restricted due to potentially very heavy performance
     *   impact. Can only be called when not actively scrolling.
     */
    contentHeightChanged() {}

    /**
     * Returns the rect of the element within the document.
     * @param {!Element} unusedEl
     * @param {number=} unusedScrollLeft Optional arguments that the caller may
     *     pass in, if they cached these values and would like to avoid
     *     remeasure. Requires appropriate updating the values on scroll.
     * @param {number=} unusedScrollTop Same comment as above.
     * @return {!../../layout-rect.LayoutRectDef}
     */
    getLayoutRect(unusedEl, unusedScrollLeft, unusedScrollTop) {}

    /**
     * Returns the client rect of the current window.
     * @return {Promise<null>|Promise<!../../layout-rect.LayoutRectDef>}
     */
    getRootClientRectAsync() {}

    /**
     * Returns the element considered the root scroller for this binding.
     * @return {!Element}
     */
    getScrollingElement() {}

    /**
     * Whether the root scroller is a native root scroller (behaves like a
     * viewport), or an overflow scroller (scrolls like an element).
     * @return {boolean}
     */
    getScrollingElementScrollsLikeViewport() {}
}

/**
 * Returns the margin-bottom of the last child of `element` that affects
 * document height (is static/relative position with non-zero height),
 * if any. Otherwise, returns 0.
 *
 * TODO(choumx): This is a weird location, so refactor to improve code sharing
 * among implementations of ViewportBindingDef generally.
 *
 * @param {!Window} win
 * @param {!Element} element
 * @return {number}
 */
export function marginBottomOfLastChild(win, element) {
    let style;
    for (let n = element.lastElementChild; n; n = n.previousElementSibling) {
        const r = n. /*OK*/ getBoundingClientRect();
        if (r.height > 0) {
            const s = computedStyle(win, n);
            if (s.position == 'static' || s.position == 'relative') {
                style = s;
                break;
            }
        }
    }
    return style ? parseInt(style.marginBottom, 10) : 0;
}