import { AppConst } from "../ConstDefine/AppConst";
import { EventConst } from "../ConstDefine/EventConst";
import { ILimitFunc } from "../Interface/FuncInterface";
import Manager from "./Manager";


/**
 * @param js自带的interval和timeout不好管理容易造成内存泄露尽量不要使用
*/


class TimeoutInfo {
    constructor(public func: Function, public timer: number, public id: number = -1) { }
}

class InterInfo {
    constructor(public func: Function, public interval: number, public id: number = -1, public timer = 0) { }
}

class DelayInfo {
    constructor(public func: Function, public couter: number = 0) { }
}

class LimitInfo {
    constructor(public iterator: ILimitFunc, public arr: Array<any>, public limit: number, public resolve: any, public reject: any, public running: number = 0) { }
}

export default class LoopMgr extends Manager {
    static get TypeName(): string { return "LoopMgr" }
    private updateDic: Map<number, Function> = new Map()
    private fixedDic: Map<number, Function> = new Map()
    private lateUpdateDic: Map<number, Function> = new Map()
    private timeoutDic: Map<number, TimeoutInfo> = new Map()
    private intervalDic: Map<number, InterInfo> = new Map()
    private delayOneFrames: Array<DelayInfo> = new Array()
    private updateFramer: number = 0
    private fixedNowTime: number = 0

    private limitDic: Map<number, LimitInfo> = new Map()
    private deleteLimits: Array<number> = new Array()

    private framer: number = 0
    public get Frame(): number { return this.framer }

    private static LoopIndex: number = 0

    async start() {
        console.log("LoopMgr onStart")
    }

    public release() {
        console.log("LoopMgr onRelease")
        this.updateDic.clear()
        this.lateUpdateDic.clear()
        this.fixedDic.clear()
        this.limitDic.clear()

        for (let v of this.intervalDic) {
            if (v[1].id != -1) {
                clearInterval(v[1].id)
            }
        }
        for (let v of this.timeoutDic) {
            if (v[1].id != -1) {
                clearTimeout(v[1].id)
            }
        }
        this.intervalDic.clear()
        this.timeoutDic.clear()

        LoopMgr.LoopIndex = 0
        this.clearEvents()
        cc.Tween.stopAll()
        
        this.updateFramer = 0
        this.fixedNowTime = 0
    }

    public update(): void {
        this.framer = (++this.framer) % Number.MIN_SAFE_INTEGER

        //fixedupdate
        this.fixedNowTime += cc.director.getDeltaTime()
        let delta = AppConst.GameFrameRate
        while (this.fixedNowTime > delta) {
            this.fixedDic.forEach((v) => { v && v() })
            this.fixedNowTime -= delta
        }

        //update
        if (this.isCanUpdate()) {
            this.updateDic.forEach((v, key) => { v && v() })

            this.runLimit()
        }
    }

    public lateUpdate(): void {
        //lateupdate不优化，一定每帧执行的才放这里
        this.lateUpdateDic.forEach((v) => { v() })

        let v: DelayInfo = null
        for (let i = this.delayOneFrames.length - 1; i >= 0; --i) {
            v = this.delayOneFrames[i]
            if (v.couter >= 1) {
                v.func && v.func()
                this.delayOneFrames.splice(i, 1)
            }
        }
        this.delayOneFrames.forEach((v) => { v.couter++ })
    }

    //-----------------------------公开外部方法start--------------------------------

    /**
     * 分帧执行await async第一次会等待回调返回，之后就直接执行
     * @param arr 
     * @param iterator 每帧如何迭代arr
     * @param limit 每帧最多执行多少次
     */
    public async eachLimit(arr: Array<any>, iterator: ILimitFunc, limit: number) {
        return new Promise((resolve, reject) => {
            let info = new LimitInfo(iterator, arr, limit, resolve, reject)
            this.limitDic.set(LoopMgr.LoopIndex++, info)
        })
    }

    public addUpdate(func: Function): number {
        let id = LoopMgr.LoopIndex++
        this.add(id, func, this.updateDic)
        return id
    }

    public remvoeUpdate(id: number) {
        this.remove(id, this.updateDic)
    }

    public addFixedUpdate(func: Function): number {
        let id = LoopMgr.LoopIndex++
        this.add(id, func, this.fixedDic)
        return id
    }

    public removeFixed(id: number) {
        this.remove(id, this.fixedDic)
    }

    public addLateUpdate(func: Function): number {
        let id = LoopMgr.LoopIndex++
        this.add(id, func, this.lateUpdateDic)
        return id
    }

    public removeLateUpdate(id: number) {
        this.remove(id, this.lateUpdateDic)
    }

    public setTimeout(func: Function, second: number): number {
        let tinfo = new TimeoutInfo(func, second)
        tinfo.id = setTimeout(func, second * 1000)
        this.timeoutDic.set(tinfo.id, tinfo)
        return tinfo.id
    }

    /**
     * 等待多少秒，0会间隔一帧
     * 用于协程
    */
    public async waitForTime(second: number) {
        return new Promise<number>((resolve) => {
            let id=this.setTimeout(() => {
                resolve(id)
            }, second)
        })
    }

    public clearTimeout(id: number) {
        if (id < 0) return
        clearTimeout(id)
        if(this.timeoutDic.has(id)){
            this.timeoutDic.delete(id)
        }
    }

    public clearInterval(id: number) {
        if (id <0) return 
        if(this.intervalDic.has(id)){
            this.intervalDic.delete(id)
        }else{
            console.error("clearInterval 不存在的id")
        }
        clearInterval(id)
    }

    public setInterval(func: Function, second: number) {
        let tinfo = new InterInfo(func, second)
        tinfo.id = setInterval(func, second * 1000)
        this.intervalDic.set(tinfo.id, tinfo)
        return tinfo.id
    }

    public delayOneFrame(func: Function) {
        this.delayOneFrames.push(new DelayInfo(func))
    }

    //-----------------------------公开外部方法start--------------------------------

    private add(id: number, func: Function, map: Map<number, Function>) {
        if (map.has(id)) return
        map.set(id, func)
    }

    private remove(id: number, map: Map<number, Function>) {
        map.delete(id)
    }

    private clearEvents() {
        for (let v in EventConst) {
            cc.systemEvent.removeAll(v)
        }
    }

    /**
     * 隔帧执行
     */
    private isCanUpdate(): boolean {
        if (AppConst.GameFrameInterval <= 0) return true

        this.updateFramer--
        if (this.updateFramer <= 0) {
            this.updateFramer = AppConst.GameFrameInterval
            return true
        }
        return false
    }

    /**
    * await async在分帧没意义，await卡几十帧，之后一帧就被执行完了
    */
    private runLimit() {
        this.deleteLimits.length = 0
        for (const [key, info] of this.limitDic.entries()) {
            let i = 0
            while (info.running < info.arr.length && i < info.limit) {
                info.iterator(info.arr[info.running], info.running)
                info.running++
                i++
            }
            if (info.running >= info.arr.length) {
                this.deleteLimits.push(key)
                info.resolve()
            }
        }
        this.deleteLimits.forEach((key) => {
            this.limitDic.delete(key)
        })
    }
}