var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { html } from 'lit';
import { property } from 'lit/decorators.js';
import { createRef, ref } from 'lit/directives/ref.js';
import lottie from 'lottie-web/build/player/lottie_light';
import animationManifest from '@momentum-design/animations/dist/manifest';
import { Component } from '../../models';
import { ROLE } from '../../utils/roles';
import styles from './animation.styles';
import { DEFAULTS } from './animation.constants';
/**
 * The `mdc-animation` component is a wrapper around the Lottie animation library.
 * It fetches the animation data dynamically based on the provided name and renders it.
 * This is a display only component that does not have any interactive functionality.
 * From accessibility perspective, (by default) it is a decorative image component.
 *
 * @tagname mdc-animation
 *
 * @event load - (React: onLoad) This event is dispatched when the animation is loaded
 * @event complete - (React: onComplete) This event is dispatched when all animation loops completed
 * @event error - (React: onError) This event is dispatched when animation loading failed
 */
class Animation extends Component {
    constructor() {
        super(...arguments);
        /**
         * How many times to loop the animation
         * - "true" - infinite
         * - "false" - no loop
         * - number - number of times to loop
         * @default 'true'
         */
        this.loop = DEFAULTS.LOOP;
        /**
         * Weather start the animation automatically
         * @default true
         */
        this.autoplay = DEFAULTS.AUTO_PLAY;
        /**
         * Aria-label attribute to be set for accessibility
         * @default null
         */
        this.ariaLabel = null;
        /**
         * Aria-labelledby attribute to be set for accessibility
         * @default null
         */
        this.ariaLabelledby = null;
        /**
         * Container for the animation
         * @internal
         */
        this.containerRef = createRef();
        /**
         * Re-dispatch the complete event from the animation library
         *
         * This handler called with the animation instance instead of the component instance
         * so we need to bind it to the component instance. The arrow function just does that.
         * @internal
         */
        this.onCompleteHandler = () => {
            const event = new CustomEvent('complete', {
                detail: { name: this.name },
                bubbles: true,
            });
            this.dispatchEvent(event);
        };
    }
    /**
     * Exposed API of the animation library (lottie)
     * @internal
     */
    get animation() {
        return this.lottieInstance;
    }
    getLoopValue() {
        if (this.loop === 'true')
            return true;
        if (this.loop === 'false')
            return false;
        if (this.loop)
            return Number(this.loop);
        return true;
    }
    /**
     * Create new lottie instance for the loaded data
     */
    onLoadSuccessHandler(animationData) {
        this.cachedAnimationData = animationData;
        this.createLottieInstance(animationData);
        // Dispatch load event when animation ready to play
        this.dispatchEvent(new CustomEvent('load', { bubbles: true, cancelable: true, detail: { name: this.name } }));
    }
    /**
     * Create or re-create the lottie instance with the given animation data
     */
    createLottieInstance(animationData) {
        if (this.lottieInstance) {
            this.lottieInstance.removeEventListener('complete', this.onCompleteHandler);
            this.lottieInstance.destroy();
        }
        if (this.containerRef.value) {
            this.lottieInstance = lottie.loadAnimation({
                container: this.containerRef.value, // the dom element that will contain the animation
                renderer: 'svg',
                loop: this.getLoopValue(),
                autoplay: this.autoplay,
                animationData,
                name: this.name,
            });
            this.lottieInstance.addEventListener('complete', this.onCompleteHandler);
        }
    }
    /**
     * Error handler for animation loading
     */
    onLoadFailHandler(error) {
        const errorEvent = new CustomEvent('error', {
            bubbles: true,
            cancelable: true,
            detail: { error },
        });
        this.dispatchEvent(errorEvent);
    }
    /**
     * Import animation data dynamically
     */
    getAnimationData() {
        if (this.src) {
            this.fetchAnimationFromUrl(this.src);
        }
        else if (this.name && animationManifest[this.name]) {
            // Make sure the path is point to a folder (and its sub-folders) that contains animation data only
            // otherwise bundlers (eg. webpack) will try to process everything in this folder including the types.d.ts
            const path = animationManifest[this.name].replace(/^\.\/lottie/, '');
            import(`@momentum-design/animations/dist/lottie${path}`)
                .then((result) => this.onLoadSuccessHandler(result.default))
                .catch((error) => this.onLoadFailHandler(error));
        }
        else {
            this.onLoadFailHandler(new Error(`Invalid animation name: ${this.name}`));
        }
    }
    /**
     * Fetch animation data from a URL
     */
    fetchAnimationFromUrl(url) {
        fetch(url)
            .then(response => {
            if (!response.ok) {
                throw new Error(`Failed to fetch animation from URL: ${url} (${response.status})`);
            }
            return response.json();
        })
            .then((animationData) => this.onLoadSuccessHandler(animationData))
            .catch((error) => this.onLoadFailHandler(error));
    }
    updated(changedProperties) {
        super.updated(changedProperties);
        // fetch animation data when the animation source changes
        if (changedProperties.has('name') || changedProperties.has('src')) {
            this.cachedAnimationData = undefined;
            this.getAnimationData();
        }
        else if ((changedProperties.has('loop') || changedProperties.has('autoplay')) && this.cachedAnimationData) {
            // re-create the lottie instance from cache for parameter changes,
            // because lottie does not have an API for changing them on the fly
            this.createLottieInstance(this.cachedAnimationData);
        }
        if (changedProperties.has('ariaLabel') || changedProperties.has('ariaLabelledby')) {
            this.role = this.ariaLabel || this.ariaLabelledby ? ROLE.IMG : null;
        }
    }
    disconnectedCallback() {
        super.disconnectedCallback();
        // Clean up the lottie instance when the component is removed from the DOM
        if (this.lottieInstance) {
            this.lottieInstance.removeEventListener('complete', this.onCompleteHandler);
            this.lottieInstance.destroy();
            this.lottieInstance = undefined;
        }
    }
    render() {
        return html `<div aria-hidden="true" ${ref(this.containerRef)}></div>`;
    }
}
Animation.styles = [...Component.styles, ...styles];
__decorate([
    property({ type: String, reflect: true }),
    __metadata("design:type", String)
], Animation.prototype, "name", void 0);
__decorate([
    property({ type: String, reflect: true }),
    __metadata("design:type", String)
], Animation.prototype, "src", void 0);
__decorate([
    property({ type: String, reflect: true }),
    __metadata("design:type", String)
], Animation.prototype, "loop", void 0);
__decorate([
    property({ type: Boolean, reflect: true }),
    __metadata("design:type", Boolean)
], Animation.prototype, "autoplay", void 0);
__decorate([
    property({ type: String, attribute: 'aria-label' }),
    __metadata("design:type", Object)
], Animation.prototype, "ariaLabel", void 0);
__decorate([
    property({ type: String, attribute: 'aria-labelledby' }),
    __metadata("design:type", Object)
], Animation.prototype, "ariaLabelledby", void 0);
export default Animation;
