local ADDON_NAME = "HailMaryGuildTools" local HMGT = _G[ADDON_NAME] if not HMGT then return end local L = HMGT.L or LibStub("AceLocale-3.0"):GetLocale(ADDON_NAME) HMGT.devToolsBuffer = HMGT.devToolsBuffer or {} HMGT.devToolsBufferMax = HMGT.devToolsBufferMax or 300 local DEVTOOLS_SCOPE_ALL = "ALL" local DEVTOOLS_SCOPE_LABELS = { System = "System", Version = "Version", Options = "Options", Comm = "Communication", Tracker = "Tracker", RaidTimeline = "Raid Timeline", Notes = "Notes", } local DEVTOOLS_LEVELS = { error = 1, trace = 2, } local function TrimText(value) return tostring(value or ""):gsub("^%s+", ""):gsub("%s+$", "") end local function SortKeys(tbl) local keys = {} for key in pairs(tbl or {}) do keys[#keys + 1] = key end table.sort(keys, function(a, b) return tostring(a) < tostring(b) end) return keys end local function EncodePayloadValue(value, depth) depth = tonumber(depth) or 0 local valueType = type(value) if valueType == "nil" then return "nil" end if valueType == "string" then return value end if valueType == "number" or valueType == "boolean" then return tostring(value) end if valueType == "table" then if depth >= 1 then return "{...}" end local parts = {} for _, key in ipairs(SortKeys(value)) do parts[#parts + 1] = string.format("%s=%s", tostring(key), EncodePayloadValue(value[key], depth + 1)) end return table.concat(parts, ", ") end return tostring(value) end function HMGT:GetDevToolsSettings() local profile = self.db and self.db.profile if not profile then return { enabled = false, level = "error", scope = DEVTOOLS_SCOPE_ALL, window = { width = 920, height = 420, minimized = false }, } end profile.devTools = type(profile.devTools) == "table" and profile.devTools or {} local settings = profile.devTools settings.enabled = settings.enabled == true if settings.level ~= "error" and settings.level ~= "trace" then settings.level = "error" end if type(settings.scope) ~= "string" or settings.scope == "" then settings.scope = DEVTOOLS_SCOPE_ALL end settings.window = type(settings.window) == "table" and settings.window or {} settings.window.width = math.max(720, tonumber(settings.window.width) or 920) settings.window.height = math.max(260, tonumber(settings.window.height) or 420) settings.window.minimized = settings.window.minimized == true return settings end function HMGT:IsDevToolsEnabled() return self:GetDevToolsSettings().enabled == true end function HMGT:GetDevToolsLevelOptions() return { error = L["OPT_DEVTOOLS_LEVEL_ERROR"] or "Errors", trace = L["OPT_DEVTOOLS_LEVEL_TRACE"] or "Trace", } end function HMGT:GetConfiguredDevToolsLevel() return self:GetDevToolsSettings().level or "error" end function HMGT:ShouldIncludeDevToolsLevel(level) local configured = self:GetConfiguredDevToolsLevel() return (DEVTOOLS_LEVELS[tostring(level or "error")] or DEVTOOLS_LEVELS.error) <= (DEVTOOLS_LEVELS[configured] or DEVTOOLS_LEVELS.error) end function HMGT:GetDevToolsScopeOptions() local values = { [DEVTOOLS_SCOPE_ALL] = L["OPT_DEVTOOLS_SCOPE_ALL"] or "All scopes", } for scope, label in pairs(DEVTOOLS_SCOPE_LABELS) do values[scope] = label end for _, entry in ipairs(self.devToolsBuffer or {}) do local scope = TrimText(entry and entry.scope or "") if scope ~= "" and scope ~= DEVTOOLS_SCOPE_ALL then values[scope] = values[scope] or scope end end return values end function HMGT:FormatDevToolsEntry(entry) local stamp = tostring(entry and entry.stamp or date("%H:%M:%S")) local level = string.upper(tostring(entry and entry.level or "error")) local scope = tostring(entry and entry.scope or "System") local eventName = tostring(entry and entry.event or "") local payload = TrimText(entry and entry.payload or "") if payload ~= "" then return string.format("%s [%s][%s] %s | %s", stamp, level, scope, eventName, payload) end return string.format("%s [%s][%s] %s", stamp, level, scope, eventName) end function HMGT:GetFilteredDevToolsEntries() local filtered = {} local settings = self:GetDevToolsSettings() for _, entry in ipairs(self.devToolsBuffer or {}) do local scopeMatches = settings.scope == DEVTOOLS_SCOPE_ALL or settings.scope == tostring(entry.scope or "") if scopeMatches and self:ShouldIncludeDevToolsLevel(entry.level) then filtered[#filtered + 1] = entry end end return filtered end function HMGT:GetFilteredDevToolsLines() local lines = {} for _, entry in ipairs(self:GetFilteredDevToolsEntries()) do lines[#lines + 1] = self:FormatDevToolsEntry(entry) end return lines end function HMGT:RecordDevEvent(level, scope, eventName, payload) if not self:IsDevToolsEnabled() then return end local normalizedLevel = tostring(level or "error") if normalizedLevel ~= "error" and normalizedLevel ~= "trace" then normalizedLevel = "trace" end if not self:ShouldIncludeDevToolsLevel(normalizedLevel) then return end local normalizedScope = TrimText(scope or "System") if normalizedScope == "" then normalizedScope = "System" end local entry = { stamp = date("%H:%M:%S"), level = normalizedLevel, scope = normalizedScope, event = TrimText(eventName or "event"), payload = EncodePayloadValue(payload, 0), } table.insert(self.devToolsBuffer, entry) if #self.devToolsBuffer > (tonumber(self.devToolsBufferMax) or 300) then table.remove(self.devToolsBuffer, 1) end if self.devToolsWindow and self.devToolsWindow:IsShown() and self.RefreshDevToolsWindow then self:RefreshDevToolsWindow() end end function HMGT:DevError(scope, eventName, payload) self:RecordDevEvent("error", scope, eventName, payload) end function HMGT:DevTrace(scope, eventName, payload) self:RecordDevEvent("trace", scope, eventName, payload) end function HMGT:ClearDevToolsLog() wipe(self.devToolsBuffer) if self.devToolsWindow and self.devToolsWindow.editBox then self.devToolsWindow.editBox:SetText("") self.devToolsWindow.editBox:SetCursorPosition(0) end end function HMGT:DumpDevToolsLog(maxLines) if self:IsDevToolsEnabled() and self.OpenDevToolsWindow then self:OpenDevToolsWindow() return end local lines = tonumber(maxLines) or 40 if lines < 1 then lines = 1 end local startIndex = math.max(1, #self.devToolsBuffer - lines + 1) for i = startIndex, #self.devToolsBuffer do self:Print(self:FormatDevToolsEntry(self.devToolsBuffer[i])) end end