import { Component } from "cc";

/***
 * 事件管理器
 * 事件可一对一也可一对多
 */
export class EventManager {
    //事件
    private _events = new Map<string, { id: string, func: Function, target: any }[]>();
    //事件ID缓存
    private _targets = new Set<string>();

    //只触发一次事件
    private _onceEvents = new Map<string, { id: string, func: Function, target: any }[]>();
    //只触发一次事件ID缓存
    private _onceTargets = new Set<string>();

    constructor() {
        this._events.clear();
        this._targets.clear();

        this._onceEvents.clear();
        this._onceTargets.clear();
    }

    /**
     * 添加事件
     * @param name 事件名
     * @param callback 回调函数
     * @param target 回调目标
     * @param uuid 事件唯一ID,如果在组件调用不需要该值
     */
    addEvent(name: string, callback: (...val: any[]) => void, target: any, uuid: string = null) {
        if (!name || !callback || !target) {
            console.error("添加事件失败name,callback或者target为空.");
            return false;
        }
        if (!this._events.has(name)) {
            this._events.set(name, []);
        }
        if (!uuid) {
            uuid = target.node.uuid;
        }
        this._events.get(name).push({ id: uuid, func: callback.bind(target), target: target });

        if (!this._targets.has(uuid)) {
            this._targets.add(uuid);
        }
        return true;
    }

        /**
     * 只触发一次事件
     * @param name 事件名
     * @param callback 回调函数
     * @param target 回调目标
     * @param uuid 事件唯一ID,如果在组件调用不需要该值
     */
            onceEvent(name: string, callback: (...val: any[]) => void, target: any, uuid: string = null) {
                if (!name || !callback || !target) {
                    console.error("添加事件失败name,callback或者target为空.");
                    return false;
                }
                if (!this._onceEvents.has(name)) {
                    this._onceEvents.set(name, []);
                }
                if (!uuid) {
                    uuid = target.node.uuid;
                }
                this._onceEvents.get(name).push({ id: uuid, func: callback.bind(target), target: target });
        
                if (!this._onceTargets.has(uuid)) {
                    this._onceTargets.add(uuid);
                }
                return true;
            }

    /**
     * 发射事件
     * @param name 事件名
     * @param val 传递的数据
     */
    fireEvent(name: string, ...val: any[]) {
        let event = this._events.get(name);
        if (event) {
            for (let i = event.length - 1; i >= 0; --i) {
                if (event[i].target instanceof Component) {
                    if (event[i].target.isValid) {
                        event[i].func(...val);
                    } else {
                        event.splice(i, 1);
                    }
                } else {
                    event[i].func(...val);
                }
            }
        }

        let onceEvent = this._onceEvents.get(name);
        if (onceEvent) {
            for (let i = onceEvent.length - 1; i >= 0; --i) {
                if (onceEvent[i].target instanceof Component) {
                    if (onceEvent[i].target.isValid) {
                        onceEvent[i].func(...val);
                    } 
                } else {
                    onceEvent[i].func(...val);
                }
            }
            this._onceEvents.delete(name);
        }
    }

    /**
     * 移除组件所有事件
     * @param target 目标组件或事件的唯一标识
     */
    removeEvent(target: Component | string) {
        let uuid = (typeof target === "string") ? target : target?.node?.uuid;
        if (this._targets.has(uuid)) {
            this._targets.delete(uuid);
            this._events.forEach((events, name) => {
                for (let i = events.length - 1; i >= 0; --i) {
                    if (events[i].id === uuid) {
                        events.splice(i, 1);
                    }
                }
                if (events.length === 0) {
                    this._events.delete(name);
                }
            });
        }

        if (this._onceTargets.has(uuid)) {
            this._onceTargets.delete(uuid);
            this._onceEvents.forEach((events, name) => {
                for (let i = events.length - 1; i >= 0; --i) {
                    if (events[i].id === uuid) {
                        events.splice(i, 1);
                    }
                }
                if (events.length === 0) {
                    this._onceEvents.delete(name);
                }
            });
        }
    }
    

    removeAll() {
        this._events.clear();
        this._targets.clear();

        this._onceEvents.clear();
        this._onceTargets.clear();
    }
}

export let EventMgr = new EventManager;

/**
 * 如果在组件上添加事件则所有的target为this
 * addEvent("Level_Up",callback,this)
 * 移除组件所有的事件,如果组件为UIMgr管理则会自动调用
 * removeEvent(this)
 * 
 * 如果在非组件上监听事件
 * addEvent("Level_Up",callback,this,only_uuid)
 * 移除事件
 * removeEvent(only_uuid)
 */