local Game = {
    frameCount = 1,
    loadedList = {},
    pauseStatus = false,
    hasFocus = true
}

EDITOR_MODE = CS.BF.GameConst.EDITOR_MODE
DEBUG = CS.BF.GameConst.DEBUG
USE_AB = CS.BF.GameConst.USE_AB
IS_PUBLISH = CS.BF.BFPlatform.IsPublishChannel()
NOT_PUBLISH = not IS_PUBLISH

if EDITOR_MODE and CS.BF.BFMain.Instance.LuaMgr.DevExist then
    -- 调试等一些特殊代码可以写到lua_debug.lua文件里面,lua_debug已添加忽略，有需要的可以在根目录创建，和main.lua同级
    local devLuaPath = CS.UnityEngine.Application.dataPath .. "/Developer/lua/lua_debug.lua"
    if CS.System.IO.File.Exists(devLuaPath) then
        require("lua_debug")
    end
end

local Input = CS.UnityEngine.Input
local KeyCode = CS.UnityEngine.KeyCode

function Game:recordloadedList()
    self.loadedList = {}
    for name, v in pairs(package.loaded) do
        self.loadedList[name] = 1
    end
end

function Game:initBase()
    self:recordloadedList()
    require "app/bf/common/functions"
    require "app/bf/init"
    require "app/global/global_unity_func"
    CS.BF.BFMain.Instance.LuaMgr:GetGlobalLuaFunc()
    BF.exports.Logger = require "app/common/logger"
    BF.exports.LocalData = require "app/common/local_data"
    BF.exports.BIReport = require "app/common/bi_report"
    BF.exports.SchedulerManager = require "app/common/scheduler_manager"
    BF.exports.ResourceManager = require "app/common/resource_manager"
    BF.exports.AudioManager = require "app/common/audio_manager"
    BF.exports.RenderManager = require "app/common/render_manager"
    AudioManager:init()

    Logger.log("initBase")
end

function Game:initOther()
    require "app/bf/unity/unity"

    BF.exports.json = require "rapidjson"

    BF.exports.GConst = require "app/global/global_const"
    BF.exports.GFunc = require "app/global/global_func"

    BF.exports.LuaComponent = require "app/bf/component/lua_component"
    BF.exports.BaseCell = require "app/ui/common/cell/base_cell"
    BF.exports.VersionCompatible = require "app/common/version_compatible"
    BF.exports.Platform = require "app/common/platform"
    BF.exports.ConfigManager = require "app/common/config_manager"
    BF.exports.EventManager = require "app/common/event_manager"
    BF.exports.CameraManager = require "app/common/camera_manager"
    BF.exports.SceneManager = require "app/scene/scene_manager"
    BF.exports.BaseScene = require "app/scene/base_scene"
    BF.exports.UIManager = require "app/ui/ui_manager"
    BF.exports.BaseUI = require "app/ui/base_ui"
    BF.exports.BaseSubUI = require "app/ui/base_sub_ui"
    BF.exports.UIPrefabManager = require "app/common/uiprefab_manager"
    BF.exports.TextureManager = require "app/common/texture_manager"
    BF.exports.ModelManager = require "app/common/model_manager"
    BF.exports.EffectManager = require "app/common/effect_manager"
    BF.exports.SpineManager = require "app/common/spine_manager"
    BF.exports.WebRequestManager = require "app/common/webrequest_manager"
    BF.exports.NetManager = require "app/net/net_manager"
    BF.exports.I18N = require "app/common/i18n_manager"
    BF.exports.CellManager = require "app/common/cell_manager"
    BF.exports.Time = require "app/common/time"
    BF.exports.BaseData = require "app/userdata/base_data"
    BF.exports.BaseObject = require "app/bf/unity/base_object"
    BF.exports.ServerPushManager = require "app/common/server_push_manager"
    BF.exports.SafeAreaManager = require "app/common/safe_area_manager"
    BF.exports.BaseModule = require "app/module/base_module"
    BF.exports.ModuleManager = require "app/common/module_manager"
    BF.exports.DataManager = require "app/common/data_manager"
    BF.exports.DOTweenManager = require "app/common/dotween_manager"
    BF.exports.FSMManager = require "app/common/state_machine_manager"
    BF.exports.ProtoMsgType = require "app/proto/proto_msg_type"
    BF.exports.SDKManager = require "app/common/sdk_manager"
    BF.exports.DeviceHelper = require "app/common/device_helper"
    BF.exports.PayManager = require "app/common/pay_manager"

    CameraManager:init()
    WebRequestManager:init()
    I18N:init()
    DataManager:init()
    DOTweenManager:init()
    ModuleManager:init()
    NetManager:init()

     -- 例如EmmyLua等IDE或者插件无法识别BF.exports.xx的全局变量赋值语法,这里专门处理一下
    self:specialForIdea()
end

function Game:specialForIdea()
    if EDITOR_MODE then
        -- xlua专用的全局变量
        CS = CS
        typeof = typeof

        json = json or require "rapidjson"

        Logger = Logger or require "app/common/logger"
        LocalData = LocalData or require "app/common/local_data"
        BIReport = BIReport or require "app/common/bi_report"
        SchedulerManager = SchedulerManager or require "app/common/scheduler_manager"
        ResourceManager = ResourceManager or require "app/common/resource_manager"
        AudioManager = AudioManager or require "app/common/audio_manager"
        RenderManager = RenderManager or require "app/common/render_manager"

        GConst = GConst or require "app/global/global_const"
        GFunc = GFunc or require "app/global/global_func"

        LuaComponent = LuaComponent or require "app/bf/component/lua_component"
        BaseCell = BaseCell or require "app/ui/common/cell/base_cell"
        VersionCompatible = VersionCompatible or require "app/common/version_compatible"
        Platform = Platform or require "app/common/platform"
        ConfigManager = ConfigManager or require "app/common/config_manager"
        EventManager = EventManager or require "app/common/event_manager"
        CameraManager = CameraManager or require "app/common/camera_manager"
        SceneManager = SceneManager or require "app/scene/scene_manager"
        BaseScene = BaseScene or require "app/scene/base_scene"
        UIManager = UIManager or require "app/ui/ui_manager"
        BaseUI = BaseUI or require "app/ui/base_ui"
        BaseSubUI = BaseSubUI or require "app/ui/base_sub_ui"
        UIPrefabManager = UIPrefabManager or require "app/common/uiprefab_manager"
        TextureManager = TextureManager or require "app/common/texture_manager"
        ModelManager = ModelManager or require "app/common/model_manager"
        EffectManager = EffectManager or require "app/common/effect_manager"
        SpineManager = SpineManager or require "app/common/spine_manager"
        WebRequestManager = WebRequestManager or require "app/common/webrequest_manager"
        NetManager = NetManager or require "app/net/net_manager"
        I18N = I18N or require "app/common/i18n_manager"
        CellManager = CellManager or require "app/common/cell_manager"
        Time = Time or require "app/common/time"
        BaseData = BaseData or require "app/userdata/base_data"
        BaseObject = BaseObject or require "app/bf/unity/base_object"
        ServerPushManager = ServerPushManager or require "app/module/login/server_push_manager"
        SafeAreaManager = SafeAreaManager or require "app/common/safe_area_manager"
        BaseModule = BaseModule or require "app/module/base_module"
        ModuleManager = ModuleManager or require "app/common/module_manager"
        DataManager = DataManager or require "app/common/data_manager"
        DOTweenManager = DOTweenManager or require "app/common/dotween_manager"
        FSMManager = FSMManager or require "app/common/state_machine_manager"
        ProtoMsgType = ProtoMsgType or require "app/proto/proto_msg_type"
        SDKManager = SDKManager or require "app/common/sdk_manager"
        DeviceHelper = DeviceHelper or require "app/common/device_helper"
        PayManager = PayManager or require "app/common/pay_manager"
    end
end

function Game:disableGlobalVariable()
    if self.g_meta_table then
        setmetatable(_G, self.g_meta_table)
    elseif BF and BF.disableGlobal then
        BF.disableGlobal()
    else
        setmetatable(_G, {
            __newindex = function(_, name, value)
                error(string.format("USE \" BF.exports.%s = value \" INSTEAD OF SET GLOBAL VARIABLE", name), 0)
            end
        })
    end
end

function Game:enableGlobalVariable()
    self.g_meta_table = getmetatable(_G)
    setmetatable(_G, nil)
end

function Game:start()
    local time = os.clock()

    RenderManager:initRender(function()
        Logger.log("===========start game================")
        self:initOther()

        -- 先初始化UIManager
        UIManager:init(function()
            Logger.log("init game use time " .. (os.clock() - time))
            SDKManager:init()
            -- 然后等字体加载完成后再显示界面
            I18N:onFontLoaded(function()
                SceneManager:enterMainScene()
            end)
        end)
    end)
end

function Game:showLoginUI()
    UIManager:hideRollToast(true)
    ModuleManager.LoginManager:showLoginUI()
end

-- 测试地址的连接情况
function Game:httpRequestHostAndReport(ipOrHost)
    local startTime = CS.UnityEngine.Time.time
    local httpRequest = CS.BestHTTP.HTTPRequest(CS.System.Uri(ipOrHost), CS.BestHTTP.HTTPMethods.Get, function(req, httpResponse)
        if httpResponse and httpResponse.IsSuccess == true then
            BIReport:postPingServer(ipOrHost, math.floor((CS.UnityEngine.Time.time - startTime)*1000), "Success")
        else
            BIReport:postPingServer(ipOrHost, math.floor((CS.UnityEngine.Time.time - startTime)*1000), "Failed")
        end
    end)
    httpRequest:Send()
end

function Game:handleInputKey()
    if not self.hasFocus then
        return
    end
    if self.pauseStatus then
        return
    end
    if Input.GetKeyDown(KeyCode.Escape) then
        if UIManager then
            UIManager:onPressAndroidBackspace()
        end
    end
    if Input.anyKey then -- 待确认
        EventManager:dispatchEvent(EventManager.CUSTOM_EVENT.GET_ANY_KEY_DOWN)
    end
end

function Game:clear()
    SchedulerManager:clear()
    EventManager:clear()
    AudioManager:clear()
    ResourceManager:clear()
end

function Game:onDestroy()
end

function Game:update()
    self.frameCount = self.frameCount + 1
    if SchedulerManager then
        SchedulerManager:update()
    end
    if FSMManager then
        FSMManager:tick()
    end
    self:handleInputKey()
end

function Game:onApplicationFocus(hasFocus)
    if hasFocus == self.hasFocus then
        return
    end
    self.hasFocus = hasFocus
end

function Game:onApplicationPause(pauseStatus)
    if pauseStatus == self.pauseStatus then
        return
    end
    self.pauseStatus = pauseStatus
end

function Game:onApplicationQuit()
    -- 上报游戏退出
    if BIReport then
        BIReport:postGameExit()
    end
end

function Game:onLogMessageReceived(logString, stackTrace)
    -- 同一帧内的多个crash只处理第一个
    if self.currCrashFrame == self.frameCount then
        return
    end
    self.currCrashFrame = self.frameCount
    if self.lastLogString == logString and self.lastStackTrace == stackTrace then
        return
    end
    self.lastLogString = logString
    self.lastStackTrace = stackTrace
    if BIReport then
        BIReport:postLuaCrash(logString, stackTrace)
    end
end

function Game:garbageCollect(callback)
	ResourceManager:unloadAllDelayAssets()
	CS.BF.BFMain.Instance.LuaMgr:LuaEnvFullGC(30000)
	collectgarbage("collect")
	local asyncOperation = CS.UnityEngine.Resources.UnloadUnusedAssets()
	local function onUnloadComplete(opration)
		asyncOperation:completed("-", onUnloadComplete)
		if callback then
			callback()
		end
	end
	asyncOperation:completed("+", onUnloadComplete)
end

function Game:destroyAll()
    self:destroyLua()
    self:destroyRes()
    self:destroyAB()
end

function Game:destroyLua()
    for name, v in pairs(package.loaded) do
        if not self.loadedList[name] then
            package.loaded[name] = nil
        end
    end
    package.loaded["app/game"] = nil
    package.loaded["app/first/first"] = nil
    package.loaded["app/first/first_text"] = nil
    CS.BF.BFMain.Instance.LuaMgr:ClearCache()
    CS.BF.BFMain.Instance.LuaMgr:Clear()
end

function Game:destroyRes()
    UIManager:destroyUIRoot()
    ResourceManager:unloadAllDelayAssets()
    ResourceManager:clear()
end

-- 清理没有被ResourceManager卸载的ab
function Game:destroyAB()
    local bundles = CS.UnityEngine.AssetBundle.GetAllLoadedAssetBundles()
    if bundles then
        local len = bundles.Length
        for i = 0, len - 1 do
            local bundle = bundles[i]
            if bundle then
                Logger.log("[game]destroyAB :%s", bundle.name)
                if "first/first.ab" ~= bundle.name then
                    bundle:Unload(false)
                end
            end
        end
    end
end

if NOT_PUBLISH then
    Game._releaseOnLogMessageReceived = Game.onLogMessageReceived
    function Game:onLogMessageReceived(logString, stackTrace)
        self:_releaseOnLogMessageReceived(logString, stackTrace)
        if UIManager then
            local params = {
                desc = logString .. stackTrace
            }
			ModuleManager.TipsManager:showHelpTips(params)
        end
    end

    Game._releaseHandleInputKey = Game.handleInputKey
    function Game:handleInputKey()
        self:_releaseHandleInputKey()
        if Input.GetKeyDown(KeyCode.T) then
            -- 如果当前正处于输入状态,则不显示响应gm快捷键
            local comps = CS.UnityEngine.GameObject.FindObjectsOfType(GConst.TYPEOF_UNITY_CLASS.UI_INPUT_FIELD)
            for i = 0, comps.Length - 1 do
                if comps[i].isFocused then
                    return
                end
            end
            if UIManager and UIManager:getTopUIObj() and UIManager:getTopUIObj():getUIIndex() ~= UIManager.UI_PATH.GM_TOO_UI then
                if ModuleManager.DevToolManager then
                    ModuleManager.DevToolManager:showOrHideDevListUI()
                end
            end
        end
        if Input.GetKeyDown(KeyCode.Backspace) and Input.GetKey(KeyCode.RightControl) then
            Logger.logHighlight("on press backspace")
            if UIManager then
                UIManager:onPressAndroidBackspace()
            end
        end
        if Input.GetKeyDown(KeyCode.J) and Input.GetKey(KeyCode.RightControl) then
            local desktopDir = CS.System.Environment.GetFolderPath(CS.System.Environment.SpecialFolder.DesktopDirectory)
            local path = CS.System.IO.Path.Combine(desktopDir, "bf_screenshot.png")
            CS.UnityEngine.ScreenCapture.CaptureScreenshot(path, 1)
            Logger.logHighlight("截图保存地址:%s", path)
        end

        if Input.GetKeyDown(KeyCode.G) and Input.GetKey(KeyCode.LeftControl) then
            local battleController = ModuleManager.BattleManager.battleController
            if not battleController then
                return
            end
            if battleController.battleData.gridEntities then
                local map = {}
                for posId, entity in pairs(battleController.battleData.gridEntities) do
                    map[tostring(posId)] = entity:getSnapshoptInfo()
                end
                Logger.logHighlight("——————————战斗棋盘快照如下————————")
                Logger.logHighlight("——————————当前战斗回合步骤————————" .. battleController.roundStep)
                Logger.logHighlight(json.encode(map))
            end
        end

        if (Input.GetKeyDown(KeyCode.Space)) and Input.GetKey(KeyCode.LeftControl) then
            local battleController = ModuleManager.BattleManager.battleController
            if not battleController then
                return
            end
            local heler = require "app/module/battle/helper/board_helper"
            local path = heler:findPvpLinkOptimalSolution(battleController, 1, 7)
            if path then
                for _, id in ipairs(path) do
                    local entity = battleController.battleData:getGridEnties()[id]
                    entity:getCell():showHighLight(true)
                end
            end
        end

        if Input.GetKeyDown(KeyCode.LeftArrow) and Input.GetKey(KeyCode.RightControl)then
            local curLanguage = I18N:getCurLanguage()
            local languageList = I18N:getSupportLanguageList()
            local curIndex = 1
            for index, language in ipairs(languageList) do
                if language == curLanguage then
                    curIndex = index + 1
                    break
                end
            end
            if not languageList[curIndex] then
                curIndex = 1
            end
            local language = languageList[curIndex]
            local changeStatus = I18N:setLanguage(language)
            if changeStatus then
                local FontManager = require "app/common/font_manager"
                FontManager:changeLanguage(I18N:getCurLanguage() or GConst.LANGUAGE.ENGLISH, function()
                    local topUi = UIManager:getTopUIObj()
                    if topUi:getUIIndex() == UIManager.UI_PATH.MAINCITY_UI then
                        EventManager:dispatchEvent(EventManager.CUSTOM_EVENT.I18N_CHANGE_LANGUAGE)
                        UIManager:closeAllUI()
                        UIManager:hideToastAndMessageBox()
                        UIManager:clearUIPrefabCache()
                        UIManager:refreshOnChangeLanguage()
                        ModuleManager.MaincityManager:showMainCityUI()
                    else
                        EventManager:dispatchEvent(EventManager.CUSTOM_EVENT.I18N_CHANGE_LANGUAGE)
                        UIManager:hideToastAndMessageBox()
                        UIManager:clearUIPrefabCache()
                        UIManager:refreshOnChangeLanguage()
                        for _, obj in ipairs(UIManager:getUIList()) do
                            obj:onClose()
                            obj:onLoadRootComplete()
                            obj:onRefresh()
                        end
                    end
                end)
            end
        end

        if (Input.GetKeyDown(KeyCode.RightArrow) or Input.GetKeyDown(KeyCode.LeftArrow)) and Input.GetKey(KeyCode.LeftControl) then
            if not ModuleManager.DevToolManager.set_board_info then
                Logger.logHighlight("请先去gm面板设置浏览的棋盘信息!")
                return
            end
            local configName = ModuleManager.DevToolManager.set_board_info.config
            local idx = ModuleManager.DevToolManager.set_board_info.idx or 0
            local isMystery = ModuleManager.DevToolManager.set_board_info.isMystery
            if not configName then
                return
            end
            local config = ConfigManager:getConfig(configName)
            if Input.GetKeyDown(KeyCode.LeftArrow) then
                idx = idx - 1
                if isMystery then
                    while config[idx] do
                        if config[idx].mystery_box_board then
                            break
                        end
                        idx = idx - 1
                    end
                end
            else
                idx = idx + 1
                if isMystery then
                    while config[idx] do
                        if config[idx].mystery_box_board then
                            break
                        end
                        idx = idx + 1
                    end
                end
            end
            if not config[idx] then
                Logger.logHighlight(configName .. " 没有id idx = " .. idx)
                return
            end
            Logger.logHighlight(configName .. " 当前 idx = " .. idx)
            ModuleManager.DevToolManager.set_board_info.idx = idx

            local board = config[idx].board
            if not board then
                board = config[idx].board_daily_challenge
            end
            if isMystery then
                board = config[idx].mystery_box_board
                if not board then
                    Logger.logHighlight(configName .. " 没有mystery_box_board idx = " .. idx)
                    return
                end
            end

            local grid_edge = config[idx].grid_edge

            if ModuleManager.BattleManager:isInBattle() then
                local battleController = ModuleManager.BattleManager.battleController
                battleController.battleData:refreshBoard(board, grid_edge, battleController:getBlockIcon())
                battleController.battleUI:initGridCell()
            end
        end

        if Input.GetKeyDown(KeyCode.LeftArrow) and Input.GetKey(KeyCode.F) then
            ModuleManager.LoginManager:reloginOnReconnectRefuse()
        end
    end

    Game._releaseOnApplicationFocus = Game.onApplicationFocus
    function Game:onApplicationFocus(hasFocus)
        if hasFocus ~= self.hasFocus and hasFocus == true then -- 说明从后台返回到游戏里
        end
        self:_releaseOnApplicationFocus(hasFocus)
    end
end

return Game
