-- Copyright ©2024-2025 by Todd Hundersmarck (ThundR) 
-- All Rights Reserved

--[[

Unauthorized use and/or distribution of this work entitles myself, the author, to unlimited free and unrestricted use, access, and distribution of any works related to the unauthorized user and/or distributor.

--]]

THEventManager = {}
local THEventManager_mt = Class(THEventManager)
THEventManager.CLASS_NAME = "THEventManager"
THEventManager.DATA_KEY = tostring(THEventManager)
local debugFlagId = THUtils.createDebugFlagId(THEventManager.CLASS_NAME)
THEventManager.debugFlagId = debugFlagId
local function initScript()
    local self = THEventManager.new()
    if self ~= nil then
        _G.g_thEventManager = self
    end
end
function THEventManager.new(customMt)
    customMt = customMt or THEventManager_mt
    if THUtils.argIsValid(type(customMt) == THValueType.TABLE, "customMt", customMt) then
        local self = setmetatable({}, customMt)
        self.isClient = g_client ~= nil
        self.isServer = g_server ~= nil
        self.isPreInitFinished = false
        self.isPostInitFinished = false
        self.areEventsRunning = false
        self.eventListeners = {}
        self.delayedLoadTasks = {}
        self.onUpdateTasks = {
            byIndex = {},
            byTarget = {}
        }
        THUtils.prependFunction("XMLManager", "createSchemas", false, false, self, THEventManager.prepend_createSchemas)
        THUtils.prependFunction("TypeManager", "finalizeTypes", false, false, self, THEventManager.prepend_finalizeTypes)
        THUtils.prependFunction("Mission00", "setMissionInfo", false, false, self, THEventManager.prepend_setMissionInfo)
        THUtils.prependFunction("Mission00", "loadMission00Finished", false, false, self, THEventManager.prepend_loadMission00Finished)
        THUtils.prependFunction("InputBinding", "beginActionEventsModification", false, false, self, THEventManager.prepend_beginActionEventsModification)
        THUtils.appendFunction("XMLManager", "initSchemas", false, false, self, THEventManager.append_initSchemas)
        THUtils.appendFunction("FSBaseMission", "initTerrain", false, false, self, THEventManager.append_initTerrain)
        addModEventListener(self)
        return self
    end
end
function THEventManager.update(self, dt)
    THUtils.pcall(function()
        if self.isClient then
            local currentInputContext = g_inputBinding:getContextName()
            local lastInputContext = self.lastInputContext
            if currentInputContext ~= lastInputContext then
                self.lastInputContext = currentInputContext
                self:raiseEvent("onUnregisterGlobalActionEvents", currentInputContext, lastInputContext)
                self:raiseEvent("onRegisterGlobalActionEvents", currentInputContext, lastInputContext)
                if THUtils.getIsDebugEnabled(debugFlagId) then
                    THUtils.displayMsg("Switching global input context: %s", currentInputContext)
                end
            end
        end
        self:raiseEvent("onUpdate", dt)
        local numUpdateTasks = #self.onUpdateTasks.byIndex
        if numUpdateTasks > 0 then
            for taskIndex = numUpdateTasks, 1, -1 do
                local taskData = self.onUpdateTasks.byIndex[taskIndex]
                taskData.runTime = taskData.runTime + dt
                if not taskData.isDrawTask then
                    taskData.func()
                end
                if taskData.runTime >= taskData.duration then
                    table.remove(self.onUpdateTasks.byIndex, taskIndex)
                    self.onUpdateTasks.byTarget[taskData.func] = nil
                end
            end
        end
    end)
end
function THEventManager.draw(self)
    THUtils.pcall(function()
        self:raiseEvent("onDraw")
        for _, taskData in pairs(self.onUpdateTasks.byIndex) do
            if taskData.isDrawTask then
                taskData.func()
            end
        end
    end)
end
function THEventManager.deleteMap(self)
    self:raiseEvent("onUnregisterGlobalActionEvents")
    self:raiseEvent("onDelete")
    g_messageCenter:unsubscribeAll(self)
end
function THEventManager.addEventListener(self, target)
    if THUtils.argIsValid(type(target) == "table", "target", target) and THUtils.assert(target ~= self, true, "Cannot add event manager to listeners table") then
        local isListenerFound = false
        for _, otherListenerData in pairs(self.eventListeners) do
            if otherListenerData.target == target then
                isListenerFound = true
                break
            end
        end
        if not isListenerFound then
            local listenerData = { target = target }
            table.insert(self.eventListeners, listenerData)
        end
    end
end
function THEventManager.removeEventListener(self, target)
    local numListeners = #self.eventListeners
    if target ~= nil and numListeners > 0 then
        local function callbackFunc()
            for i = numListeners, 1, -1 do
                local listenerData = self.eventListeners[i]
                if target == listenerData.target then
                    table.remove(self.eventListeners[i])
                end
            end
        end
        if self.areEventsRunning then
            THUtils.addTask(true, callbackFunc)
        else
            callbackFunc()
        end
    end
end
function THEventManager.raiseEvent(self, eventName, ...)
    if THUtils.argIsValid(type(eventName) == THValueType.STRING, "eventName", eventName) then
        if type(self[eventName]) == THValueType.FUNCTION then
            self[eventName](self, ...)
        end
        local numListeners = #self.eventListeners
        if numListeners > 0 then
            local lastIsRunning = self.areEventsRunning == true
            local listenerIndex = 1
            self.areEventsRunning = true
            while true do
                local listenerData = self.eventListeners[listenerIndex]
                if listenerData == nil then
                    break
                end
                local callbackFunc = listenerData.target[eventName]
                if callbackFunc ~= nil then
                    callbackFunc(listenerData.target, ...)
                end
                listenerIndex = listenerIndex + 1
            end
            self.areEventsRunning = lastIsRunning
        end
    end
end
function THEventManager.addDelayedLoadTask(self, taskFunc)
    if THUtils.argIsValid(type(taskFunc) == THValueType.FUNCTION, "taskFunc", taskFunc) then
        local isTaskFound = false
        for _, otherTaskFunc in pairs(self.delayedLoadTasks) do
            if otherTaskFunc == taskFunc then
                isTaskFound = true
                break
            end
        end
        if not isTaskFound then
            table.insert(self.delayedLoadTasks, taskFunc)
            return true
        end
    end
    return false
end
function THEventManager.runDelayedLoadTasks(self)
    local numTasks = #self.delayedLoadTasks
    if numTasks > 0 then
        local taskIndex = 1
        while true do
            local taskFunc = self.delayedLoadTasks[taskIndex]
            if taskFunc == nil then
                break
            end
            THUtils.pcall(taskFunc)
            table.remove(self.delayedLoadTasks, taskIndex)
        end
    end
    return true
end
function THEventManager.addUpdateTask(self, duration, taskFunc)
    if THUtils.argIsValid(type(duration) == THValueType.NUMBER and duration >= 0, "duration", duration)
        and THUtils.argIsValid(type(taskFunc) == THValueType.FUNCTION, "taskFunc", taskFunc)
    then
        local taskData = self.onUpdateTasks.byTarget[taskFunc]
        if taskData == nil then
            taskData = {
                func = taskFunc,
                duration = duration,
                runTime = 0,
                isDrawTask = false
            }
            table.insert(self.onUpdateTasks.byIndex, 1, taskData)
            self.onUpdateTasks[taskFunc] = taskData
        end
        return taskData
    end
end
function THEventManager.addDrawTask(self, duration, taskFunc)
    local taskData = self:addUpdateTask(duration, taskFunc)
    if taskData ~= nil then
        taskData.isDrawTask = true
        return taskData
    end
end
function THEventManager.prepend_createSchemas(self, superFunc, ...)
    if not self.isPreInitFinished then
        self:raiseEvent("onPreInit")
        self.isPreInitFinished = true
    end
end
function THEventManager.prepend_finalizeTypes(self, superFunc, manager, ...)
    THUtils.addSubTask(function()
        self:raiseEvent("onFinalizeTypes", manager)
    end)
end
function THEventManager.prepend_setMissionInfo(self, superFunc, mission, missionInfo, missionDynamicInfo, ...)
    THUtils.addTask(function()
        self:raiseEvent("onSetMissionInfo", mission, missionInfo, missionDynamicInfo)
    end)
end
function THEventManager.prepend_loadMission00Finished(self, superFunc, parent, ...)
    self:raiseEvent("onLoadMapFinished", parent)
end
function THEventManager.prepend_beginActionEventsModification(self, superFunc, parent, contextName, ...)
    self.lastInputContext = nil
end
function THEventManager.append_initSchemas(self, superFunc, ...)
    THUtils.addSubTask(function()
        if not self.isPostInitFinished then
            self:raiseEvent("onPostInit")
            self.isPostInitFinished = true
        end
        self:runDelayedLoadTasks()
    end)
end
function THEventManager.append_initTerrain(self, superFunc, parent, terrainNode, mapI3DFilename, ...)
    self:raiseEvent("onInitTerrain", parent, terrainNode, mapI3DFilename)
    self:raiseEvent("onPostInitTerrain", parent, terrainNode, mapI3DFilename)
end
THUtils.pcall(initScript)