-- UI/WarBoard_MapPins.lua
-- Optional objective markers on the zone map (HereBeDragons-Pins, zone-coordinate variant).
--
-- Mirrors WB.MapOverlay (which draws live bounty targets), but for STATIC War Effort
-- objectives: any objective that carries a `marker = { mapID, x, y }` table gets a recurring-
-- quest-style icon on its zone map, with a tooltip and a left-click that opens the War Board.
-- Objectives without a marker are skipped, so this module is purely additive — it never changes
-- how existing objectives, cards, tracking, sync, or records behave.
--
-- The marker lives in its OWN nested table (not flat obj.x / obj.mapID) on purpose: the kill
-- tracker reads obj.mapID for player-kill objectives, so keeping marker data separate guarantees
-- markers can never alter tracking — now or for any future objective type.
--
-- Two deliberate choices:
--   • Separate pin ref ("WarBoardObjectives") from the bounty overlay ("WarBoard") so the two
--     never clear each other's icons.
--   • showFlag is omitted (defaults to 0) => the pin shows ONLY on its exact zone map, never the
--     continent/world — exactly the "relevant zone map only" goal.

local addonName, WB = ...

WB.MapPins = WB.MapPins or {}
local MapPins = WB.MapPins

local HBDPins = LibStub and LibStub("HereBeDragons-Pins-2.0", true)

local PIN_REF = "WarBoardObjectives"

-- Composite recurring-quest icon (all three are atlases): outer circle, the daily-type glyph in
-- the centre, and an outer glow shown only on hover. Sizes are tweakable; the glow is shrunk
-- from its native 64 to a modest halo around the 32 circle.
local ATLAS_CONTAINER = "UI-QuestPoiRecurring-QuestNumber"   -- the circle container (32x32)
local ATLAS_CENTER    = "questlog-questtypeicon-weekly"      -- glyph in the middle (18x18)
local ATLAS_GLOW      = "UI-QuestPoiRecurring-OuterGlow"      -- hover highlight (64 -> shrunk)
local PIN_SIZE, CENTER_SIZE, GLOW_SIZE = 32, 18, 44

-- ── Visibility gate (mirrors the objective-card filter) ────────────────────────────────
-- Show a marker only when the objective has one AND would currently be offered as a card:
-- correct faction, not retired, and (for weekly-flip fronts) its zone is the one up this week.
local function ShouldShow(obj)
    if type(obj) ~= "table" then return false end
    local m = obj.marker
    if not (m and m.mapID and m.x and m.y) then return false end   -- no marker → nothing
    if obj.retired then return false end
    local f = obj.faction and obj.faction:lower()
    local myFaction = (WB.faction or UnitFactionGroup("player") or "Horde"):lower()
    if f and f ~= myFaction then return false end
    if WB.WarEffort and WB.WarEffort.IsFlipZoneActive
        and not WB.WarEffort.IsFlipZoneActive(obj) then
        return false
    end
    return true
end

-- ── Icon pool (composite frame) ────────────────────────────────────────────────────────
local pool = {}
local function GetIcon(idx)
    if pool[idx] then return pool[idx] end
    local icon = CreateFrame("Frame", nil, UIParent)
    icon:SetSize(PIN_SIZE, PIN_SIZE)
    icon:EnableMouse(true)

    -- Outer glow (behind), hover-only.
    local glow = icon:CreateTexture(nil, "BACKGROUND")
    glow:SetSize(GLOW_SIZE, GLOW_SIZE)
    glow:SetPoint("CENTER")
    glow:SetAtlas(ATLAS_GLOW)
    glow:Hide()
    icon.glow = glow

    -- Circle container (fills the frame).
    local container = icon:CreateTexture(nil, "ARTWORK")
    container:SetAllPoints()
    container:SetAtlas(ATLAS_CONTAINER)
    icon.container = container

    -- Daily-type glyph, centred on top.
    local center = icon:CreateTexture(nil, "OVERLAY")
    center:SetSize(CENTER_SIZE, CENTER_SIZE)
    center:SetPoint("CENTER")
    center:SetAtlas(ATLAS_CENTER)
    icon.center = center

    icon:SetScript("OnEnter", function(self)
        self.glow:Show()
        local obj = self.obj
        if not obj then return end
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        local title = (WB.WarEffort and WB.WarEffort.DisplayTitle and WB.WarEffort.DisplayTitle(obj)) or obj.title
        GameTooltip:SetText(title or "Objective", 1, 0.82, 0.2)
        local body = (obj.marker and obj.marker.tooltip) or obj.desc
        if body and body ~= "" then
            -- desc may contain a %s enemy-faction placeholder — fill it like the cards do.
            local enemy = (WB.faction == "Alliance") and "Horde" or "Alliance"
            GameTooltip:AddLine((body:gsub("%%s", enemy)), 0.85, 0.85, 0.85, true)
        end
        if WB.WarEffort and WB.WarEffort.GetProgress and obj.goal then
            local cur = WB.WarEffort.GetProgress(obj.id) or 0
            GameTooltip:AddLine(("Progress: %d / %d"):format(cur, obj.goal), 0.6, 0.8, 1)
        end
        GameTooltip:Show()
    end)
    icon:SetScript("OnLeave", function(self)
        self.glow:Hide()
        GameTooltip:Hide()
    end)
    icon:SetScript("OnMouseUp", function(self, button)
        -- Hook point for later (e.g. "track this objective"); defaults to opening the board.
        if MapPins.OnObjectiveClick then
            MapPins.OnObjectiveClick(self.obj, button)
        elseif button == "LeftButton" and WB.UI and WB.UI.Toggle then
            WB.UI.Toggle()
        end
    end)

    pool[idx] = icon
    return icon
end

-- ── Supply Run event pin (its own horn icon, from WB.SupplyRun.DEF) ─────────────────────
-- A single distinct pin (not the recurring-quest composite) so the delivery event reads as an
-- event, not a daily. Left-click opens the War Board, like the objective pins.
local eventPin
local function GetEventPin()
    if eventPin then return eventPin end
    local icon = CreateFrame("Frame", nil, UIParent)
    icon:SetSize(24, 24)
    icon:EnableMouse(true)
    local tex = icon:CreateTexture(nil, "ARTWORK")
    tex:SetAllPoints()
    icon.tex = tex
    icon:SetScript("OnEnter", function(self)
        local d = self.def
        if not d then return end
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        GameTooltip:SetText(d.title or "Supply Run", 1, 0.82, 0.2)
        if d.desc and d.desc ~= "" then GameTooltip:AddLine(d.desc, 0.85, 0.85, 0.85, true) end
        if WB.SupplyRun and WB.SupplyRun.StepText then
            GameTooltip:AddLine(WB.SupplyRun:StepText(), 0.6, 0.8, 1, true)
        end
        GameTooltip:Show()
    end)
    icon:SetScript("OnLeave", function() GameTooltip:Hide() end)
    icon:SetScript("OnMouseUp", function(_, button)
        if button == "LeftButton" and WB.UI and WB.UI.Toggle then WB.UI.Toggle() end
    end)
    eventPin = icon
    return icon
end

local function PlaceEventPin()
    local d = WB.SupplyRun and WB.SupplyRun.DEF
    local m = d and d.marker
    if not (m and m.mapID and m.x and m.y) then return end
    local myFaction = (WB.faction or UnitFactionGroup("player") or "Horde"):lower()
    if d.faction and d.faction:lower() ~= myFaction then return end
    local pin = GetEventPin()
    pin.def = d
    if d.icon then pcall(pin.tex.SetAtlas, pin.tex, d.icon) end
    HBDPins:AddWorldMapIconMap(PIN_REF, pin, m.mapID, m.x, m.y)   -- showFlag 0 → this zone map only
end

-- ── Refresh (place every eligible objective marker on the current map) ──────────────────
function MapPins:Refresh()
    if not HBDPins then return end
    if not WorldMapFrame or not WorldMapFrame:IsShown() then return end
    HBDPins:RemoveAllWorldMapIcons(PIN_REF)

    local idx = 0
    for _, obj in ipairs((WB.WarEffort and WB.WarEffort.Objectives) or {}) do
        if ShouldShow(obj) then
            idx = idx + 1
            local icon = GetIcon(idx)
            icon.obj = obj
            icon.glow:Hide()   -- reset hover state on (re)placement
            local m = obj.marker
            -- showFlag omitted → 0 → marker appears ONLY on m.mapID's zone map.
            HBDPins:AddWorldMapIconMap(PIN_REF, icon, m.mapID, m.x, m.y)
        end
    end

    PlaceEventPin()   -- the Supply Run delivery event
end

-- Re-render only if the map is open (call when objective state changes live, e.g. weekly flip
-- or a completion while the map is already up). Safe to call anytime.
function MapPins:RequestRefresh()
    if WorldMapFrame and WorldMapFrame:IsShown() then self:Refresh() end
end

-- ── "X marks the spot" markers (standalone, keyed) ──────────────────────────────────────
-- Each marker lives under its OWN pin ref, so the objective-pin refresh never clears it and two
-- markers (e.g. a race finish + a supply turn-in) never clear each other. HBD keeps each on its
-- zone map until removed, so no per-refresh re-add is needed.
local xMarkers = {}   -- key -> { ref = "...", pin = frame }
local function GetXMarker(key)
    local m = xMarkers[key]
    if m then return m end
    local icon = CreateFrame("Frame", nil, UIParent)
    icon:SetSize(20, 20)
    local tex = icon:CreateTexture(nil, "OVERLAY")
    tex:SetAllPoints()
    if not (pcall(tex.SetAtlas, tex, "XMarksTheSpot") and tex:GetAtlas()) then
        tex:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")   -- fallback if the atlas is missing
    end
    icon.tex = tex
    m = { ref = "WarBoardXMark:" .. tostring(key), pin = icon }
    xMarkers[key] = m
    return m
end

function MapPins:ShowXMark(key, mapID, x, y)
    if not (HBDPins and key and mapID and x and y) then return end
    local m = GetXMarker(key)
    HBDPins:RemoveAllWorldMapIcons(m.ref)                     -- drop any prior placement first
    HBDPins:AddWorldMapIconMap(m.ref, m.pin, mapID, x, y)     -- showFlag 0 → this zone map only
end

function MapPins:HideXMark(key)
    if not (HBDPins and key) then return end
    local m = xMarkers[key]
    if m then HBDPins:RemoveAllWorldMapIcons(m.ref) end
end

-- Race finish-line marker (thin wrappers over the keyed markers, so callers stay unchanged).
function MapPins:ShowRaceFinish(mapID, x, y) self:ShowXMark("race", mapID, x, y) end
function MapPins:HideRaceFinish()            self:HideXMark("race") end

-- ── Hooks (mirror WB.MapOverlay) ───────────────────────────────────────────────────────
if WorldMapFrame then
    WorldMapFrame:HookScript("OnShow", function() MapPins:Refresh() end)
    if WorldMapFrame.RegisterCallback then
        WorldMapFrame:RegisterCallback("MapChanged", function() MapPins:Refresh() end, MapPins)
    end
end
