-- ============================================================
-- Asylum Compagnon — Bars.lua
-- Widget de barre réutilisable (esprit Portal Authority) : icône
-- carrée à gauche + StatusBar qui se vide en temps réel, textes
-- gauche/droite superposés. Utilisé par les overlays kicks & CD.
-- NS.CreateBar(parent, w, h) → frame avec :Set(icon, col, l, r, pct, rcol)
-- ============================================================

local ADDON_NAME, NS = ...

local WHITE   = "Interface\\Buttons\\WHITE8x8"
local FONT    = "Fonts\\FRIZQT__.TTF"
local BAR_TEX = "Interface\\TargetingFrame\\UI-StatusBar"

function NS.ClassRGB(token)
    local c = token and RAID_CLASS_COLORS and RAID_CLASS_COLORS[token]
    if c then return { c.r, c.g, c.b } end
    return { 0.6, 0.6, 0.62 }
end

-- Chrome commun des encadrés (kicks & cooldowns) : fond réglable, bordure
-- 1 px, liseré accent, petit titre. Une seule implémentation (avant : ~45
-- lignes dupliquées dans chaque module). Renvoie { bg, borders } pour les
-- ApplyStyle respectifs.
-- lockApi (optionnel) = { get = fn()->bool, set = fn(bool) } : ajoute un petit
-- cadenas en haut à droite de la barre de titre pour verrouiller/déverrouiller le
-- cadre sans passer par le menu. Enfant du cadre + niveau relevé : sinon le cadre
-- déplaçable (OnMouseDown = StartMoving) capterait le clic.
function NS.MakeOverlayChrome(frame, titleText, lockApi)
    local bg = frame:CreateTexture(nil, "BACKGROUND")
    bg:SetTexture(WHITE)
    bg:SetAllPoints()

    local borders = {}
    for _, side in ipairs({ "TOP", "BOTTOM", "LEFT", "RIGHT" }) do
        local t = frame:CreateTexture(nil, "BORDER")
        t:SetTexture(WHITE)
        if side == "TOP" then
            t:SetPoint("TOPLEFT"); t:SetPoint("TOPRIGHT"); t:SetHeight(1)
        elseif side == "BOTTOM" then
            t:SetPoint("BOTTOMLEFT"); t:SetPoint("BOTTOMRIGHT"); t:SetHeight(1)
        elseif side == "LEFT" then
            t:SetPoint("TOPLEFT"); t:SetPoint("BOTTOMLEFT"); t:SetWidth(1)
        else
            t:SetPoint("TOPRIGHT"); t:SetPoint("BOTTOMRIGHT"); t:SetWidth(1)
        end
        borders[#borders + 1] = t
    end

    local accent = frame:CreateTexture(nil, "BORDER")
    accent:SetTexture(WHITE)
    accent:SetPoint("TOPLEFT"); accent:SetPoint("BOTTOMLEFT")
    accent:SetWidth(2)
    accent:SetVertexColor(0.655, 0.545, 0.98, 0.9)

    local title = frame:CreateFontString(nil, "OVERLAY")
    title:SetFont(FONT, 10, "OUTLINE")
    title:SetTextColor(0.65, 0.66, 0.72, 1)
    title:SetPoint("TOPLEFT", 10, -7)
    title:SetText(titleText or "")

    -- cadenas de verrouillage (optionnel)
    local syncLock
    if lockApi then
        local lb = CreateFrame("Button", nil, frame)
        lb:SetSize(14, 14)
        lb:SetPoint("TOPRIGHT", -3, -3)
        lb:SetFrameLevel((frame:GetFrameLevel() or 1) + 10)
        lb:RegisterForClicks("LeftButtonUp")
        local tex = lb:CreateTexture(nil, "OVERLAY")
        tex:SetAllPoints()
        syncLock = function()
            local locked = lockApi.get and lockApi.get()
            tex:SetTexture(locked and "Interface\\Buttons\\LockButton-Locked-Up"
                or "Interface\\Buttons\\LockButton-Unlocked-Up")
            tex:SetAlpha(locked and 0.95 or 0.55)
        end
        lb:SetScript("OnClick", function()
            if lockApi.set then lockApi.set(not (lockApi.get and lockApi.get())) end
            syncLock()
        end)
        lb:SetScript("OnEnter", function()
            GameTooltip:SetOwner(lb, "ANCHOR_LEFT")
            GameTooltip:SetText((lockApi.get and lockApi.get())
                and NS.L("Déverrouiller le cadre") or NS.L("Verrouiller le cadre"))
            GameTooltip:Show()
        end)
        lb:SetScript("OnLeave", function() GameTooltip:Hide() end)
        syncLock()
    end

    return { bg = bg, borders = borders, syncLock = syncLock }
end

-- Icône d'un sort (sécurisé Midnight)
function NS.SpellIcon(spellID)
    local fn = (C_Spell and C_Spell.GetSpellTexture) or GetSpellTexture
    if not fn or not spellID then return nil end
    local ok, tex = pcall(fn, spellID)
    if ok then return tex end
end

-- Icône de cooldown façon OmniCD : icône du sort + balayage (Cooldown frame)
-- + décompte au centre. Style v0.26 (maquette validée) : PRÊT = icône nette,
-- bordure neutre (le calme = la norme, fini le mur de bordures vertes) ;
-- EN RECHARGE = désaturée + assombrie + décompte + fine jauge rouge en bas
-- qui se vide avec le CD. Survol → tooltip (SetInfo), bord teintable.
function NS.CreateCDIcon(parent, size)
    size = size or 22
    local f = CreateFrame("Frame", nil, parent)
    f:SetSize(size, size)
    f._size = size

    local border = f:CreateTexture(nil, "BACKGROUND")
    border:SetPoint("TOPLEFT", -1, 1)
    border:SetPoint("BOTTOMRIGHT", 1, -1)
    border:SetColorTexture(0, 0, 0, 0.9)
    f.border = border

    local tex = f:CreateTexture(nil, "ARTWORK")
    tex:SetAllPoints()
    tex:SetTexCoord(0.08, 0.92, 0.08, 0.92)
    f.tex = tex

    local cdf = CreateFrame("Cooldown", nil, f, "CooldownFrameTemplate")
    cdf:SetAllPoints()
    if cdf.SetDrawEdge then cdf:SetDrawEdge(true) end
    if cdf.SetSwipeColor then cdf:SetSwipeColor(0, 0, 0, 0.78) end
    if cdf.SetHideCountdownNumbers then cdf:SetHideCountdownNumbers(true) end
    f.cdf = cdf

    -- fine jauge de recharge (2 px) en bas de l'icône, se vide avec le CD
    local cdLine = f:CreateTexture(nil, "OVERLAY")
    cdLine:SetHeight(2)
    cdLine:SetPoint("BOTTOMLEFT", 0, 0)
    cdLine:SetColorTexture(0.88, 0.34, 0.30, 0.95)
    cdLine:Hide()
    f.cdLine = cdLine

    local count = f:CreateFontString(nil, "OVERLAY")
    count:SetFont(FONT, math.max(10, size - 9), "OUTLINE")
    count:SetPoint("CENTER", 0, 0)
    f.count = count

    -- compteur de charges (coin bas-droit)
    local chargeText = f:CreateFontString(nil, "OVERLAY")
    chargeText:SetFont(FONT, math.max(9, size - 11), "OUTLINE")
    chargeText:SetPoint("BOTTOMRIGHT", 1, -1)
    f.chargeText = chargeText

    -- tooltip au survol (sans bloquer le déplacement du cadre : seul l'icône
    -- capte la souris, le reste du cadre reste déplaçable)
    f:EnableMouse(true)
    f:SetScript("OnEnter", function(self)
        if not (self.spellID or self.title) then return end
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        local shown = false
        if self.spellID and GameTooltip.SetSpellByID then
            shown = pcall(GameTooltip.SetSpellByID, GameTooltip, self.spellID)
        end
        if not shown then GameTooltip:SetText(self.title or "") end
        if self.subText and self.subText ~= "" then
            GameTooltip:AddLine(self.subText, 0.72, 0.78, 0.92)
        end
        if self.catText and self.catText ~= "" then
            GameTooltip:AddLine(self.catText, 0.55, 0.75, 1)
        end
        GameTooltip:Show()
    end)
    f:SetScript("OnLeave", function() GameTooltip:Hide() end)

    function f:SetInfo(spellID, title, subText, catText)
        self.spellID, self.title, self.subText, self.catText = spellID, title, subText, catText
    end
    function f:SetBorderColor(r, g, b, a)
        self.border:SetColorTexture(r, g, b, a or 0.9)
    end

    -- SetCooldown relancé UNIQUEMENT quand start/durée changent : le balayage
    -- s'anime tout seul côté client (code natif), le rappeler à chaque tick
    -- était le plus gros poste CPU du board.
    local function ApplySwipe(self, start, dur)
        if start and dur and dur > 0 then
            if start ~= self._cdStart or dur ~= self._cdDur then
                self._cdStart, self._cdDur = start, dur
                pcall(self.cdf.SetCooldown, self.cdf, start, dur)
            end
        elseif self._cdStart then
            self._cdStart, self._cdDur = nil, nil
            self.cdf:Clear()
        end
    end

    -- petite jauge rouge : largeur = fraction de CD restante
    local function ApplyLine(self, remaining, dur)
        if remaining and dur and dur > 0 and remaining > 0 then
            local w = self._size * math.max(0, math.min(1, remaining / dur))
            if w >= 1 then
                self.cdLine:SetWidth(w)
                self.cdLine:Show()
                return
            end
        end
        self.cdLine:Hide()
    end

    function f:SetState(iconTex, start, duration, remaining)
        if iconTex then self.tex:SetTexture(iconTex) end
        self.chargeText:SetText("")
        self.border:SetColorTexture(0, 0, 0, 0.9) -- neutre (SetBorderColor peut surcharger après)
        if remaining and remaining > 0 then
            ApplySwipe(self, start, duration)
            self.tex:SetDesaturated(true)
            self.tex:SetVertexColor(0.72, 0.72, 0.72)
            local txt = (remaining >= 60) and (("%dm"):format(math.ceil(remaining / 60)))
                or (("%d"):format(math.ceil(remaining)))
            self.count:SetText(txt)
            self.count:SetTextColor(0.97, 0.86, 0.4)
            ApplyLine(self, remaining, duration)
        else
            ApplySwipe(self, nil, nil)
            self.tex:SetDesaturated(false)
            self.tex:SetVertexColor(1, 1, 1)
            self.count:SetText("")
            self.cdLine:Hide()
        end
    end
    function f:SetCount(t, r, g, b)
        self.count:SetText(t or "")
        if r then self.count:SetTextColor(r, g, b) end
    end
    -- état complet avec charges : o = { onCD, start, dur, remaining, cur, max }
    -- charge dispo (cur>0) = prêt (pas de swipe) ; 0 charge = CD de la prochaine
    function f:SetCD(iconTex, o)
        if iconTex then self.tex:SetTexture(iconTex) end
        o = o or {}
        self.border:SetColorTexture(0, 0, 0, 0.9) -- neutre dans tous les états
        if o.max and o.max > 1 then
            self.chargeText:SetText(tostring(o.cur or 0))
            if (o.cur or 0) > 0 then self.chargeText:SetTextColor(0.95, 0.95, 0.98)
            else self.chargeText:SetTextColor(0.95, 0.45, 0.4) end
        else
            self.chargeText:SetText("")
        end
        if o.onCD then
            ApplySwipe(self, o.start, o.dur)
            self.tex:SetDesaturated(true)
            self.tex:SetVertexColor(0.72, 0.72, 0.72)
            local rem = o.remaining or 0
            local txt = (rem >= 60) and (("%dm"):format(math.ceil(rem / 60)))
                or (("%d"):format(math.ceil(rem)))
            self.count:SetText(txt)
            self.count:SetTextColor(0.97, 0.86, 0.4)
            ApplyLine(self, rem, o.dur)
        else
            ApplySwipe(self, nil, nil)
            self.tex:SetDesaturated(false)
            self.tex:SetVertexColor(1, 1, 1)
            self.count:SetText("")
            self.cdLine:Hide()
        end
    end
    return f
end

-- Barre pleine largeur : la StatusBar va jusqu'au bout à gauche ; l'icône du
-- sort et la coche ✓/✗ sont À L'INTÉRIEUR de la barre, entre le nom (gauche)
-- et l'état/décompte (droite).
function NS.CreateBar(parent, width, height)
    local bar = CreateFrame("Frame", nil, parent)
    bar:SetSize(width, height)

    local sb = CreateFrame("StatusBar", nil, bar)
    sb:SetAllPoints()
    sb:SetStatusBarTexture(BAR_TEX)
    sb:SetMinMaxValues(0, 1)
    sb:SetValue(1)
    bar.sb = sb

    local sbBg = sb:CreateTexture(nil, "BACKGROUND")
    sbBg:SetAllPoints()
    sbBg:SetTexture(WHITE)
    sbBg:SetVertexColor(0, 0, 0, 0.55)
    bar.bg = sbBg

    local fsz = math.max(9, height - 8)

    -- état / décompte (droite)
    local right = sb:CreateFontString(nil, "OVERLAY")
    right:SetFont(FONT, fsz, "OUTLINE")
    right:SetPoint("RIGHT", -4, 0)
    right:SetJustifyH("RIGHT")
    bar.right = right

    -- coche ✓/✗, juste à gauche du décompte
    local mark = sb:CreateFontString(nil, "OVERLAY")
    mark:SetFont(FONT, fsz, "OUTLINE")
    mark:SetPoint("RIGHT", right, "LEFT", -3, 0)
    bar.mark = mark

    -- icône du sort, juste à gauche de la coche → ordre : icône PUIS coche
    local icon = sb:CreateTexture(nil, "ARTWORK")
    icon:SetSize(height - 2, height - 2)
    icon:SetPoint("RIGHT", mark, "LEFT", -3, 0)
    icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
    bar.icon = icon

    -- nom (gauche), tronqué avant le cluster icône/coche/décompte
    local left = sb:CreateFontString(nil, "OVERLAY")
    left:SetFont(FONT, fsz, "OUTLINE")
    left:SetPoint("LEFT", 5, 0)
    left:SetPoint("RIGHT", icon, "LEFT", -3, 0)
    left:SetJustifyH("LEFT")
    left:SetWordWrap(false)
    bar.left = left

    -- iconTex, col{r,g,b} (barre), leftText, rightText, pct(0..1), rcol{r,g,b}, markText
    function bar:Set(iconTex, col, leftText, rightText, pct, rcol, markText)
        if iconTex then
            self.icon:SetTexture(iconTex); self.icon:Show()
        else
            self.icon:Hide()
        end
        self.mark:SetText(markText or "")
        col = col or { 0.5, 0.5, 0.5 }
        self.sb:SetStatusBarColor(col[1], col[2], col[3], 0.9)
        self.sb:SetValue(pct == nil and 1 or math.max(0, math.min(1, pct)))
        self.left:SetText(leftText or "")
        self.right:SetText(rightText or "")
        if rcol then
            self.right:SetTextColor(rcol[1], rcol[2], rcol[3])
        else
            self.right:SetTextColor(1, 1, 1)
        end
    end

    return bar
end
