local ldb = LibStub("LibDataBroker-1.1", true)
local dbicon = LibStub("LibDBIcon-1.0", true)
local L = LibStub("AceLocale-3.0"):GetLocale("CritMatic")
local qtip = LibStub("LibQTip-1.0", true)
local LSM = LibStub("LibSharedMedia-3.0", true)

local ICON_PATH = "Interface\\AddOns\\CritMatic\\Media\\Textures\\critmatic-medallion.tga"
local QTIP_KEY = "CritMaticBrokerTip"

local DEFAULT_ICONS = {
    damage = ICON_PATH,
    healing = "Interface\\Icons\\Spell_Holy_Heal",
    pet = "Interface\\Icons\\Ability_Hunter_BeastCall",
}

local function CategoryIcon(category)
    local broker = (Critmatic.db and Critmatic.db.profile and Critmatic.db.profile.brokerSettings) or {}
    local icons = broker.icons or {}
    return icons[category] or DEFAULT_ICONS[category] or ICON_PATH
end

local function EntryMatches(d, category)
    local G = _G.CritMaticRecordGrouping
    if G and G.EntryMatchesCategory then return G.EntryMatchesCategory(d, category) end
    return false
end

local brokerTipFont = CreateFont("CritMaticBrokerTipFont")

local function TopRecords()
    local bestCrit, bestCritName = 0, nil
    local bestHit, bestHitName = 0, nil
    local bestHealCrit, bestHealCritName = 0, nil
    local bestHeal, bestHealName = 0, nil
    for spellID, d in pairs(CritMaticData or {}) do
        if d.highestCrit and d.highestCrit > bestCrit then
            bestCrit = d.highestCrit
            bestCritName = Critmatic.GetRecordName(spellID, d)
        end
        if d.highestNormal and d.highestNormal > bestHit then
            bestHit = d.highestNormal
            bestHitName = Critmatic.GetRecordName(spellID, d)
        end
        if d.highestHealCrit and d.highestHealCrit > bestHealCrit then
            bestHealCrit = d.highestHealCrit
            bestHealCritName = Critmatic.GetRecordName(spellID, d)
        end
        if d.highestHeal and d.highestHeal > bestHeal then
            bestHeal = d.highestHeal
            bestHealName = Critmatic.GetRecordName(spellID, d)
        end
    end
    return bestCrit, bestCritName, bestHit, bestHitName, bestHealCrit, bestHealCritName, bestHeal, bestHealName
end

local function ColorHex(c, fallback)
    if type(c) ~= "table" then return fallback end
    local r = math.floor((c[1] or 1) * 255 + 0.5)
    local g = math.floor((c[2] or 1) * 255 + 0.5)
    local b = math.floor((c[3] or 1) * 255 + 0.5)
    return string.format("ff%02x%02x%02x", r, g, b)
end

local function AddCompactSummary(tooltip)
    local broker = (Critmatic.db and Critmatic.db.profile and Critmatic.db.profile.brokerSettings) or {}
    local bestCrit, bestCritName, bestHit, bestHitName, bestHealCrit, bestHealCritName, bestHeal, bestHealName = TopRecords()
    local function addCritLine()
        if bestCrit > 0 then
            tooltip:AddDoubleLine(L["broker_top_crit"], (bestCritName and (bestCrit .. "  " .. bestCritName)) or tostring(bestCrit))
        end
    end
    local function addHitLine()
        if bestHit > 0 then
            tooltip:AddDoubleLine(L["broker_top_hit"], (bestHitName and (bestHit .. "  " .. bestHitName)) or tostring(bestHit))
        end
    end
    if broker.hitFirst then
        addHitLine()
        addCritLine()
    else
        addCritLine()
        addHitLine()
    end
    if bestHealCrit > 0 then
        tooltip:AddDoubleLine(L["broker_top_crit_heal"], (bestHealCritName and (bestHealCrit .. "  " .. bestHealCritName)) or tostring(bestHealCrit))
    end
    if bestHeal > 0 then
        tooltip:AddDoubleLine(L["broker_top_heal"], (bestHealName and (bestHeal .. "  " .. bestHealName)) or tostring(bestHeal))
    end
end

local function BuildQtipTooltip(tip)
    local profile = Critmatic.db and Critmatic.db.profile
    local tfs = (profile and profile.tooltipFontSettings) or {}
    local broker = (profile and profile.brokerSettings) or {}
    local grayHex = "ffd4d4d4"

    local fontPath = (LSM and tfs.font and LSM:Fetch("font", tfs.font)) or _G.STANDARD_TEXT_FONT or "Fonts\\FRIZQT__.TTF"
    brokerTipFont:SetFont(fontPath, tfs.fontSize or 12, (tfs.fontOutline ~= "NONE" and tfs.fontOutline) or nil)
    tip:SetFont(brokerTipFont)
    tip:SetHeaderFont(brokerTipFont)

    local nameHex = ColorHex(Critmatic:ResolveColor("broker", "spellName"), "ffffd700")
    local critHex = ColorHex(Critmatic:ResolveColor("broker", "crit"), "ffffd700")
    local hitHex = ColorHex(Critmatic:ResolveColor("broker", "hit"), "ffe5e5e5")
    local critHealHex = ColorHex(Critmatic:ResolveColor("broker", "critHeal"), "ffffd700")
    local healHex = ColorHex(Critmatic:ResolveColor("broker", "heal"), "ffe5e5e5")
    local petCritHex = ColorHex(Critmatic:ResolveColor("broker", "petCrit"), "ffff9933")
    local petHitHex = ColorHex(Critmatic:ResolveColor("broker", "petHit"), "ffffcc80")

    tip:AddLine("|cffffd700CritMatic|r")

    local entries = (Critmatic.GetGroupedSpellRecords and Critmatic.GetGroupedSpellRecords({
        sortMode = tfs.sortMode,
        groupByType = tfs.groupByType,
    })) or {}

    local cap = broker.tooltipSpellCap or 15
    local groupByType = tfs.groupByType

    local function isIgnored(name)
        return Critmatic.ignoredSpells and Critmatic.ignoredSpells[name:lower()]
    end

    local function renderEntry(entry, showDamage, showHealing, isPet)
        local d = entry.data
        local idForIcon = (#entry.ids > 1 and entry.ids[2]) or entry.ids[1]
        local icon = d.spellIcon or select(3, GetSpellInfo(idForIcon))
        local iconPrefix = icon and ("|T" .. icon .. ":18|t ") or ""
        tip:AddLine(iconPrefix .. "|c" .. nameHex .. entry.name .. "|r")

        local cHex = isPet and petCritHex or critHex
        local hHex = isPet and petHitHex or hitHex
        local chHex = isPet and petCritHex or critHealHex
        local hlHex = isPet and petHitHex or healHex

        if showDamage then
            if d.highestCrit and d.highestCrit > 0 then
                local oldStr = ""
                if d.highestCritOld and d.highestCritOld > 0 then
                    oldStr = "  |c" .. grayHex .. "(" .. L["crit_log_old"] .. " " .. d.highestCritOld .. ")|r"
                end
                tip:AddLine("  |c" .. cHex .. L["crit_log_crit"] .. " " .. d.highestCrit .. "|r" .. oldStr)
            end
            if d.highestNormal and d.highestNormal > 0 then
                local oldStr = ""
                if d.highestNormalOld and d.highestNormalOld > 0 then
                    oldStr = "  |c" .. grayHex .. "(" .. L["crit_log_old"] .. " " .. d.highestNormalOld .. ")|r"
                end
                tip:AddLine("  |c" .. hHex .. L["crit_log_hit"] .. " " .. d.highestNormal .. "|r" .. oldStr)
            end
        end

        if showHealing then
            if d.highestHealCrit and d.highestHealCrit > 0 then
                local oldStr = ""
                if d.highestHealCritOld and d.highestHealCritOld > 0 then
                    oldStr = "  |c" .. grayHex .. "(" .. L["crit_log_old"] .. " " .. d.highestHealCritOld .. ")|r"
                end
                tip:AddLine("  |c" .. chHex .. L["crit_log_crit"] .. " " .. L["crit_log_heal"] .. " " .. d.highestHealCrit .. "|r" .. oldStr)
            end
            if d.highestHeal and d.highestHeal > 0 then
                local oldStr = ""
                if d.highestHealOld and d.highestHealOld > 0 then
                    oldStr = "  |c" .. grayHex .. "(" .. L["crit_log_old"] .. " " .. d.highestHealOld .. ")|r"
                end
                tip:AddLine("  |c" .. hlHex .. L["crit_log_heal"] .. " " .. d.highestHeal .. "|r" .. oldStr)
            end
        end
    end

    local shown = 0
    local total = 0

    if groupByType then
        local hasDamage, hasHealing, hasPet = false, false, false
        for _, entry in ipairs(entries) do
            if not isIgnored(entry.name) then
                if EntryMatches(entry.data, "damage") then hasDamage = true end
                if EntryMatches(entry.data, "healing") then hasHealing = true end
                if EntryMatches(entry.data, "pet") then hasPet = true end
            end
        end

        local function emitBlock(category, headerKey)
            local headerShown = false
            for _, entry in ipairs(entries) do
                if not isIgnored(entry.name) and EntryMatches(entry.data, category) then
                    total = total + 1
                    if shown < cap then
                        shown = shown + 1
                        if not headerShown then
                            headerShown = true
                            tip:AddHeader("|cffffd700" .. L[headerKey] .. "|r")
                        end
                        renderEntry(entry, category ~= "healing", category ~= "damage", category == "pet")
                    end
                end
            end
        end

        if tfs.groupOrder == "healing" then
            if hasHealing then emitBlock("healing", "appearance_section_healing") end
            if hasDamage then emitBlock("damage", "appearance_section_damage") end
        else
            if hasDamage then emitBlock("damage", "appearance_section_damage") end
            if hasHealing then emitBlock("healing", "appearance_section_healing") end
        end
        if hasPet then emitBlock("pet", "appearance_section_pet") end
    else
        for _, entry in ipairs(entries) do
            if not isIgnored(entry.name) then
                total = total + 1
                if shown < cap then
                    shown = shown + 1
                    renderEntry(entry, true, true)
                end
            end
        end
    end

    local remaining = total - shown
    if remaining > 0 then
        tip:AddLine("|c" .. grayHex .. string.format(L["broker_more_records"], remaining) .. "|r")
    end

    tip:AddLine("|c" .. grayHex .. L["broker_left_click_crit_log"] .. "|r")
    tip:AddLine("|c" .. grayHex .. L["broker_right_click_options"] .. "|r")
end

local function RenderFullLogTooltip(tooltip)
    local broker = (Critmatic.db and Critmatic.db.profile and Critmatic.db.profile.brokerSettings) or {}
    tooltip:AddLine("|cffffd700CritMatic|r")
    if broker.showFullLog ~= false then
        local nameHex = ColorHex(Critmatic:ResolveColor("broker", "spellName"), "ffffd700")
        local critHex = ColorHex(Critmatic:ResolveColor("broker", "crit"), "ffffd700")
        local hitHex = ColorHex(Critmatic:ResolveColor("broker", "hit"), "ffe5e5e5")
        local critHealHex = ColorHex(Critmatic:ResolveColor("broker", "critHeal"), "ffffd700")
        local healHex = ColorHex(Critmatic:ResolveColor("broker", "heal"), "ffe5e5e5")
        local grayHex = "ffd4d4d4"
        local entries = (Critmatic.GetGroupedSpellRecords and Critmatic.GetGroupedSpellRecords()) or {}
        local cap = broker.tooltipSpellCap or 15
        local shown, total = 0, 0
        for _, entry in ipairs(entries) do
            local name = entry.name
            if not (Critmatic.ignoredSpells and Critmatic.ignoredSpells[name:lower()]) then
                total = total + 1
                if shown < cap then
                    shown = shown + 1
                    local d = entry.data
                    local idForIcon = (#entry.ids > 1 and entry.ids[2]) or entry.ids[1]
                    local icon = d.spellIcon or select(3, GetSpellInfo(idForIcon))
                    local nameLine = "|c" .. nameHex .. name .. "|r"
                    if icon then
                        nameLine = "|T" .. icon .. ":18|t " .. nameLine
                    end
                    tooltip:AddLine(nameLine)
                    if d.highestCrit and d.highestCrit > 0 then
                        tooltip:AddLine(string.format("  |c%s%s %s|r |c%s(%s %s)|r", critHex, L["crit_log_crit"], d.highestCrit, grayHex, L["crit_log_old"], d.highestCritOld or 0))
                    end
                    if d.highestNormal and d.highestNormal > 0 then
                        tooltip:AddLine(string.format("  |c%s%s %s|r |c%s(%s %s)|r", hitHex, L["crit_log_hit"], d.highestNormal, grayHex, L["crit_log_old"], d.highestNormalOld or 0))
                    end
                    if d.highestHealCrit and d.highestHealCrit > 0 then
                        tooltip:AddLine(string.format("  |c%s%s %s %s|r |c%s(%s %s)|r", critHealHex, L["crit_log_crit"], L["crit_log_heal"], d.highestHealCrit, grayHex, L["crit_log_old"], d.highestHealCritOld or 0))
                    end
                    if d.highestHeal and d.highestHeal > 0 then
                        tooltip:AddLine(string.format("  |c%s%s %s|r |c%s(%s %s)|r", healHex, L["crit_log_heal"], d.highestHeal, grayHex, L["crit_log_old"], d.highestHealOld or 0))
                    end
                end
            end
        end
        if total > shown then
            tooltip:AddLine(string.format(L["broker_more_records"], total - shown), 0.8, 0.8, 0.8)
        end
    else
        AddCompactSummary(tooltip)
    end
    tooltip:AddLine(L["broker_left_click_crit_log"], 0.8, 0.8, 0.8)
    tooltip:AddLine(L["broker_right_click_options"], 0.8, 0.8, 0.8)
end

local CATEGORY_DEFS = {
    { key = "damage",  name = "CritMatic_Damage",  labelKey = "broker_damage_label",  minimapKey = "minimapDamage" },
    { key = "healing", name = "CritMatic_Healing", labelKey = "broker_healing_label", minimapKey = "minimapHealing" },
    { key = "pet",     name = "CritMatic_Pets",    labelKey = "broker_pet_label",     minimapKey = "minimapPets" },
}

local CATEGORY_HEADER_KEY = {
    damage = "appearance_section_damage",
    healing = "appearance_section_healing",
    pet = "broker_section_pet",
}

Critmatic.categoryBrokers = Critmatic.categoryBrokers or {}

-- One pass over CritMaticData computing the overall best crit/hit plus the
-- per-category pairs. UpdateBroker runs on every new record, so this replaces
-- the old TopRecords + 3x CategoryTopPair layout (4 full scans, plus a
-- throwaway kind table per spell per category) with a single allocation-free
-- scan.
local function ScanBrokerStats()
    local bestCrit, bestHit = 0, 0
    local dmgCrit, dmgHit = 0, 0
    local healCrit, healHit = 0, 0
    local petCrit, petHit = 0, 0
    for _, d in pairs(CritMaticData or {}) do
        local hc, hn = d.highestCrit or 0, d.highestNormal or 0
        local hhc, hh = d.highestHealCrit or 0, d.highestHeal or 0
        if hc > bestCrit then bestCrit = hc end
        if hn > bestHit then bestHit = hn end
        if d.isPet then
            local pc = hc > hhc and hc or hhc
            local ph = hn > hh and hn or hh
            if pc > petCrit then petCrit = pc end
            if ph > petHit then petHit = ph end
        else
            if hc > dmgCrit then dmgCrit = hc end
            if hn > dmgHit then dmgHit = hn end
            if hhc > healCrit then healCrit = hhc end
            if hh > healHit then healHit = hh end
        end
    end
    return bestCrit, bestHit, dmgCrit, dmgHit, healCrit, healHit, petCrit, petHit
end

local function RenderCategoryTooltip(tooltip, category)
    local profile = Critmatic.db and Critmatic.db.profile
    local grayHex = "ffd4d4d4"
    local isPet = category == "pet"
    local nameHex = ColorHex(Critmatic:ResolveColor("broker", "spellName"), "ffffd700")
    local critHex = ColorHex(Critmatic:ResolveColor("broker", isPet and "petCrit" or "crit"), "ffffd700")
    local hitHex = ColorHex(Critmatic:ResolveColor("broker", isPet and "petHit" or "hit"), "ffe5e5e5")
    local critHealHex = ColorHex(Critmatic:ResolveColor("broker", isPet and "petCrit" or "critHeal"), "ffffd700")
    local healHex = ColorHex(Critmatic:ResolveColor("broker", isPet and "petHit" or "heal"), "ffe5e5e5")
    tooltip:AddLine("|cffffd700CritMatic|r")
    tooltip:AddLine("|c" .. grayHex .. L[CATEGORY_HEADER_KEY[category]] .. "|r")
    local broker = (profile and profile.brokerSettings) or {}
    local cap = broker.tooltipSpellCap or 15
    local entries = (Critmatic.GetGroupedSpellRecords and Critmatic.GetGroupedSpellRecords()) or {}
    local showDamage = category ~= "healing"
    local showHealing = category ~= "damage"
    local shown, total = 0, 0
    for _, entry in ipairs(entries) do
        local name = entry.name
        if name and not (Critmatic.ignoredSpells and Critmatic.ignoredSpells[name:lower()])
                and EntryMatches(entry.data, category) then
            total = total + 1
            if shown < cap then
                shown = shown + 1
                local d = entry.data
                local idForIcon = (#entry.ids > 1 and entry.ids[2]) or entry.ids[1]
                local icon = d.spellIcon or select(3, GetSpellInfo(idForIcon))
                local nameLine = "|c" .. nameHex .. name .. "|r"
                if icon then nameLine = "|T" .. icon .. ":18|t " .. nameLine end
                tooltip:AddLine(nameLine)
                if showDamage and d.highestCrit and d.highestCrit > 0 then
                    tooltip:AddLine(string.format("  |c%s%s %s|r |c%s(%s %s)|r", critHex, L["crit_log_crit"], d.highestCrit, grayHex, L["crit_log_old"], d.highestCritOld or 0))
                end
                if showDamage and d.highestNormal and d.highestNormal > 0 then
                    tooltip:AddLine(string.format("  |c%s%s %s|r |c%s(%s %s)|r", hitHex, L["crit_log_hit"], d.highestNormal, grayHex, L["crit_log_old"], d.highestNormalOld or 0))
                end
                if showHealing and d.highestHealCrit and d.highestHealCrit > 0 then
                    tooltip:AddLine(string.format("  |c%s%s %s %s|r |c%s(%s %s)|r", critHealHex, L["crit_log_crit"], L["crit_log_heal"], d.highestHealCrit, grayHex, L["crit_log_old"], d.highestHealCritOld or 0))
                end
                if showHealing and d.highestHeal and d.highestHeal > 0 then
                    tooltip:AddLine(string.format("  |c%s%s %s|r |c%s(%s %s)|r", healHex, L["crit_log_heal"], d.highestHeal, grayHex, L["crit_log_old"], d.highestHealOld or 0))
                end
            end
        end
    end
    if total == 0 then
        tooltip:AddLine("|c" .. grayHex .. L["broker_no_records"] .. "|r")
    elseif total > shown then
        tooltip:AddLine("|c" .. grayHex .. string.format(L["broker_more_records"], total - shown) .. "|r")
    end
    tooltip:AddLine("|c" .. grayHex .. L["broker_left_click_crit_log"] .. "|r")
    tooltip:AddLine("|c" .. grayHex .. L["broker_right_click_options"] .. "|r")
end

local function BuildCategoryQtip(tip, category)
    local profile = Critmatic.db and Critmatic.db.profile
    local tfs = (profile and profile.tooltipFontSettings) or {}
    local fontPath = (LSM and tfs.font and LSM:Fetch("font", tfs.font)) or _G.STANDARD_TEXT_FONT or "Fonts\\FRIZQT__.TTF"
    brokerTipFont:SetFont(fontPath, tfs.fontSize or 12, (tfs.fontOutline ~= "NONE" and tfs.fontOutline) or nil)
    tip:SetFont(brokerTipFont)
    tip:SetHeaderFont(brokerTipFont)
    RenderCategoryTooltip(tip, category)
end

if ldb then
    Critmatic.brokerObject = ldb:NewDataObject("CritMatic", {
        type = "data source",
        text = "CritMatic",
        icon = ICON_PATH,
        OnClick = function(_, button)
            if button == "RightButton" then
                Critmatic:OpenOptions()
            else
                Critmatic:OpenCritLog()
            end
        end,
        OnEnter = function(self)
            local broker = (Critmatic.db and Critmatic.db.profile and Critmatic.db.profile.brokerSettings) or {}
            local ok = false
            if qtip and broker.showFullLog ~= false then
                ok = pcall(function()
                    local tip = qtip:Acquire(QTIP_KEY, 1, "LEFT")
                    BuildQtipTooltip(tip)
                    tip:SmartAnchorTo(self)
                    tip:Show()
                    Critmatic.brokerObject._activeTip = tip
                end)
            end
            if not ok then
                GameTooltip:SetOwner(self, "ANCHOR_NONE")
                GameTooltip:SetPoint("TOPLEFT", self, "BOTTOMLEFT", 0, -4)
                GameTooltip:ClearLines()
                RenderFullLogTooltip(GameTooltip)
                GameTooltip:Show()
            end
        end,
        OnLeave = function(self)
            local tip = Critmatic.brokerObject._activeTip
            if tip then
                qtip:Release(tip)
                Critmatic.brokerObject._activeTip = nil
            else
                GameTooltip:Hide()
            end
        end,
    })

    for _, def in ipairs(CATEGORY_DEFS) do
        local category = def.key
        local obj = ldb:NewDataObject(def.name, {
            type = "data source",
            label = L[def.labelKey],
            text = L[def.labelKey],
            icon = CategoryIcon(category),
            OnClick = function(_, button)
                if button == "RightButton" then
                    Critmatic:OpenOptions()
                else
                    Critmatic:OpenCritLog()
                end
            end,
            OnEnter = function(self)
                local ok = false
                if qtip then
                    ok = pcall(function()
                        local tip = qtip:Acquire(QTIP_KEY .. "_" .. category, 1, "LEFT")
                        BuildCategoryQtip(tip, category)
                        tip:SmartAnchorTo(self)
                        tip:Show()
                        self._cmCategoryTip = tip
                    end)
                end
                if not ok then
                    GameTooltip:SetOwner(self, "ANCHOR_NONE")
                    GameTooltip:SetPoint("TOPLEFT", self, "BOTTOMLEFT", 0, -4)
                    GameTooltip:ClearLines()
                    RenderCategoryTooltip(GameTooltip, category)
                    GameTooltip:Show()
                end
            end,
            OnLeave = function(self)
                if self._cmCategoryTip then
                    qtip:Release(self._cmCategoryTip)
                    self._cmCategoryTip = nil
                else
                    GameTooltip:Hide()
                end
            end,
        })
        Critmatic.categoryBrokers[category] = obj
    end
end

function Critmatic.UpdateBroker()
    if not Critmatic.brokerObject then return end
    local Format = _G.CritMaticBrokerFormat
    if not Format then return end

    local profile = Critmatic.db and Critmatic.db.profile
    local broker = (profile and profile.brokerSettings) or {}

    local function hexFor(category)
        local critKey, hitKey = Format.CategoryColorKeys(category)
        return ColorHex(Critmatic:ResolveColor("broker", critKey), "ffffd700"), ColorHex(Critmatic:ResolveColor("broker", hitKey), "ffffffff")
    end

    local critLabel, hitLabel, separator = Format.StyleParts({
        compactNumbers = broker.compactNumbers,
        compactInitials = broker.compactInitials,
        compactLabels = broker.compactLabels,
        critInitial = L["broker_crit_initial"],
        hitInitial = L["broker_hit_initial"],
        critShort = L["broker_crit_short"],
        hitShort = L["broker_hit_short"],
        critFull = L["broker_top_crit"],
        hitFull = L["broker_top_hit"],
    })

    local function feedText(critVal, hitVal, fallback, critHex, hitHex)
        return Format.RecordPair({
            critValue = critVal,
            hitValue = hitVal,
            critHex = critHex,
            hitHex = hitHex,
            critLabel = critLabel,
            hitLabel = hitLabel,
            separator = separator,
            hitFirst = broker.hitFirst,
            fallback = fallback,
        })
    end

    local bestCrit, bestHit, dmgCrit, dmgHit, healCrit, healHit, petCrit, petHit = ScanBrokerStats()
    local mainCritHex, mainHitHex = hexFor("damage")
    Critmatic.brokerObject.text = feedText(bestCrit, bestHit, "CritMatic", mainCritHex, mainHitHex)

    if Critmatic.categoryBrokers then
        for _, def in ipairs(CATEGORY_DEFS) do
            local obj = Critmatic.categoryBrokers[def.key]
            if obj then
                local catCrit, catHit
                if def.key == "damage" then
                    catCrit, catHit = dmgCrit, dmgHit
                elseif def.key == "healing" then
                    catCrit, catHit = healCrit, healHit
                else
                    catCrit, catHit = petCrit, petHit
                end
                local cHex, hHex = hexFor(def.key)
                obj.text = feedText(catCrit, catHit, L[def.labelKey], cHex, hHex)
                local ic = CategoryIcon(def.key)
                if obj.icon ~= ic then obj.icon = ic end
            end
        end
    end
end

function Critmatic.SetupMinimapIcon()
    if not dbicon or not Critmatic.brokerObject then return end
    if not Critmatic.db or not Critmatic.db.profile then return end
    Critmatic.db.profile.minimap = Critmatic.db.profile.minimap or { hide = false }
    if not dbicon:IsRegistered("CritMatic") then
        dbicon:Register("CritMatic", Critmatic.brokerObject, Critmatic.db.profile.minimap)
    elseif dbicon.Refresh then
        dbicon:Refresh("CritMatic", Critmatic.db.profile.minimap)
    end
    if Critmatic.db.profile.minimap.hide then
        dbicon:Hide("CritMatic")
    else
        dbicon:Show("CritMatic")
    end

    for _, def in ipairs(CATEGORY_DEFS) do
        local obj = Critmatic.categoryBrokers and Critmatic.categoryBrokers[def.key]
        if obj then
            local mm = Critmatic.db.profile[def.minimapKey]
            if not mm then
                mm = { hide = true }
                Critmatic.db.profile[def.minimapKey] = mm
            end
            if not dbicon:IsRegistered(def.name) then
                dbicon:Register(def.name, obj, mm)
            elseif dbicon.Refresh then
                dbicon:Refresh(def.name, mm)
            end
            if mm.hide then
                dbicon:Hide(def.name)
            else
                dbicon:Show(def.name)
            end
        end
    end
end

function Critmatic.SetCategoryMinimapHidden(category, hide)
    if not Critmatic.db or not Critmatic.db.profile then return end
    local def
    for _, d in ipairs(CATEGORY_DEFS) do
        if d.key == category then def = d break end
    end
    if not def then return end
    local mm = Critmatic.db.profile[def.minimapKey] or { hide = true }
    mm.hide = hide and true or false
    Critmatic.db.profile[def.minimapKey] = mm
    if not dbicon then return end
    local obj = Critmatic.categoryBrokers and Critmatic.categoryBrokers[category]
    if obj and not dbicon:IsRegistered(def.name) then
        dbicon:Register(def.name, obj, mm)
    end
    if mm.hide then
        dbicon:Hide(def.name)
    else
        dbicon:Show(def.name)
    end
end

function Critmatic.SetMinimapHidden(hide)
    if not Critmatic.db or not Critmatic.db.profile then return end
    Critmatic.db.profile.minimap = Critmatic.db.profile.minimap or { hide = false }
    Critmatic.db.profile.minimap.hide = hide and true or false
    if not dbicon then return end
    if hide then
        dbicon:Hide("CritMatic")
    else
        dbicon:Show("CritMatic")
    end
end
