import {avatarLoader} from "./XAvatarLoader.js"
import AvatarAnimationError from "./error/AvatarAnimationError.js"
import Logger from "./Logger.js"

const logger = new Logger('AnimationController')
export default class XAnimationController {
    constructor(e) {
        E(this, "iBodyAnim");
        E(this, "animations", []);
        E(this, "defaultAnimation", "Idle");
        E(this, "onPlay", "Idle");
        E(this, "loop", !0);
        E(this, "animationExtras", []);
        E(this, "enableBlend", !1);
        E(this, "enableSkLod", !1);
        E(this, "_boneMap", new Map);
        E(this, "_lodMask", new Map);
        E(this, "activeFaceAnimation");
        E(this, "iFaceAnim");
        E(this, "_scene");
        E(this, "_avatar");
        E(this, "onPlayObservable", new Observable);
        E(this, "postObserver");
        E(this, "playAnimation", (e,t,r=0,n,o,a)=>new Promise((s,l)=>{
            if (this._isPlaying(e, r) || (this._registerAnimInfo(e, t, r, n, o, a),
            !this._isAnimate()))
                return s(null);
            this._prerocess(e, t),
            this._avatar.avatarManager.loadAnimation(this._avatar.avatarType, e).then(u=>{
                if (!u)
                    return l(new AvatarAnimationError("animation group does not exist"));
                const c = this._mappingSkeleton(u);
                if (!c)
                    return l(new AvatarAnimationError("mapping animation failed"));
                if (c && this._isAnimationValid(c))
                    return c.dispose(),
                    l(new AvatarAnimationError("mapping animation failed"));
                if (this.enableSkLod && this.skeletonMask(c, r),
                this.detachAnimation(r),
                r == 0 ? this.iBodyAnim.animGroup = c : r == 1 && (this.iFaceAnim.animGroup = c),
                !this._playAnimation(r))
                    return l(new AvatarAnimationError("[Engine] play animation failed, animtion resource does not match current character"));
                this._playEffect(),
                this.postObserver = c.onAnimationEndObservable.addOnce(()=>(this._postprocess(r),
                s(null)))
            }
            )
        }
        ));
        E(this, "stopAnimation", (e=0)=>{
            var t, r, n, o;
            switch (e) {
            case 0:
                this.iBodyAnim && this.iBodyAnim.animGroup && ((t = this.iBodyAnim) == null || t.animGroup.stop());
                break;
            case 1:
                this.iFaceAnim && this.iFaceAnim.animGroup && ((r = this.iFaceAnim) == null || r.animGroup.stop());
                break;
            case 2:
                this.iBodyAnim && this.iBodyAnim.animGroup && ((n = this.iBodyAnim) == null || n.animGroup.stop()),
                this.iFaceAnim && this.iFaceAnim.animGroup && ((o = this.iFaceAnim) == null || o.animGroup.stop());
                break
            }
        }
        );
        E(this, "pauseAnimation", (e=0)=>{
            var t, r, n, o;
            switch (e) {
            case 0:
                this.iBodyAnim && this.iBodyAnim.animGroup && ((t = this.iBodyAnim) == null || t.animGroup.pause());
                break;
            case 1:
                this.iFaceAnim && this.iFaceAnim.animGroup && ((r = this.iFaceAnim) == null || r.animGroup.pause());
                break;
            case 2:
                this.iBodyAnim && this.iBodyAnim.animGroup && ((n = this.iBodyAnim) == null || n.animGroup.pause()),
                this.iFaceAnim && this.iFaceAnim.animGroup && ((o = this.iFaceAnim) == null || o.animGroup.pause());
                break
            }
        }
        );
        E(this, "resetAnimation", (e=0)=>{
            var t, r, n, o;
            switch (e) {
            case 0:
                this.iBodyAnim && this.iBodyAnim.animGroup && ((t = this.iBodyAnim) == null || t.animGroup.reset());
                break;
            case 1:
                this.iFaceAnim && this.iFaceAnim.animGroup && ((r = this.iFaceAnim) == null || r.animGroup.reset());
                break;
            case 2:
                this.iBodyAnim && this.iBodyAnim.animGroup && ((n = this.iBodyAnim) == null || n.animGroup.reset()),
                this.iFaceAnim && this.iFaceAnim.animGroup && ((o = this.iFaceAnim) == null || o.animGroup.reset());
                break
            }
        }
        );
        this._avatar = e,
        this._scene = e.avatarManager.scene,
        this.animationExtras.push(action.Cheering.animName),
        this._boneMap = new Map
    }
    _isPlaying(e, t) {
        return t == 0 && this.iBodyAnim != null && this.iBodyAnim.animGroup && e == this.iBodyAnim.name ? !0 : !!(t == 1 && this.iFaceAnim != null && this.iFaceAnim.animGroup && e == this.iFaceAnim.name)
    }
    activeAnimation(e=0) {
        var t, r;
        switch (e) {
        case 0:
            return (t = this.iBodyAnim) == null ? void 0 : t.animGroup;
        case 1:
            return (r = this.iFaceAnim) == null ? void 0 : r.animGroup;
        default:
            return
        }
    }
    enableAnimationBlend(e=.1, t=0) {
        var r, n, o, a;
        if (t == 0 && ((r = this.iBodyAnim) == null ? void 0 : r.animGroup))
            for (const s of (n = this.iBodyAnim) == null ? void 0 : n.animGroup.targetedAnimations)
                s.animation.enableBlending = !0,
                s.animation.blendingSpeed = e;
        else if (t == 0 && ((o = this.iFaceAnim) == null ? void 0 : o.animGroup))
            for (const s of (a = this.iFaceAnim) == null ? void 0 : a.animGroup.targetedAnimations)
                s.animation.enableBlending = !0,
                s.animation.blendingSpeed = e
    }
    disableAnimationBlend(e=0) {
        var t, r, n, o;
        if (e == 0 && ((t = this.iBodyAnim) == null ? void 0 : t.animGroup))
            for (const a of (r = this.iBodyAnim) == null ? void 0 : r.animGroup.targetedAnimations)
                a.animation.enableBlending = !1;
        else if (e == 0 && ((n = this.iFaceAnim) == null ? void 0 : n.animGroup))
            for (const a of (o = this.iFaceAnim) == null ? void 0 : o.animGroup.targetedAnimations)
                a.animation.enableBlending = !1
    }
    skeletonMask(e, t=0) {
        if (t == 0) {
            const r = this._lodMask.get(this._avatar.distLevel);
            if (r)
                for (let n = 0; n < e.targetedAnimations.length; ++n)
                    r.includes(e.targetedAnimations[n].target.name) || (e.targetedAnimations.splice(n, 1),
                    n--);
            return !0
        }
        return !1
    }
    detachAnimation(e=2) {
        var t, r;
        switch (e) {
        case 0:
            this.iBodyAnim && this.iBodyAnim.animGroup && (this.iBodyAnim.animGroup._parentContainer.xReferenceCount && this.iBodyAnim.animGroup._parentContainer.xReferenceCount--,
            this.iBodyAnim.animGroup.stop(),
            this.iBodyAnim.animGroup.dispose(),
            this.iBodyAnim.animGroup = void 0);
            break;
        case 1:
            this.iFaceAnim && this.iFaceAnim.animGroup && (this.iFaceAnim.animGroup._parentContainer.xReferenceCount && this.iFaceAnim.animGroup._parentContainer.xReferenceCount--,
            this.iFaceAnim.animGroup.stop(),
            this.iFaceAnim.animGroup.dispose(),
            this.iFaceAnim.animGroup = void 0);
            break;
        case 2:
            this.iBodyAnim && this.iBodyAnim.animGroup && (this.iBodyAnim.animGroup._parentContainer.xReferenceCount && this.iBodyAnim.animGroup._parentContainer.xReferenceCount--,
            (t = this.iBodyAnim) == null || t.animGroup.stop(),
            (r = this.iBodyAnim) == null || r.animGroup.dispose(),
            this.iBodyAnim.animGroup = void 0),
            this.iFaceAnim && this.iFaceAnim.animGroup && (this.iFaceAnim.animGroup._parentContainer.xReferenceCount && this.iFaceAnim.animGroup._parentContainer.xReferenceCount--,
            this.iFaceAnim.animGroup.stop(),
            this.iFaceAnim.animGroup.dispose(),
            this.iFaceAnim.animGroup = void 0);
            break
        }
    }
    blendAnimation() {}
    getAnimation(e, t) {
        return avatarLoader.animations.get(getAnimationKey(t, e))
    }
    _mappingSkeleton(e) {
        if (e) {
            const t = e.clone(e.name, r=>{
                var o, a, s;
                const n = r.name.split(" ").length > 2 ? r.name.split(" ")[2] : r.name;
                if (this._boneMap.size === ((o = this._avatar.skeleton) == null ? void 0 : o.bones.length))
                    return this._boneMap.get(n);
                {
                    const l = (s = (a = this._avatar.skeleton) == null ? void 0 : a.bones.find(u=>u.name === r.name || u.name === r.name.split(" ")[2])) == null ? void 0 : s.getTransformNode();
                    return l && (l.name = n,
                    this._boneMap.set(n, l)),
                    l
                }
            }
            );
            return t._parentContainer = e._parentContainer,
            t
        } else
            return
    }
    removeAnimation(e) {
        const t = avatarLoader.containers.get(e.name);
        t && (t.dispose(),
        avatarLoader.containers.delete(e.name),
        avatarLoader.animations.delete(getAnimationKey(e.name, e.skType)))
    }
    _setPosition(e, t) {
        this._avatar.priority === 0 && this._avatar.isRender && e === this.defaultAnimation && e != this.onPlay && !this._avatar.isSelected && this._avatar.setPosition(this._avatar.position, !0)
    }
    _registerAnimInfo(e, t, r=0, n, o, a) {
        const s = {
            name: e,
            skType: this._avatar.avatarType,
            loop: t,
            playSpeed: n,
            currentFrame: 0,
            startFrame: o,
            endFrame: a
        };
        r == 0 ? this.iBodyAnim == null ? this.iBodyAnim = s : (this.iBodyAnim.name = e,
        this.iBodyAnim.skType = this._avatar.avatarType,
        this.iBodyAnim.loop = t,
        this.iBodyAnim.playSpeed = n,
        this.iBodyAnim.currentFrame = 0,
        this.iBodyAnim.startFrame = o,
        this.iBodyAnim.endFrame = a) : r == 1 && (this.iFaceAnim == null ? this.iFaceAnim = s : (this.iFaceAnim.name = e,
        this.iFaceAnim.skType = this._avatar.avatarType,
        this.iFaceAnim.loop = t,
        this.iFaceAnim.playSpeed = n,
        this.iFaceAnim.currentFrame = 0,
        this.iFaceAnim.startFrame = o,
        this.iFaceAnim.endFrame = a)),
        this.onPlay = e,
        this.loop = t
    }
    _isAnimate() {
        var e;
        return !(!this._avatar.isRender || !this._avatar.skeleton || ((e = this._avatar.rootNode) == null ? void 0 : e.getChildMeshes().length) == 0)
    }
    _prerocess(e, t) {
        this._avatar.isRayCastEnable && this._setPosition(e, t),
        this._avatar.priority === 0 && logger.info(`start play animation: ${e} on avatar ${this._avatar.id}`)
    }
    _playEffect() {
        this.animationExtras.indexOf(this.iBodyAnim.name) != -1 && action.Cheering.attachPair.forEach(t=>{
            this._avatar.attachExtraProp(t.obj, t.bone, new BABYLON.Vector3(t.offset.x,t.offset.y,t.offset.z), new BABYLON.Vector3(t.rotate.x,t.rotate.y,t.rotate.z)),
            this._avatar.showExtra(t.obj)
        }
        )
    }
    _playAnimation(e=0) {
        var t, r;
        return e == 0 && this.iBodyAnim && ((t = this.iBodyAnim) == null ? void 0 : t.animGroup) ? (this.onPlayObservable.notifyObservers(this._scene),
        this.iBodyAnim.animGroup.start(this.loop, this.iBodyAnim.playSpeed, this.iBodyAnim.startFrame, this.iBodyAnim.endFrame, !1),
        !0) : e == 1 && this.iFaceAnim && ((r = this.iFaceAnim) == null ? void 0 : r.animGroup) ? (this.iFaceAnim.animGroup.start(this.loop, this.iFaceAnim.playSpeed, this.iFaceAnim.startFrame, this.iFaceAnim.endFrame, !1),
        !0) : !1
    }
    _postprocess(e) {
        var r, n;
        let t;
        e == 0 ? t = (r = this.iBodyAnim) == null ? void 0 : r.name : e == 1 && (t = (n = this.iFaceAnim) == null ? void 0 : n.name),
        t === action.Cheering.animName && this._avatar.disposeExtra()
    }
    _isAnimationValid(e) {
        for (let t = 0; t < e.targetedAnimations.length; ++t)
            if (e.targetedAnimations[t].target)
                return !1;
        return !0
    }
}