import * as Core from "@intelligentgraphics/ig.gfx.core";
import { EventEmitter, isValue, noop, } from "@intelligentgraphics/ig.utilities";
import { getPosition as getAnimationPosition } from "./Util";
export const createAnimationManager = (session, plannerManager, { continueAnimationsAfterUpdate = false, reapplyAnimationProgressAfterUpdate = false, } = {}) => {
    const playbackControls = new Map();
    const eventEmitter = new EventEmitter();
    const getRootIds = (comIds) => comIds.map(comId => session.getArticleGfxId(comId)).filter(isValue);
    const getPosition = (comIds) => {
        const rootIds = getRootIds(comIds);
        return getAnimationPosition(Core.State.API, rootIds);
    };
    const setPosition = (comIds, position) => {
        forEachPlaybackControl(comIds, playbackControl => playbackControl.stop());
        Core.Animation.setPosition(getRootIds(comIds), position === "end" ? "End" : "Start");
        eventEmitter.emit("statusChange", {
            comIds,
            status: position,
        });
    };
    const showAnimation = (comIds, params, options) => {
        const playbackControl = Core.Animation.showAnimation(getRootIds(comIds), params, options);
        const createStatusChanger = (method, status) => () => {
            playbackControl[method].apply(playbackControl);
            eventEmitter.emit("statusChange", {
                comIds,
                status,
            });
        };
        const decoratedPlaybackControl = {
            done: playbackControl.done,
            continue: createStatusChanger("continue", "running"),
            pause: createStatusChanger("pause", "paused"),
            stop: createStatusChanger("stop", "stopped"),
        };
        for (const comId of comIds) {
            playbackControls.set(comId, decoratedPlaybackControl);
        }
        eventEmitter.emit("statusChange", {
            comIds,
            status: "running",
        });
        const newStatus = params.forward === !params.invertAnimationAfter ? "end" : "start";
        playbackControl.done
            .then(() => {
            eventEmitter.emit("statusChange", {
                comIds,
                status: newStatus,
            });
        })
            .finally(() => {
            for (const comId of comIds) {
                playbackControls.delete(comId);
            }
        })
            .catch(noop);
    };
    const showAnimationFromCurrentPosition = (comIds, invertAnimationAfter, options) => {
        const position = getPosition(comIds);
        return showAnimation(comIds, { forward: position !== "end", invertAnimationAfter }, options);
    };
    const getPlaybackControls = (comIds) => {
        const controls = new Set();
        for (const comId of comIds) {
            const control = playbackControls.get(comId);
            if (control !== undefined)
                controls.add(control);
        }
        return controls;
    };
    const forEachPlaybackControl = (comIds, cb) => {
        for (const playbackControl of getPlaybackControls(comIds)) {
            cb(playbackControl);
        }
    };
    const pauseAnimation = (comIds) => forEachPlaybackControl(comIds, playbackControl => playbackControl.pause());
    const continueAnimation = (comIds) => forEachPlaybackControl(comIds, playbackControl => playbackControl.continue());
    const stopAnimation = (comIds) => forEachPlaybackControl(comIds, playbackControl => playbackControl.stop());
    const canAnimate = (comIds) => getRootIds(comIds).length > 0;
    return {
        on: eventEmitter.on.bind(eventEmitter),
        off: eventEmitter.off.bind(eventEmitter),
        once: eventEmitter.once.bind(eventEmitter),
        setPosition,
        getPosition,
        showAnimation,
        showAnimationFromCurrentPosition,
        pauseAnimation,
        continueAnimation,
        stopAnimation,
        canAnimate,
        getPlaybackControls,
    };
};
