type DSMSClass = DSModuleS<any, Subdata>;
namespace DSDefine {
    /**当前版本，游戏的版本号 */
    export const curVersion ="v1.0" //SystemUtil.getVersion();
    export const DE_INIT = 'DE_INIT';
    export const DE_DEL = 'DE_DEL';
    export const DE_PING = 'DE_PING';
    export const DE_DSLIST = 'DE_DSLIST';
    export const DE_SYNCMSG = 'DE_SYNCINFO';
    export const DSPingTime = 3;
    let msgIdx = 0;
    export function getMsgIdx() {
        return ++msgIdx;
    }
}
interface DSInfo {
    dsId: string,
    createTime: number,
    isHost: boolean;
}
interface DSMsgInfo {
    type: number,
    /**消息唯一ID */
    msgId: string,
    /**处理模块 */
    module: string,
    /**主机处理 */
    hosted: boolean,
    /**数据 */
    data: any,
    version: string
}
class DSMgr {
    /**所有同场景的DS信息 */
    private static dsList: Map<string, DSInfo> = new Map<string, DSInfo>();
    /**DS中注册了的全局模块 */
    private static dsModules: Map<string, DSMSClass> = new Map<string, DSMSClass>();
    /**广播收到的消息 */
    private static msgList: DSMsgInfo[] = [];
    /**转发列表 */
    private static sendList: string[] = [];
    /**当前DS信息 */
    public static curInfo: DSInfo;
    /**主机ID */
    private static hostId: string;
    /**选主机超时时间 */
    private static timeOut: number = 10;
    /**主机ping */
    private static pingTime: number = 3;
    private static init() {
        if (DSMgr.curInfo)
            return;
        DSMgr.curInfo = {
            createTime: TimeUtil.time(),
            dsId: SystemUtil.roomId,
            isHost: false
        }
        DSMgr.timeOut = DSDefine.DSPingTime + 2;
        DSMgr.dsList.set(DSMgr.curInfo.dsId, DSMgr.curInfo);
        Event.addSceneEventListener(DSDefine.DE_INIT, DSMgr.onInit);
        Event.addSceneEventListener(DSDefine.DE_DSLIST, DSMgr.onInitList);
        Event.addSceneEventListener(DSDefine.DE_PING, DSMgr.onPing);
        Event.addSceneEventListener(DSDefine.DE_SYNCMSG, DSMgr.onMsg);
        Event.addSceneEventListener(DSDefine.DE_DEL, DSMgr.onDel);
        setInterval(DSMgr.update, 100);

        //Event.dispatchSceneEvent(DSDefine.DE_INIT, JSON.stringify(DSMgr.curInfo));
        Player.onPlayerRemove.add(() => {
            let ps = Player.getAllPlayers();
            //最后一个玩家退出,这还有一个，但PIE不执行???
            if (ps.length < 2)
                Event.dispatchSceneEvent(DSDefine.DE_DEL, DSMgr.curInfo.dsId);
        })
        Player.onPlayerAdd.add(() => {
            let ps = Player.getAllPlayers();
            if (ps.length < 2)
                Event.dispatchSceneEvent(DSDefine.DE_INIT, JSON.stringify(DSMgr.curInfo));
        })
    }
    public static register(ms: DSMSClass) {
        DSMgr.init();
        DSMgr.dsModules.set(ms.moduleName, ms);
    }
    /**
     * 给所有DS发送数据
     * @param data 数据
     * @param ignoreVer 忽略版本 
     */
    public static sendMsg(str: string) {
        DSMgr.sendList.push(str);
        if (DSMgr.sendList.length > 20)
            LogMgr.typeError('sendMsg is more than 20', DSMgr.sendList.length);
    }
    /**添加一个DS */
    private static onInit = (str: string) => {
        let info = JSON.parse(str) as DSInfo;
        DSMgr.dsList.set(info.dsId, info);

        let list = []
        for (let v of DSMgr.dsList.values()) {
            list.push(v);
        }
        let json = JSON.stringify(list);
        // if (DSMgr.curInfo.isHost)
        Event.dispatchSceneEvent(DSDefine.DE_DSLIST, json);
    }
    /**同步所有DS */
    private static onInitList = (str: string) => {
        let list = JSON.parse(str) as DSInfo[];
        // console.error('onInitList', list.length, str);
        DSMgr.dsList.clear();
        for (let i = 0; i < list.length; i++) {
            let info = list[i];
            DSMgr.dsList.set(info.dsId, info);
        }
    }
    private static onDel = (dsId: string) => {
        let info = DSMgr.dsList.get(dsId);
        if (!info)
            return;
        DSMgr.dsList.delete(dsId);
        if (this.hostId != dsId)
            return;
        if (this.curInfo.dsId == dsId) {
            for (let v of DSMgr.dsModules.values()) {
                v.onCancelHost();
            }
        }
        this.getHost();
    }
    /**
     * 收到主机ping
     * @param hid 主机id
     * @returns 
     */
    private static onPing = (hid: string) => {
        DSMgr.timeOut = DSDefine.DSPingTime + 2;
        if (DSMgr.hostId == hid)
            return;
        DSMgr.setHost(hid);
        if (DSMgr.curInfo.isHost) {
            //防报错阻断
            try {
                for (let v of DSMgr.dsModules.values()) {
                    v.onBecomeHost();
                }
            }
            catch (e) {
                console.error("DSModule", e);
            }
        }
    }

    private static setHost(hid: string) {
        DSMgr.hostId = hid;
        DSMgr.curInfo.isHost = DSMgr.curInfo.dsId == hid;
        for (let v of DSMgr.dsList.values()) {
            v.isHost = v.dsId == hid;
        }
    }
    /**
     * 收到消息 
     * @param data 消息内容
     */
    private static onMsg = (data: string) => {
        let info = JSON.parse(data) as DSMsgInfo;
        if (!info?.msgId)
            return;
        DSMgr.msgList.push(info);
    }
    /**
     * check
     */
    private static update = () => {
        if (DSMgr.curInfo.isHost) {
            DSMgr.pingTime -= 0.1;
            if (DSMgr.pingTime <= 0) {
                Event.dispatchSceneEvent(DSDefine.DE_PING, DSMgr.hostId);
            }
        }
        else {
            //不是主机才检测超时
            DSMgr.timeOut -= 0.1;
            if (DSMgr.timeOut < 0) {
                DSMgr.getHost();
            }
        }
        DSMgr.dispatchMsg();
        DSMgr.decodeMsg();
    }
    /**
     * 获取新主机
     */
    private static getHost() {
        if (DSMgr.hostId)
            DSMgr.dsList.delete(DSMgr.hostId);
        DSMgr.hostId = null;
        let hid = this.curInfo.dsId;
        let min = TimeUtil.time();
        for (let [k, v] of DSMgr.dsList) {
            if (v.createTime < min) {
                min = v.createTime;
                hid = k;
            }
        }
        DSMgr.timeOut = DSDefine.DSPingTime + 2;
        DSMgr.pingTime = 0;
        DSMgr.onPing(hid);
    }
    private static dispatchMsg() {
        if (DSMgr.sendList.length < 1)
            return;
        //目前一次发送1个
        let str = DSMgr.sendList.shift();
        Event.dispatchSceneEvent(DSDefine.DE_SYNCMSG, str);
    }
    private static decodeMsg() {
        if (DSMgr.msgList.length < 1)
            return;
        let info = DSMgr.msgList.shift();
        if (info.hosted && !DSMgr.curInfo.isHost)
            return;
        if (info.version && info.version != DSDefine.curVersion)
            return;
        let module = DSMgr.dsModules.get(info.module);
        module.onMsg(info.type, info.data);
    }
}


/**
 * 全DS事件通知模块基类
 */
export class DSModuleS<T, S extends Subdata> extends ModuleS<T, S> {
    /**是否为主机 */
    protected get isHost(): boolean {
        return DSMgr.curInfo?.isHost;
    }
    /**模块名，可自定义，保证全DS唯一 */
    public get moduleName(): string {
        return this.constructor.name;
    }
    /**
     * 子模块记得要调用super.onStart()
     */
    protected onStart(): void {
        super.onStart();
        DSMgr.register(this);
    }
    /**
     * 成为主机时调用
     */
    public onBecomeHost(): void {
    }
    /**
     * 被取消主机,主房间没人了，会触发，暂无其他触发方式
     */
    public onCancelHost(): void {
    }
    /**
     * 发送消息
     * @param type 自定义类型,方便接到消息判断
     * @param data 数据
     * @param ignoreVer 忽略版本
     * @param beHost 主机才收到
     */
    protected sendMsg(type: number, data: string, ignoreVer: boolean = true, beHost: boolean = false): void {
        if (!DSMgr.curInfo) {
            console.error('DSModuleS not inited, onStart?');
            return;
        }
        let ver = ignoreVer ? null : DSDefine.curVersion;
        let msg: DSMsgInfo = {
            type: type,
            msgId: this.getMsgId(),
            data: data,
            module: this.moduleName,
            hosted: beHost,
            version: ver
        }
        let str = JSON.stringify(msg);
        DSMgr.sendMsg(str);
    }
    /**
     * 接到全DS消息
     * @param type 自定义消息类型
     * @param data 内容
     */
    public onMsg(type: number, data: string): void {

    }
    /**
     * 当前房间全DS消息序列号
     * @returns 消息id
     */
    private getMsgId() {
        return DSMgr.curInfo.dsId + DSDefine.getMsgIdx();
    }
}

