// #region Imports
import { RendererServiceLocator } from '../Dom/Renderer';
// #endregion
/**
 * The `AnimationPlayer` class provides functionality to play and manage animations on HTML elements.
 *
 * @public
 */
export class AnimationPlayer {
    // #region Ctor
    /**
     * Constructs a new instance of the `AnimationPlayer` class.
     *
     * @public
     */
    constructor() {
    }
    // #endregion
    // #region Methods
    /**
     * Plays an animation on the specified element.
     *
     * @param element - The element to animate.
     * @param animation - The animation reference metadata containing keyframes and options.
     * @param overrideOptions - Optional override options for the animation.
     * @returns A promise that resolves when the animation completes.
     * @public
     */
    play(element, animation, overrideOptions) {
        return new Promise((resolve) => {
            const parsedKeyframes = this.parseKeyframes(element, animation.steps);
            const options = {
                duration: 333,
                easing: 'ease',
                fill: 'forwards',
                ...animation.options,
                ...overrideOptions
            };
            // Respect prefers-reduced-motion
            if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
                options.duration = 0;
            }
            const anim = element.animate(parsedKeyframes, options);
            anim.addEventListener('cancel', (event) => {
                resolve(event);
            }, { once: true });
            anim.addEventListener('finish', (event) => {
                this.applyKeyframes(element, parsedKeyframes);
                resolve(event);
            }, { once: true });
            anim.play();
        });
    }
    /**
     * Stops all running animations on the specified element.
     *
     * @param element - The element to stop animations on.
     * @returns A promise that resolves when all animations are stopped.
     * @public
     */
    stopAll(element) {
        return Promise.all(element.getAnimations().map((animation) => new Promise((resolve) => {
            const handleAnimationEvent = () => requestAnimationFrame(resolve);
            animation.addEventListener('cancel', handleAnimationEvent, { once: true });
            animation.addEventListener('finish', handleAnimationEvent, { once: true });
            animation.cancel();
        })));
    }
    /**
     * Parses keyframes and resolves special values like `height: 'auto'`.
     *
     * @param element - The element being animated.
     * @param keyframes - The keyframes to parse.
     * @returns The parsed keyframes with resolved values.
     * @private
     */
    parseKeyframes(element, keyframes) {
        return keyframes.map((keyframe) => {
            const parsed = { ...keyframe };
            if (parsed.height === 'auto') {
                parsed.height = `${element.scrollHeight}px`;
            }
            if (parsed.width === 'auto') {
                parsed.width = `${element.scrollWidth}px`;
            }
            return parsed;
        });
    }
    /**
     * Plays an animation from the current element state to the final keyframe.
     * This is useful when initial styles have been applied and you want to animate
     * from those styles to the final state.
     *
     * @param element - The element to animate.
     * @param animation - The animation reference metadata containing keyframes and options.
     * @param overrideOptions - Optional override options for the animation.
     * @returns A promise that resolves when the animation completes.
     * @public
     */
    playToFinal(element, animation, overrideOptions) {
        return new Promise((resolve) => {
            if (animation.steps.length === 0) {
                resolve(new AnimationPlaybackEvent('finish', { currentTime: 0, timelineTime: 0 }));
                return;
            }
            // Get the final keyframe as the target
            const finalKeyframe = this.parseKeyframes(element, [animation.steps[animation.steps.length - 1]])[0];
            // Convert to PropertyIndexedKeyframes format (animates from current state to target)
            const targetProps = {};
            for (const [key, value] of Object.entries(finalKeyframe)) {
                if (key === 'offset' || key === 'easing' || key === 'composite') {
                    continue;
                }
                targetProps[key] = value;
            }
            const options = {
                duration: 333,
                easing: 'ease',
                fill: 'forwards',
                ...animation.options,
                ...overrideOptions
            };
            // Respect prefers-reduced-motion
            if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
                options.duration = 0;
            }
            const anim = element.animate(targetProps, options);
            anim.addEventListener('cancel', (event) => {
                resolve(event);
            }, { once: true });
            anim.addEventListener('finish', (event) => {
                this.applyStyles(element, targetProps);
                resolve(event);
            }, { once: true });
            anim.play();
        });
    }
    /**
     * Applies the final keyframe values to the element's style.
     *
     * @param element - The element to apply styles to.
     * @param keyframes - The keyframes array.
     * @private
     */
    applyKeyframes(element, keyframes) {
        if (keyframes.length === 0) {
            return;
        }
        const finalKeyframe = keyframes[keyframes.length - 1];
        for (const [key, value] of Object.entries(finalKeyframe)) {
            if (key === 'offset' || key === 'easing' || key === 'composite') {
                continue;
            }
            if (key in element.style) {
                RendererServiceLocator.current.setStyle(element, key, value?.toString() ?? '');
            }
        }
    }
    /**
     * Applies styles from a PropertyIndexedKeyframes object to the element.
     *
     * @param element - The element to apply styles to.
     * @param props - The property values to apply.
     * @private
     */
    applyStyles(element, props) {
        for (const [key, value] of Object.entries(props)) {
            if (key in element.style) {
                RendererServiceLocator.current.setStyle(element, key, value?.toString() ?? '');
            }
        }
    }
}
//# sourceMappingURL=AnimationPlayer.js.map