-- WarBoard_ObjectivePopup.lua
-- Center-screen "War Objective" banner shown when the player enters an objective's radius.
-- Ported from the SemiDevTools prototype (SDT.Popup) — a plain frame + faction title art +
-- notification sound + fade in/out. Taint-free, no secure frames, no combat concerns.
--
-- WB.Popup:Show(objectiveName, label, key, cooldown)
--   objectiveName → big top line (e.g. "Battle for the Scar")
--   label         → small bottom line (defaults to "War Objective")
--   key           → objective id; scopes the sound cooldown per objective (optional)
--   cooldown      → seconds between sounds for this objective; match its TalkingHead cooldown (optional)

local addonName, WB = ...
WB.Popup = WB.Popup or {}
local P = WB.Popup

-- Faction title-background atlases (Interface/Scenarios/ScenarioHordeAlliance/...).
local FACTION_BG = {
    Horde    = "HordeScenario-TitleBG",
    Alliance = "AllianceScenario-TitleBG",
}

local BANNER_W, BANNER_H = 600, 144   -- sized so the text sits inside the solid middle of the art
local SHOW_TIME = 4.0     -- seconds fully visible before fading out
local FADE_IN   = 0.35
local FADE_OUT  = 1.0

-- The banner VISUAL shows every time the player enters an area, but its notification SOUND is
-- rate-limited PER OBJECTIVE so it doesn't get annoying on repeated entries. Each objective's
-- sound cooldown MATCHES that objective's proximity cooldown (the same gate as its TalkingHead
-- line), so the banner sound and the leader's voice line come back at the same time. While an
-- objective's sound is on cooldown the banner also dismisses a bit quicker (shorter hold + faster
-- fade), since there is no audio cue to linger for. The quicker dismiss is likewise per objective.
local SOUND_CD        = 120    -- fallback cooldown when a caller doesn't pass one (e.g. the test command)
local SHOW_TIME_QUICK = 2.0    -- hold while the sound is on cooldown
local FADE_OUT_QUICK  = 0.6    -- fade-out while the sound is on cooldown
local lastSoundByObj  = {}     -- objective id -> GetTime() of that objective's last sound

-- ── Zone-text suppression while the banner is up ──────────────────────────────
-- A zone / subzone change renders its big yellow name at the top of the screen, right where
-- our banner sits — looks cluttered. While the banner shows we suppress it (the same thing
-- Blizzard's own banners do via ZoneText_Clear): clear whatever's up now, and hide the
-- zone-text frames if they try to reappear (e.g. crossing into another subzone). When the
-- banner fades out, suppression is lifted and zone text behaves normally again.
local suppressing = false
local zoneHooked  = false

local function EnsureZoneHooks()
    if zoneHooked then return end
    zoneHooked = true
    local function suppress(self) if suppressing then self:Hide() end end
    if ZoneTextFrame    then ZoneTextFrame:HookScript("OnShow", suppress)    end
    if SubZoneTextFrame then SubZoneTextFrame:HookScript("OnShow", suppress) end
end

local function SetZoneSuppressed(on)
    suppressing = on
    if on then
        EnsureZoneHooks()
        if ZoneText_Clear then pcall(ZoneText_Clear) end   -- clear anything currently displayed
        if ZoneTextFrame    then ZoneTextFrame:Hide()    end
        if SubZoneTextFrame then SubZoneTextFrame:Hide() end
    end
end

local function BuildFrame()
    if P.frame then return P.frame end

    local f = CreateFrame("Frame", "WarBoardObjectivePopup", UIParent)
    f:SetSize(BANNER_W, BANNER_H)
    f:SetPoint("TOP", UIParent, "TOP", 0, -160)   -- center-top, like Blizzard's banners
    f:SetFrameStrata("HIGH")
    f:SetToplevel(true)
    f:Hide()

    -- Faction title background.
    local bg = f:CreateTexture(nil, "BACKGROUND")
    bg:SetAllPoints(f)
    f.bg = bg

    -- Objective name on TOP, larger.
    local name = f:CreateFontString(nil, "OVERLAY", "GameFontNormalHuge")
    name:SetPoint("CENTER", f, "CENTER", 0, 11)
    f.nameText = name

    -- "War Objective" category label on BOTTOM, smaller.
    local label = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    label:SetPoint("TOP", name, "BOTTOM", 0, -3)
    f.labelText = label

    P.frame = f
    return f
end

function P:Show(objectiveName, label, key, cooldown)
    local f = BuildFrame()

    local faction = (WB.faction) or UnitFactionGroup("player") or "Alliance"
    f.bg:SetAtlas(FACTION_BG[faction] or FACTION_BG.Alliance, false)  -- keep our banner size
    f.nameText:SetText(objectiveName or "Objective")
    f.labelText:SetText(label or "War Objective")

    -- Notification cue (same one Blizzard uses entering a world-quest area), rate-limited PER
    -- OBJECTIVE on that objective's own cooldown (so it returns in step with its TalkingHead line).
    -- While this objective's sound is on cooldown the banner dismisses a bit quicker.
    local now    = GetTime()
    local bucket = key or "__default"
    local cd     = cooldown or SOUND_CD
    local showTime, fadeOut = SHOW_TIME, FADE_OUT
    if (now - (lastSoundByObj[bucket] or -math.huge)) >= cd then
        local snd = SOUNDKIT and (SOUNDKIT.UI_WORLDQUEST_START or SOUNDKIT.UI_SCENARIO_STAGE_END)
        if snd then PlaySound(snd, "Master") end
        lastSoundByObj[bucket] = now
    else
        showTime, fadeOut = SHOW_TIME_QUICK, FADE_OUT_QUICK
    end

    -- Restart a clean fade cycle: cancel anything in flight, then fade in → hold → fade out.
    if f._holdTimer then f._holdTimer:Cancel(); f._holdTimer = nil end
    if f._hideTimer then f._hideTimer:Cancel(); f._hideTimer = nil end
    if UIFrameFadeRemoveFrame then UIFrameFadeRemoveFrame(f) end

    SetZoneSuppressed(true)   -- hide zone/subzone text for as long as the banner is up
    f:Show()
    f:SetAlpha(0)
    UIFrameFadeIn(f, FADE_IN, 0, 1)
    f._holdTimer = C_Timer.NewTimer(FADE_IN + showTime, function()
        UIFrameFadeOut(f, fadeOut, f:GetAlpha() or 1, 0)
        f._hideTimer = C_Timer.NewTimer(fadeOut, function()
            f:Hide()
            SetZoneSuppressed(false)
        end)
    end)
end

function P:Hide()
    local f = self.frame
    if not f then return end
    if f._holdTimer then f._holdTimer:Cancel(); f._holdTimer = nil end
    if f._hideTimer then f._hideTimer:Cancel(); f._hideTimer = nil end
    if UIFrameFadeRemoveFrame then UIFrameFadeRemoveFrame(f) end
    f:Hide()
    SetZoneSuppressed(false)
end
