import AppFacade from "../AppFacade";

import { EventConst } from "../ConstDefine/EventConst";
import ViewComp from "../ViewComp/ViewComp";
import { ViewType } from "../ConstDefine/ViewConst";
import IView from "../Interface/IView";
import AssetMgr from "../Manager/AssetMgr";

/**
 * @param 所有对象的父类
 */
export default class View implements IView {

    //【禁止使用】类名会因为编译修改
    private getClsName(): string {
        return this.constructor.name
    }

    private static gIndex=0
    private instanceId:number=0
    public get InstanceId():number{
        if(!this.instanceId){
            this.instanceId=++View.gIndex
        }
        return this.instanceId
    }

    public getTypeName(): string { return "View" }
    public getViewType():ViewType{ return ViewType.None}

    public get node(): cc.Node { return this.rootNode }
    private rootNode: cc.Node
    protected isOpen: boolean
    public get IsOpen(): boolean { return this.isOpen }
    protected resName: string = ""
    public get ResName(): string { return this.resName }
    protected abPath: string = ""
    
    protected isStarLoad:boolean=false
    public get IsStartLoad():boolean{return this.isStarLoad}
    protected isLoadFinish: boolean = false //用来判断是否加载完
    public get IsLoadFinish():boolean{return this.isLoadFinish}
    

    private viewComps:Map<typeof ViewComp,ViewComp>

    //子类写以下方法
    protected setLayer(): void { }
    protected async getChilds() { }
    protected async onUpdateData(data?: Object) { }
    protected onOpen(): void { }
    protected onClose(): void { }

    /**是否可以设置active */
    public isCanActiveClose:boolean=true
    public isCanActiveOpen:boolean=true


    public async loadViewAsync(abPath: string, resName: string) {
        this.isStarLoad=true
        return new Promise(async (resolve)=>{
            this.resName = resName
            this.abPath = abPath
            try{
                let prefab = await AppFacade.getMgr(AssetMgr).loadAssetAsync<cc.Prefab>(abPath, resName, cc.Prefab)
                this.rootNode = cc.instantiate(prefab)
                await this.loadViewFinish()
                resolve(this)
            }catch(err){
                console.log(err)
            }
        })
    }

    public async loadViewClone(prefab: cc.Node, instantiate: boolean)
    public async loadViewClone(prefab: cc.Prefab)
    public async loadViewClone(prefab: any, instantiate: boolean = true) {
        this.isStarLoad=true
        return new Promise(async (resolve)=>{
            if (!prefab) {
                console.error("loadViewClone prefab is null")
                return
            }
            this.rootNode = instantiate && cc.instantiate(prefab) || prefab
            await this.loadViewFinish()
            resolve(this)
        })
    }

    protected async loadViewFinish(){
        this.setLayer()
        await this.getChilds()
        this.viewComps?.forEach((comp)=>comp.getChilds())
        this.isLoadFinish = true
    }

    public isRootView(): boolean { return cc.isValid(this.node) }
    public isParentView(): boolean { return this.isRootView() && cc.isValid(this.node.parent) }

    //防止没加载完成的时候多次调用
    public async updateData(data?: Object) {
        if (this.isLoadFinish == false)
            return
        await this.onUpdateData(data)
        this.viewComps?.forEach((comp)=>comp.updateData(data))
    }

    public open(): void {
        if (this.isLoadFinish==false||this.isOpen==true) return
        this.isOpen = true
        this.viewComps?.forEach((comp)=>comp.open())
        if(this.isCanActiveOpen&&this.isRootView()){
            this.node.active=true
        }
        this.isParentView() && this.node.setSiblingIndex(this.node.parent.childrenCount)
        this.emitEvent(EventConst.OpenView)
        this.onOpen()
    }

    protected emitEvent(event:string){
        cc.systemEvent.emit(event,this.getTypeName(),this.getViewType())
    }

    public close(): void {
        if (this.isOpen==false) return
        this.isOpen = false
        this.viewComps?.forEach((comp)=>comp.close())
        if(this.isCanActiveClose&&this.isRootView()){
            this.node.active=false
        }
        this.onClose()
        this.emitEvent(EventConst.CloseView)
    }

    /**
     * 事件在loop中释放了
    */
    public release(): void {
        this.onRelsese()
        this.isLoadFinish = false
        this.node?.destroy()
        this.viewComps?.forEach((comp)=>comp.release())
        AppFacade.getMgr(AssetMgr).releaseAsset(this.abPath,this.resName)
    }

    protected onRelsese(){}

    public setActive(active:boolean){
        this.isRootView()&&(this.node.active=active)
    }

    public setParent(parent: cc.Node) {
        this.isRootView() && (this.node.parent = parent)
    }

    public setPosition(newPosOrX: cc.Vec2 | cc.Vec3) {
        this.isRootView() && (this.node.setPosition(newPosOrX))
    }

    public setRotation(rotation: number) {
        this.isRootView() && (this.node.angle = -rotation)
    }

    public getWorldPos(): cc.Vec3 {
        let res = cc.Vec3.ZERO
        if (this.isParentView()) {
            res = this.node.parent.convertToWorldSpaceAR(this.node.position)
        }
        return res
    }

    public setWorldPos(worldPos: cc.Vec3) {
        if (this.isParentView()) {
            this.node.position = this.node.parent.convertToNodeSpaceAR(worldPos)
        }
    }

    protected addViewComp(type:typeof ViewComp):ViewComp{
        if(!this.viewComps){
            this.viewComps=new Map()
        }
        let c=this.viewComps.get(type)
        if(!c){
            c=new type(this)
            this.viewComps.set(type,c)
        }
        return c
    }

    public getViewComp<T extends ViewComp>(type:typeof ViewComp):T{
        if(!this.viewComps) return
        let comp=this.viewComps.get(type)
        return <T>comp
    }

    protected getChildNode(url: string): cc.Node {
        if (!this.isRootView()) return null
        if (url === "") return this.node
        return cc.find(url, this.node)
    }

    protected getChildComp<T extends cc.Component>(url: string, type: { prototype: T }): T {
        if (!this.isRootView()) return null
        let node = this.getChildNode(url)
        !node && (cc.error(this.resName, url, "not exit", "getChildComp"))
        return node && node.getComponent(type)
    }

    protected getChildComps<T extends cc.Component>(url: string, type: { prototype: T }): T[] {
        if (!this.isRootView()) return null
        let node = this.getChildNode(url)
        !node && (cc.error(this.resName, url, "not exit", "getChildComp"))
        return node && node.getComponentsInChildren(type)
    }

    protected addCCComp<T extends cc.Component>(node: cc.Node, type: new () => cc.Component): T {
        if (!node) return null
        let res = node.getComponent(type)
        if (!res) {
            res = node.addComponent(type) as T
        }
        return res
    }

    protected setName(name: string) {
        this.isRootView() && (this.node.name = name)
    }

    public getPathInLayer():string{
        if(!this.isRootView()) return ""
        let p=this.node.parent
        let res=this.node.name
        while(cc.isValid(p)){
            res=p.name+"/"+res
            p=p.parent
        }
        return res
    }


}