local rndx = include("lib/rndx.lua")
local logo = Material("nocode/announcement/logo.png")

-- client/ charge avant shared/, donc je m'assure que la table est là
BladW = BladW or {}
BladW.Announcement = BladW.Announcement or {}

local COL_ACCENT = Color(76, 119, 200)
local COL_WHITE  = Color(255, 255, 255)
local COL_BG     = Color(0, 0, 0, 200)
local COL_TRACK  = Color(54, 54, 54, 200)

-- couleur des contours extérieurs selon la config ("blue" ou "white")
local function OuterCol()
    return (BladW.Announcement.Conf or {}).announcement_theme == "white" and COL_WHITE or COL_ACCENT
end

-- fonts responsives (refaites quand la résolution change)
local function AnnScale()
    return math.min(ScrW() / 1920, ScrH() / 1080)
end

-- durées + easing de l'anim d'apparition/disparition
local INTRO_TIME = 0.45
local OUTRO_TIME = 0.35
local function EaseOutCubic(t) return 1 - (1 - t) ^ 3 end

local function BuildFonts()
    local s = AnnScale()
    surface.CreateFont("bladw_ann_title", {
        font = "Poppins-SemiBold", size = math.max(1, math.Round(25 * s)), weight = 600, antialias = true,
    })
    surface.CreateFont("bladw_ann_text", {
        font = "Poppins-SemiBold", size = math.max(1, math.Round(20 * s)), weight = 800, antialias = true,
    })
end
BuildFonts()
hook.Add("OnScreenSizeChanged", "BladeWin:AnnouncementFonts", BuildFonts)

-- coupe le texte en lignes qui rentrent dans maxW (px) pour la police donnée
local function WrapText(text, font, maxW)
    surface.SetFont(font)
    local lines, line = {}, ""

    for _, word in ipairs(string.Explode(" ", text or "")) do
        local test = (line == "") and word or (line .. " " .. word)
        if surface.GetTextSize(test) > maxW and line ~= "" then
            lines[#lines + 1] = line
            line = word
        else
            line = test
        end
    end
    if line ~= "" then lines[#lines + 1] = line end

    return lines
end

-- le contour du cadre sert de jauge : reste en colRemain, ce qui est passé en colDone (1 = plein, 0 = fini)
local function DrawBorderTimer(x, y, w, h, r, remaining, colRemain, colDone, thickness)
    thickness = math.max(1, thickness or 3)

    local pts = {}
    local function line(x1, y1, x2, y2)
        local steps = math.max(1, math.floor(math.Distance(x1, y1, x2, y2) / 4))
        for i = 0, steps do
            pts[#pts + 1] = { Lerp(i / steps, x1, x2), Lerp(i / steps, y1, y2) }
        end
    end
    local function arc(cx, cy, a0, a1)
        for i = 0, 6 do
            local a = math.rad(Lerp(i / 6, a0, a1))
            pts[#pts + 1] = { cx + math.cos(a) * r, cy + math.sin(a) * r }
        end
    end

    -- sens horaire à partir du bord haut
    line(x + r,     y,         x + w - r, y)
    arc (x + w - r, y + r,     270, 360)
    line(x + w,     y + r,     x + w,     y + h - r)
    arc (x + w - r, y + h - r,   0,  90)
    line(x + w - r, y + h,     x + r,     y + h)
    arc (x + r,     y + h - r,  90, 180)
    line(x,         y + h - r, x,         y + r)
    arc (x + r,     y + r,     180, 270)

    local total = 0
    for i = 1, #pts - 1 do
        total = total + math.Distance(pts[i][1], pts[i][2], pts[i + 1][1], pts[i + 1][2])
    end
    if total == 0 then return end

    local consumedLen = (1 - remaining) * total
    draw.NoTexture()

    local acc = 0
    for i = 1, #pts - 1 do
        local x1, y1 = pts[i][1],     pts[i][2]
        local x2, y2 = pts[i + 1][1], pts[i + 1][2]
        local segLen = math.Distance(x1, y1, x2, y2)

        surface.SetDrawColor(acc < consumedLen and colDone or colRemain)
        surface.DrawTexturedRectRotated((x1 + x2) * 0.5, (y1 + y2) * 0.5,
            segLen + thickness, thickness, -math.deg(math.atan2(y2 - y1, x2 - x1)))

        acc = acc + segLen
    end
end

-- état de l'annonce en cours (survit au reload)
BladW.Announcement.Active    = BladW.Announcement.Active    or false
BladW.Announcement.Title     = BladW.Announcement.Title     or "Annonce"
BladW.Announcement.Text      = BladW.Announcement.Text      or ""
BladW.Announcement.Style     = BladW.Announcement.Style     or "loader"
BladW.Announcement.Duration  = BladW.Announcement.Duration  or 10
BladW.Announcement.StartTime = BladW.Announcement.StartTime or 0

-- lance une annonce. style/duration vides = valeurs de la config
function BladW.Announcement.Show(title, text, style, duration)
    local Conf = BladW.Announcement.Conf or {}

    BladW.Announcement.Title     = (title and title ~= "") and title or "Annonce"
    BladW.Announcement.Text      = text or ""
    BladW.Announcement.Style     = (style and style ~= "") and style or (Conf.announcement_style or "loader")
    BladW.Announcement.Duration  = (duration and duration > 0) and duration or (Conf.announcement_duration or 10)
    BladW.Announcement.StartTime = CurTime()
    BladW.Announcement.Active    = true

    if Conf.announcement_sound and Conf.announcement_sound ~= "" then
        surface.PlaySound(Conf.announcement_sound)
    end
end

-- le serveur m'envoie une annonce à afficher
net.Receive("bladWShowAnnouncement", function()
    local title    = net.ReadString()
    local text     = net.ReadString()
    local style    = net.ReadString()
    local duration = net.ReadUInt(16)
    BladW.Announcement.Show(title, text, style, duration)
end)

-- affichage (responsive + s'adapte au message)
hook.Add("HUDPaint", "BladeWin:Announcement", function()
    if not BladW.Announcement.Active then return end

    local ply = LocalPlayer()
    if not IsValid(ply) then return end

    local duration = BladW.Announcement.Duration or 10
    local elapsed  = CurTime() - BladW.Announcement.StartTime

    -- temps écoulé -> on cache
    if elapsed >= duration then
        BladW.Announcement.Active = false
        return
    end

    local style     = BladW.Announcement.Style or "loader"
    local title     = BladW.Announcement.Title
    local text      = BladW.Announcement.Text
    local remaining = math.Clamp(1 - (elapsed / duration), 0, 1)

    -- échelle responsive
    local s     = AnnScale()
    local padX  = 26 * s
    local padY  = 18 * s
    local gap   = 8  * s
    local logoS = 62 * s
    local barW  = 200 * s
    local barH  = 18  * s
    local radius = 14 * s

    -- wrap du message (largeur interne responsive, la vraie limite c'est le nb de caractères)
    local wrapW = ScrW() * 0.8
    local lines = WrapText(text, "bladw_ann_text", wrapW)

    surface.SetFont("bladw_ann_title")
    local titleW, titleH = surface.GetTextSize(title)

    surface.SetFont("bladw_ann_text")
    local _, lineH = surface.GetTextSize("Ay")
    local msgW = 0
    for _, l in ipairs(lines) do
        msgW = math.max(msgW, (surface.GetTextSize(l)))
    end

    -- taille de la box selon le contenu
    local contentW = math.max(titleW, msgW, style == "loader" and barW or 0)
    local boxW     = math.Clamp(contentW + padX * 2, 260 * s, ScrW() * 0.92)
    local msgH     = #lines * lineH
    local boxH     = padY + logoS + gap + titleH + gap + msgH
                     + (style == "loader" and (gap + barH) or 0) + padY

    -- anim : fondu + glisse du haut (et l'inverse quand ça part)
    local appear    = math.Clamp(elapsed / INTRO_TIME, 0, 1)
    local disappear = math.Clamp((duration - elapsed) / OUTRO_TIME, 0, 1)
    local anim      = math.min(EaseOutCubic(appear), EaseOutCubic(disappear)) -- 0 -> 1 -> 0
    local slide     = (1 - anim) * 26 * s

    local boxX = (ScrW() - boxW) * 0.5
    local boxY = ScrH() * 0.035 - slide
    local cx   = boxX + boxW * 0.5

    surface.SetAlphaMultiplier(anim)

    -- fond
    rndx.Draw(radius, boxX, boxY, boxW, boxH, nil, rndx.SHAPE_FIGMA + rndx.BLUR)
    rndx.Draw(radius, boxX, boxY, boxW, boxH, COL_BG, rndx.SHAPE_FIGMA)

    -- le loader garde un contour fixe (couleur selon le thème config ; le simple a sa jauge sur le bord)
    if style == "loader" then
        rndx.DrawOutlined(radius, boxX, boxY, boxW, boxH, OuterCol(), 3 * s, rndx.SHAPE_FIGMA)
    end

    -- contenu empilé et centré
    local y = boxY + padY
    rndx.DrawMaterial(0, cx - logoS * 0.5, y, logoS, logoS, COL_WHITE, logo)
    y = y + logoS + gap

    draw.SimpleText(title, "bladw_ann_title", cx, y, COL_WHITE, TEXT_ALIGN_CENTER, TEXT_ALIGN_TOP)
    y = y + titleH + gap

    for _, l in ipairs(lines) do
        draw.SimpleText(l, "bladw_ann_text", cx, y, COL_WHITE, TEXT_ALIGN_CENTER, TEXT_ALIGN_TOP)
        y = y + lineH
    end

    -- jauge de temps
    if style == "loader" then
        y = y + gap
        local bx = cx - barW * 0.5
        rndx.Draw(barH * 0.5, bx, y, barW, barH, COL_TRACK, rndx.SHAPE_FIGMA)
        rndx.Draw(barH * 0.5, bx, y, barW * remaining, barH, COL_ACCENT, rndx.SHAPE_FIGMA)
    else
        -- le bord bleu qui se vide en blanc
        DrawBorderTimer(boxX, boxY, boxW, boxH, radius, remaining, COL_ACCENT, COL_WHITE, 3 * s)
    end

    surface.SetAlphaMultiplier(1)
end)
