local _, GSE = ...

LibStub("AceEvent-3.0"):Embed(GSE)
LibStub("AceComm-3.0"):Embed(GSE)

GSE.L = LibStub("AceLocale-3.0"):GetLocale("GSE")
GSE.Static = {}

GSE.WagoAnalytics = LibStub("WagoAnalytics"):Register("kGr0YY6y")

GSE.VersionString = C_AddOns.GetAddOnMetadata("GSE", "Version")

--[==[@debug@
if GSE.VersionString:find("version") then
    GSE.VersionString = "3.3.00-development"
    GSE.Developer = true
end
--@end-debug@]==]

if GSE.VersionString:find("Patron") then
    GSE.Patron = true
end
GSE.MediaPath = "Interface\\Addons\\GSE\\Media"
GSE.Pause = {}
GSE.OutputQueue = {}
GSE.IncomingQueue = {}
GSE.DebugOutput = ""
GSE.SequenceDebugOutput = ""
GSE.GUI = {}
GSE.V = {}
GSE.BooleanVariables = {}
GSE.PlayerEntered = false
GSE.WagoAnalytics:Switch("Patron", GSE.Patron)
local L = GSE.L
local Statics = GSE.Static
local GNOME = "|cFFFFFFFFGS|r|cFF00FFFFE|r"

-- Initialisation Functions
--- Checks for nil or empty string.
function GSE.isEmpty(s)
    return s == nil or s == ""
end

--- Split a string into an array based on the delimiter specified.
function GSE.split(source, delimiters)
    local elements = {}
    local pattern = "([^" .. delimiters .. "]+)"
    local _ =
        string.gsub(
        source,
        pattern,
        function(value)
            elements[#elements + 1] = value
        end
    )
    return elements
end

local gameversion, _, _, _, _, buildType = GetBuildInfo()
local majorVersion = GSE.split(gameversion, ".")

GSE.GameMode = tonumber(majorVersion[1])

--- This function takes a version String and returns a version number.
function GSE.ParseVersion(version)
    -- If it contains alpha or beta replace with the current version.  This will prevent notifying about test builds.
    if string.match(version, "alpha") or string.match(version, "beta") then
        version = GSE.VersionString
    end
    local parts = GSE.split(version, "-")
    local numbers = GSE.split(parts[1], ".")
    local returnVal
    if GSE.isEmpty(numbers) and type(version) == "number" then
        returnVal = version
    else
        if #numbers > 1 then
            returnVal = (tonumber(numbers[1]) * 1000) + (tonumber(numbers[2]) * 100) + (tonumber(numbers[3]))
        else
            returnVal = tonumber(version)
        end
    end
    return tonumber(returnVal)
end

GSE.VersionNumber = GSE.ParseVersion(GSE.VersionString)

--- When the Addon loads, printing is paused until after every other mod has loaded.
--    This method prints the print queue.
function GSE.PerformPrint()
    for k, v in ipairs(GSE.OutputQueue) do
        print(GNOME .. ": " ..v)
        GSE.OutputQueue[k] = nil
    end
end

--- Prints <code>filepath</code>to the chat handler.  This accepts an optional
--    <code>title</code> to be prepended to that message.
function GSE.Print(message, title)
    -- Store this for later on.
    if not GSE.isEmpty(title) then
        message = GSEOptions.CommandColour .. title .. Statics.StringReset .. " " .. message
    end
    table.insert(GSE.OutputQueue, message)
    if GSE.PrintAvailable then
        GSE.PerformPrint()
    end
end

--- Send the message string to an output source.
--    If <code>GSEOptions.sendDebugOutputGSE.DebugOutput</code> then the output will
--    be appended to variable <code>GSE.DebugOutput</code>
--    If <code>GSEOptions.sendDebugOutputToChat</code> then the output will
--    be sent to variable <code>GSE.Print</code>
--    The Title is stripped for intermod debug output via GSE.DebugOutput
local function determinationOutputDestination(message, title)
    local wroteDebugOutput = false
    if GSE.UnsavedOptions.DebugSequenceExecution then
        GSE.DebugOutput = GSE.DebugOutput .. message .. "\n"
        wroteDebugOutput = true
    elseif GSEOptions.sendDebugOutputToDebugOutput then
        GSE.DebugOutput = GSE.DebugOutput .. message .. "\n"
        wroteDebugOutput = true
    end
    if GSEOptions.sendDebugOutputToChatWindow then
        GSE.Print(message, title)
    end
    if
        wroteDebugOutput and type(GSE.GUIUpdateOutput) == "function" and GSE.GUIDebugFrame and
            type(GSE.GUIDebugIsOpenOrMinimized) == "function" and GSE.GUIDebugIsOpenOrMinimized() and not GSE.GUIDebugPaused and
            not GSE.DebugOutputFlushing
     then
        GSE.DebugOutputFlushing = true
        pcall(GSE.GUIUpdateOutput)
        GSE.DebugOutputFlushing = nil
    end
end

--- Prints <code>message</code>to the chat handler.  This accepts an optional
--    <code>module</code> that is used to identify whether debugging for that module
--    is currently enabled.
function GSE.PrintDebugMessage(message, module)
    if GSE.isEmpty(module) then
        module = "GS-Core"
    end
    local DebugModules, Debug, CommandColour, AuthorColour = {}, true, "|cFF00FF00", "|cFF00D1FF"
    if GSE.isEmpty(GSEOptions) then
        DebugModules[module] = false
    else
        DebugModules[module] = GSEOptions.DebugModules[module]
        Debug = GSEOptions.debug
        if GSEOptions.AuthorColour then
            AuthorColour = GSEOptions.AuthorColour
            CommandColour = GSEOptions.CommandColour
        end
    end
    if module == Statics.SequenceDebug then
        determinationOutputDestination(message, GNOME .. ":|r " .. AuthorColour .. L["<SEQUENCEDEBUG> |r "])
    elseif Debug and module ~= Statics.SequenceDebug and DebugModules[module] == true then
        determinationOutputDestination(
            (GSE.isEmpty(module) and GNOME or GNOME .. ": " .. CommandColour .. module) ..
                "|r " .. AuthorColour .. L["<DEBUG> |r "] .. message
        )
    end
end

function GSE.DebugProfile(event)
    local currentTimeStop = debugprofilestop()
    if GSE.ProfileStop then
        GSE.WagoAnalytics:SetCounter("Init_" .. event, currentTimeStop - GSE.ProfileStop)
    end
    GSE.ProfileStop = currentTimeStop
end

GSE.CurrentGCD = 1.5
GSE.RecorderActive = false

-- Macro Mode Status
GSE.PVPFlag = false
GSE.inRaid = false
GSE.inMythic = false
GSE.inDungeon = false
GSE.inHeroic = false
GSE.inParty = false

-- initialise debugprofilestart
GSE.DebugProfile("init")

-- ---------------------------------------------------------------------------
-- Submodule init dispatch
--
-- Submodules (GSE_Utils, GSE_Options, GSE_GUI, GSE_LDB, GSE_QoL) cannot reach
-- this addon's private namespace at parse-time. Each one exposes a single
-- global `<name>_Initialize` function that takes the GSE table and runs the
-- submodule's deferred setup. Reinit guards live inside each submodule.
-- ---------------------------------------------------------------------------
local SUBMODULES = {
    GSE_Utils = true,
    GSE_Options = true,
    GSE_GUI = true,
    GSE_LDB = true,
    GSE_QoL = true,
    GSE_Companion = true,
}

local function pushGSEInto(addon)
    if not SUBMODULES[addon] then return end
    local initFn = _G[addon .. "_Initialize"]
    if type(initFn) == "function" then
        initFn(GSE)
    end
end

-- If a submodule somehow loaded before main GSE (TOC dependencies should
-- prevent this, but cover the case anyway), push GSE into it now.
for name in pairs(SUBMODULES) do
    if C_AddOns.IsAddOnLoaded(name) then
        pushGSEInto(name)
    end
end

-- Catch submodules that load after us. AceEvent dispatches as
-- `self:method(eventname, ...payload)`, so the loaded addon name is the
-- second positional, not the first. Define the method BEFORE registering:
-- AceEvent (via CallbackHandler) checks that self[method] is callable at
-- registration time and errors if the slot is still nil.
function GSE:ADDON_LOADED(_, addon)
    if not SUBMODULES[addon] then return end
    pushGSEInto(addon)
end
GSE:RegisterEvent("ADDON_LOADED")
