import { StringMap } from "../../Collections/StringMap";
import Config from "../../Config";
import { gameMethod } from "../../common/gameMethod";
import GameDataCenter from "../../data/GameDataCenter";
import { GUIEvent } from "../../data/const/EventConst";
import { PlatFormDevType } from "../../data/const/TypeConst";
import { ViewZOrder } from "../../data/const/ViewZOrder";
import EventMng from "../../manager/EventMng";
import { Singleton } from "../../manager/Singleton";
import AssetsBundleMgr from "../../utils/AssetsBundleMgr";
import { PromiseDefault } from "../Promise/SpecialPromise";
import { ResCollector } from "../compment/ResCollector";
import { ResKeeper } from "../compment/ResKeeper";
import FguiLoadMgr from "./FguiLoadMgr";
import { IDisposable } from "./Interface/IDisposable";
import { ILateUpdate } from "./Interface/ILateUpdate";
import { IUpdate } from "./Interface/IUpdate";
import { FguiClass, FguiView, ViewType } from "./mvc/FguiView";


export default class FguiMgr extends Singleton<FguiMgr>() implements IUpdate, ILateUpdate, IDisposable {
    /** 记录图层节点的Map */
    private _layerNodeMap: Map<ViewZOrder, fgui.GComponent> = new Map();
    /** 已开启过的ui */
    private _opened: StringMap<FguiView> = new StringMap<FguiView>();
    /** 缓存被关闭的ui */
    private _closed: StringMap<FguiView> = new StringMap<FguiView>();
    /** 记录所加载的包 */
    private _addPkged: StringMap<number> = new StringMap<number>();


    Init(): void {
        fgui.GRoot.create();

        //设置默认字体
        AssetsBundleMgr.loadBundle("commonView", (error, bundle) => {
            if (error) {
                console.error("load font bundle fail:", error)
                return
            }
            bundle.load("font/Alimama_ShuHeiTi_Bold", cc.Font, (error, assset) => {
                if (error) {
                    console.error("load font fail:", error)
                    return
                }
                fgui.registerFont('Alimama_ShuHeiTi_Bold', assset);
                fgui.UIConfig.defaultFont = 'Alimama_ShuHeiTi_Bold'
            })
        })

        fgui.GRoot.inst.width = Config.safeAreaRect.width
        fgui.GRoot.inst.height = Config.realHeight
        fgui.GRoot.inst.node.y = Config.realHeight + Config.safeAreaRect.y / 2

        this._layerNodeMap = new Map();
        // 创建所有UI层级
        Object.values(ViewZOrder).forEach((layer) => {
            if (typeof layer === 'number') {
                const layerName = ViewZOrder[layer];
                this.createLayer(layerName + 'Layer', layer);
            }
        });
    }


    private createLayer(name: string, zOrder: number): fgui.GComponent {
        const layer = new fgui.GComponent();
        layer.name = name;
        layer.node.name = name;
        layer.sortingOrder = zOrder;
        fgui.GRoot.inst.addChild(layer);
        layer.makeFullScreen();
        this._layerNodeMap.set(zOrder, layer);
        return layer;
    }


    /**
     * @param callback 打开完毕回调函数
     * @param intent 传入到UI的参数
    */
    public openUI<T extends FguiView>(uiClass: FguiClass<T>, zOrder: number = ViewZOrder.UI, callback?: Function, intent?: any): Promise<void> {
        if (CC_PREVIEW) console.log(`打开页面${uiClass.getViewName()}`);
        const viewKey = uiClass.getViewName();
        if (this._opened.ContainsKey(viewKey)) {
            console.log(`FguiMgr::Open ${viewKey} has already opened.`);
            return;
        }
        if (this.getCacheView(uiClass, zOrder, callback)) {
            return;
        }
        let pkgName = uiClass.getPkgName();
        EventMng.emit(GUIEvent.SHOW_MASK, pkgName, true);
        if (fgui.UIPackage.getByName(pkgName) == null) {
            // 加载uipackage
            FguiLoadMgr.loadPackage(pkgName, pkgName, async (error, pkg) => {
                if (error) {
                    EventMng.emit(GUIEvent.SHOW_MASK, pkgName, false)
                    console.error(`FguiMgr LoadPackage[${pkgName}] error: ${error}`);
                    return;
                }
                this.CreateView(uiClass, zOrder, intent, callback);
            });
        }
        else {
            this.CreateView(uiClass, zOrder, intent, callback);
        }
    }

    /** 从缓存中加载页面 */
    private getCacheView<T extends FguiView>(uiClass: FguiClass<T>, zOrder: number = ViewZOrder.UI, callback?: Function, intent?: any): boolean {
        const viewKey = uiClass.getViewName();
        if (this._closed.ContainsKey(viewKey)) {
            const uiview = this._closed.Value(viewKey);
            if (uiview.viewType == ViewType.Full) {
                this._opened.Foreach((key: string, value: FguiView) => {
                    if (value.viewType != ViewType.Part && value.zOrder <= uiview.zOrder) {
                        value.addFullHideCount(1);
                    }
                })
            }
            this._closed.Remove(viewKey);
            this._opened.Add(viewKey, uiview);
            this.AddPanel(uiview.Panel, zOrder);
            uiview.show(intent);
            uiview.zOrder = zOrder;
            uiview.uiPkgName = uiClass.getPkgName();
            //回调
            callback && callback(uiview);
            return true;
        }
        return false;
    }


    private CreateView<T extends FguiView>(uiClass: FguiClass<T>, zOrder: number = ViewZOrder.UI, intent?: any, complete?: Function) {
        let handler = new fgui.AsyncOperation();
        handler.callback = async (gObject) => {
            if (this._opened.ContainsKey(uiClass.getViewName())) {
                EventMng.emit(GUIEvent.SHOW_MASK, uiClass.getPkgName(), false)
                console.error(`FguiMgr::Open ${uiClass.getViewName()} has already opened.`);
                return;
            }
            let panel = gObject as fgui.GComponent;
            if (panel == null) {
                EventMng.emit(GUIEvent.SHOW_MASK, uiClass.getPkgName(), false)
                console.error(`FguiMgr createObject[${uiClass.getViewName}] error: GObject为空.`);
                return;
            }
            panel.node.name = uiClass.getPrefabName();
            let uiview = new uiClass();
            uiview.zOrder = zOrder;
            uiview.uiPkgName = uiClass.getPkgName();
            uiview.initForward(panel, null);
            const resKeeper = ResKeeper.Get(panel.node, true);
            let resCollector = new ResCollector();
            uiview.Controller.onCollectRes(resCollector, intent);
            if (resCollector.GetResList().length > 0) {
                await resKeeper.LoadResCollector(resCollector, PromiseDefault);
            }
            if (uiview.viewType == ViewType.Full) {
                this._opened.Foreach((key: string, value: FguiView) => {
                    if (value.viewType != ViewType.Part && value.zOrder <= uiview.zOrder) {
                        value.addFullHideCount(1);
                    }
                })
            }
            this._opened.Add(uiClass.getViewName(), uiview);
            uiview.init();
            this.AddPanel(panel, zOrder);
            uiview.show(intent);
            this.AddPkgCount(uiClass.getPkgName(), uiClass.getViewName());

            EventMng.emit(GUIEvent.SHOW_MASK, uiClass.getPkgName(), false)
            complete && complete(uiview);
        };
        handler.createObject(uiClass.getPkgName(), uiClass.getPrefabName());
    }



    /**
     * @param isDispose 是否销毁
    */
    public closeUI<T extends FguiView>(uiClass: FguiClass<T>, isDispose: boolean = false) {
        const viewKey = uiClass.getViewName();
        if (this._opened.ContainsKey(viewKey)) {
            const view = this._opened.Value(viewKey);
            this._opened.Remove(viewKey);
            view.hide(isDispose);
            this.RemovePanel(view.Panel);
            if (isDispose) {
                this.DestroyView(view, uiClass);
            }
            else {
                this._closed.Add(viewKey, view);
            }
            if (view.viewType == ViewType.Full) {
                this._opened.Foreach((key: string, value: FguiView) => {
                    if (value.viewType != ViewType.Part && value.zOrder <= view.zOrder) {
                        value.addFullHideCount(-1);
                    }
                })
            }
        }
        else if (isDispose && this._closed.ContainsKey(viewKey)) {
            const view = this._closed.Value(viewKey);
            this._closed.Remove(viewKey);
            this.DestroyView(view, uiClass);
        }
    }
    // 判断界面是否打开
    public isShowing<T extends FguiView>(uiClass: FguiClass<T>) {
        const viewKey = uiClass?.getViewName();
        if (this._opened.ContainsKey(viewKey)) {
            return true
        }
        return false
    }
    // 获取打开的界面
    public getShowingView<T extends FguiView>(uiClass: FguiClass<T>) {
        const viewKey = uiClass?.getViewName();
        if (this._opened.ContainsKey(viewKey)) {
            const view = this._opened.Value(viewKey);
            return view
        }
        return null
    }
    /**
     * 当前是否有除了指定页面以外的其他页面被打开
     */
    public checkOtherUIOpen(uilist: FguiClass<any>[]) {
        let list = []
        for (let i of uilist) {
            list.push(i.getViewName())
        }
        let haveother: boolean = false
        this._opened.Foreach((key: string, value: FguiView) => {
            if (list.indexOf(key) < 0) {
                haveother = true
            }
        })
        return haveother
    }


    public isShowingByName(uiViewName: string) {
        return this._opened.ContainsKey(uiViewName);
    }
    // 获取打开的界面
    public getShowingViewByName(uiViewName: string) {
        if (this._opened.ContainsKey(uiViewName)) {
            const view = this._opened.Value(uiViewName);
            return view
        }
        return null
    }
    public findNodeByName(uiViewName: string, goName: string): cc.Node {
        if (this._opened.ContainsKey(uiViewName)) {
            let view = this._opened.Value(uiViewName);
            let goNames = goName.split("/")
            let go = null
            let goTemp = view["_viewModel"][goNames[0]];
            if (gameMethod.isEmpty(goTemp)) {
                goTemp = view.Panel.getChild(goNames[0]);
            }
            for (let i = 1; i < goNames.length; i++) {
                if (goTemp?.Panel) {
                    goTemp = goTemp.Panel?.getChild(goNames[i])
                } else {
                    goTemp = goTemp?.getChild(goNames[i]);
                }
            }
            go = goTemp
            if (!gameMethod.isEmpty(go)) {
                go.visible = true
            } else {
                console.error(`界面：${uiViewName} 找不到目标节点: ${goNames}`)
            }
            return go?.node ?? null;
        }
        return null;
    }

    public removePkg<T extends FguiView>(uiClass: FguiClass<T>) {
        if (!this._addPkged.ContainsKey(uiClass.getPkgName()) || this._addPkged.Value(uiClass.getPkgName()) == 0) {
            fgui.UIPackage.removePackage(uiClass.getPkgName())
        } else {
            console.warn("removePkg fail")
        }
    }

    public clearOpenUI() {
        this._opened.Foreach((viewKey: string, value: FguiView) => {
            value.hide();
            this.RemovePanel(value.Panel);
            this._closed.Add(viewKey, value);
        })
        this._opened.Clear()
    }

    public clearAllUI() {
        this._opened.Foreach((viewKey: string, value: FguiView) => {
            value.hide();
            this.RemovePanel(value.Panel);
        })
        this.clearCacheMap()
        // this._addPkged.Foreach((pkgName: string, value: number) => {
        //     fgui.UIPackage.removePackage(pkgName)
        // })
        this._addPkged.Clear();
    }

    public clearCacheMap() {
        this._opened.Clear()
        this._closed.Clear()
    }



    OnUpdate(elapseTime: number): void {
        this._opened.Foreach((k, v) => {
            v.OnUpdate(elapseTime);
        }, this);
    }
    OnLateUpdate(elapseTime: number): void {
        this._opened.Foreach((k, v) => {
            v.OnLateUpdate(elapseTime);
        }, this);
    }
    Dispose(): void {
        //TODO 有需要再补充
    }

    /** 增加fgui pkg计数 */
    public AddPkgCount(pkgName: string, from?: string) {
        if (this._addPkged.ContainsKey(pkgName)) {
            //已存在++
            let pkgCount = this._addPkged.Value(pkgName);
            pkgCount++;
            this._addPkged.Replace(pkgName, pkgCount)
        } else {
            this._addPkged.Add(pkgName, 1)
        }
        if (!gameMethod.isEmpty(GameDataCenter?.login.playerInfo?.switch?.clientLog)) {
            console.log(`${from} ${pkgName} count: ${this._addPkged.Value(pkgName)}`)
        }
    }

    /** 减少fgui pkg计数 */
    public DelPkgCount(pkgName: string, from?: string) {
        if (this._addPkged.ContainsKey(pkgName)) {
            //已存在++
            let pkgCount = this._addPkged.Value(pkgName);
            pkgCount--;
            if (!gameMethod.isEmpty(GameDataCenter?.login.playerInfo?.switch?.clientLog)) {
                console.log(`${pkgName} count: ${pkgCount}`, from)
            }
            this._addPkged.Replace(pkgName, pkgCount);
            if (pkgCount <= 0 && pkgName != "Common") {
                if (!gameMethod.isEmpty(GameDataCenter?.login.playerInfo?.switch?.clientLog)) {
                    console.log(`removePackage:${pkgName}`, from)
                }
                fgui.UIPackage.removePackage(pkgName)
            }
        }
    }


    AddPkgByPkgName(pkgName: string, cb?: Function) {
        if (fgui.UIPackage.getByName(pkgName) == null) {
            // 加载uipackage
            FguiLoadMgr.loadPackage(pkgName, pkgName, (error, pkg) => {
                if (error) {
                    EventMng.emit(GUIEvent.SHOW_MASK, pkgName, false)
                    console.error(`FguiMgr LoadPackage[${pkgName}] error: ${error}`);
                    return;
                }
                this.AddPkgCount(pkgName);
                if (!gameMethod.isEmpty(GameDataCenter?.login.playerInfo?.switch?.clientLog)) {
                    console.log(`${pkgName} count: ${this._addPkged.Value(pkgName)}`)
                }
                cb && cb();
            });
        } else {
            this.AddPkgCount(pkgName);
            if (!gameMethod.isEmpty(GameDataCenter?.login.playerInfo?.switch?.clientLog)) {
                console.log(`${pkgName} count: ${this._addPkged.Value(pkgName)}`)
            }
            cb && cb();
        }
    }

    RemovePkgByPkgName(pkgName: string) {
        //ios平台才释放，安卓平台不释放
        if (GameDataCenter.plat.instance.deviceOS != PlatFormDevType.iosH5 && GameDataCenter.plat.instance.deviceOS != PlatFormDevType.ios && GameDataCenter.plat.instance.deviceOS != PlatFormDevType.local) {
            return;
        }
        this.DelPkgCount(pkgName);
    }

    private AddPanel(panel: fgui.GComponent, zOrder: ViewZOrder) {
        const layerNode = this._layerNodeMap.get(zOrder);
        if (layerNode) {
            layerNode.addChild(panel);
        } else {
            fgui.GRoot.inst.addChild(panel);
        }
        // UI全屏自适应
        panel.makeFullScreen();
    }
    private RemovePanel(panel: fgui.GComponent) {
        fgui.GRoot.inst.removeChild(panel);
    }
    private DestroyView<T extends FguiView>(uiview: FguiView, uiClass: FguiClass<T>) {
        //ios平台才释放，安卓平台不释放
        if (GameDataCenter.plat.instance.deviceOS != PlatFormDevType.iosH5 && GameDataCenter.plat.instance.deviceOS != PlatFormDevType.ios && GameDataCenter.plat.instance.deviceOS != PlatFormDevType.local) {
            return;
        }
        var panel = uiview.Panel;
        panel.dispose();
        this.DelPkgCount(uiClass.getPkgName());
    }


    public CloseUIByLayer(zOrder: ViewZOrder) {
        this._opened.Foreach((viewKey: string, value: FguiView) => {
            if (value.zOrder == zOrder) {
                const view = this._opened.Value(viewKey);
                this._opened.Remove(viewKey);
                view.hide();
                this.RemovePanel(view.Panel);
                this._closed.Add(viewKey, view);
                if (view.viewType == ViewType.Full) {
                    this._opened.Foreach((key: string, value: FguiView) => {
                        if (value.viewType != ViewType.Part && value.zOrder <= view.zOrder) {
                            value.addFullHideCount(-1);
                        }
                    })
                }
            }
        })
    }

    /** 关闭所有的UI  elseView 排除无需关闭的界面 */
    public CloseAllUI(elseView: string[] = []): void {
        this._opened.Foreach((viewKey: string, value: FguiView) => {
            if (elseView.indexOf(viewKey) < 0) {
                const view = this._opened.Value(viewKey);
                this._opened.Remove(viewKey);
                view.hide();
                this.RemovePanel(view.Panel);
                this._closed.Add(viewKey, view);
                if (view.viewType == ViewType.Full) {
                    this._opened.Foreach((key: string, value: FguiView) => {
                        if (value.viewType != ViewType.Part && value.zOrder <= view.zOrder) {
                            value.addFullHideCount(-1);
                        }
                    })
                }
            }
        })
    }


    //#endregion
}