import { supportsScrollTimeline } from 'motion-dom';
import { noop } from 'motion-utils';
import { observeTimeline } from './observe.mjs';
import { scrollInfo } from './track.mjs';

function scrollTimelineFallback({ source, container, axis = "y", }) {
    // Support legacy source argument. Deprecate later.
    if (source)
        container = source;
    // ScrollTimeline records progress as a percentage CSSUnitValue
    const currentTime = { value: 0 };
    const cancel = scrollInfo((info) => {
        currentTime.value = info[axis].progress * 100;
    }, { container, axis });
    return { currentTime, cancel };
}
const timelineCache = new Map();
function getTimeline({ source, container = document.documentElement, axis = "y", } = {}) {
    // Support legacy source argument. Deprecate later.
    if (source)
        container = source;
    if (!timelineCache.has(container)) {
        timelineCache.set(container, {});
    }
    const elementCache = timelineCache.get(container);
    if (!elementCache[axis]) {
        elementCache[axis] = supportsScrollTimeline()
            ? new ScrollTimeline({ source: container, axis })
            : scrollTimelineFallback({ source: container, axis });
    }
    return elementCache[axis];
}
/**
 * If the onScroll function has two arguments, it's expecting
 * more specific information about the scroll from scrollInfo.
 */
function isOnScrollWithInfo(onScroll) {
    return onScroll.length === 2;
}
/**
 * Currently, we only support element tracking with `scrollInfo`, though in
 * the future we can also offer ViewTimeline support.
 */
function needsElementTracking(options) {
    return options && (options.target || options.offset);
}
function scrollFunction(onScroll, options) {
    if (isOnScrollWithInfo(onScroll) || needsElementTracking(options)) {
        return scrollInfo((info) => {
            onScroll(info[options.axis].progress, info);
        }, options);
    }
    else {
        return observeTimeline(onScroll, getTimeline(options));
    }
}
function scrollAnimation(animation, options) {
    animation.flatten();
    if (needsElementTracking(options)) {
        animation.pause();
        return scrollInfo((info) => {
            animation.time = animation.duration * info[options.axis].progress;
        }, options);
    }
    else {
        const timeline = getTimeline(options);
        if (animation.attachTimeline) {
            return animation.attachTimeline(timeline, (valueAnimation) => {
                valueAnimation.pause();
                return observeTimeline((progress) => {
                    valueAnimation.time = valueAnimation.duration * progress;
                }, timeline);
            });
        }
        else {
            return noop;
        }
    }
}
function scroll(onScroll, { axis = "y", ...options } = {}) {
    const optionsWithDefaults = { axis, ...options };
    return typeof onScroll === "function"
        ? scrollFunction(onScroll, optionsWithDefaults)
        : scrollAnimation(onScroll, optionsWithDefaults);
}

export { scroll };
