import { _decorator, sp } from "cc";
import AnimatorSpineSecondary from "./AnimatorSpineSecondary";
import AnimatorBase, { AnimationPlayer } from "./core/AnimatorBase";
import { AnimatorStateLogic } from "./core/AnimatorStateLogic";

const { ccclass, property, requireComponent, disallowMultiple, menu, help } = _decorator;

/** 
 * Spine状态机组件（主状态机），trackIndex为0
 */
@ccclass
@disallowMultiple
@requireComponent(sp.Skeleton)
@menu('OopsFramework/Animator/AnimatorSpine（Spine 状态机）')
@help('https://gitee.com/dgflash/oops-framework/wikis/pages?sort_id=12036279&doc_id=2873565')
export default class AnimatorSpine extends AnimatorBase {
    /** spine组件 */
    protected _spine: sp.Skeleton = null!;
    /** 动画完成的回调 */
    protected _completeListenerMap: Map<(entry?: any) => void, any> = new Map();
    /** 次状态机注册的回调 */
    protected _secondaryListenerMap: Map<(entry?: any) => void, AnimatorSpineSecondary> = new Map();

    protected start() {
        if (!this.PlayOnStart || this._hasInit) {
            return;
        }
        this._hasInit = true;

        this._spine = this.getComponent(sp.Skeleton)!;
        this._spine.setEventListener(this.onSpineEvent.bind(this));
        this._spine.setCompleteListener(this.onSpineComplete.bind(this));

        if (this.AssetRawUrl !== null) {
            this.initJson(this.AssetRawUrl.json);
        }
    }

    /**
     * 手动初始化状态机，可传入0-3个参数，类型如下
     * - onStateChangeCall 状态切换时的回调
     * - stateLogicMap 各个状态逻辑控制
     * - animationPlayer 自定义动画控制
     * @override
     */
    public onInit(...args: Array<Map<string, AnimatorStateLogic> | ((fromState: string, toState: string) => void) | AnimationPlayer>) {
        if (this.PlayOnStart || this._hasInit) {
            return;
        }
        this._hasInit = true;

        this.initArgs(...args);

        this._spine = this.getComponent(sp.Skeleton)!;
        this._spine.setEventListener(this.onSpineEvent.bind(this));
        this._spine.setCompleteListener(this.onSpineComplete.bind(this));

        if (this.AssetRawUrl !== null) {
            this.initJson(this.AssetRawUrl.json);
        }
    }

    /** ---------- 后续扩展代码 开始 ---------- */

    public getBone(name: string): any {
        const bone = this._spine.findBone(name);
        return bone
    }

    private onSpineEvent(trackEntry: any, event: any) {
        const animationName = trackEntry.animation ? event.data.name : "";
        this._animationPlayer?.onFrameEventCallback(animationName, this);
    }

    /** ---------- 后续扩展代码 结束 ---------- */

    private onSpineComplete(entry: any) {
        entry.trackIndex === 0 && this.onAnimFinished();
        this._completeListenerMap.forEach((target, cb) => { target ? cb.call(target, entry) : cb(entry); });
        this._secondaryListenerMap.forEach((target, cb) => { entry.trackIndex === target.TrackIndex && cb.call(target, entry); });
    }

    /**
     * 播放动画
     * @override
     * @param animName 动画名
     * @param loop 是否循环播放
     */
    protected playAnimation(animName: string, loop: boolean) {
        if (animName) {
            this._spine.setAnimation(0, animName, loop);
        }
        else {
            this._spine.clearTrack(0);
        }
    }

    /**
     * 缩放动画播放速率
     * @override
     * @param scale 缩放倍率
     */
    protected scaleTime(scale: number) {
        if (scale > 0)
            this._spine.timeScale = scale;
    }

    /**
     * 注册次状态机动画结束的回调（状态机内部方法，不能由外部直接调用）
     */
    public addSecondaryListener(cb: (entry?: any) => void, target: AnimatorSpineSecondary) {
        this._secondaryListenerMap.set(cb, target);
    }

    /**
     * 注册动画完成时的监听
     * @param cb 回调
     * @param target 调用回调的this对象
     */
    public addCompleteListener(cb: (entry?: any) => void, target: any = null) {
        if (this._completeListenerMap.has(cb)) {
            return;
        }
        this._completeListenerMap.set(cb, target);
    }

    /**
     * 注销动画完成的监听
     * @param cb 回调
     */
    public removeCompleteListener(cb: (entry?: any) => void) {
        this._completeListenerMap.delete(cb);
    }

    /**
     * 清空动画完成的监听
     */
    public clearCompleteListener() {
        this._completeListenerMap.clear;
    }
}
