-- ============================================================══
-- MODUL: MapMarkers - Todesmarker auf der Weltkarte
-- Classic Era kompatibel
-- ============================================================══

local addonName, addon = ...
local GDL = _G["GuildDeathLog"]
local MapMarkers = {}

local MARKER_DURATION = 30 * 24 * 60 * 60 -- 30 Tage
local mapPins = {}
local pinPool = {}

function MapMarkers:Initialize()
    -- 30-Tage-Bereinigung beim Start
    self:PruneOldDeaths()

    -- WORLD_MAP_UPDATE was removed in the Modern Client Backport (Classic 1.14+).
    -- pcall so it silently no-ops on current clients; the ScrollContainer.SetMapID
    -- hook below handles map-change detection on Classic 1.14+ / 1.15.x.
    local eventFrame = CreateFrame("Frame")
    local wmEventOk = pcall(function() eventFrame:RegisterEvent("WORLD_MAP_UPDATE") end)
    if wmEventOk then
        eventFrame:SetScript("OnEvent", function()
            if WorldMapFrame and WorldMapFrame:IsShown() then
                C_Timer.After(0.2, function() self:UpdateMarkers() end)
            end
        end)
    end

    -- Hook WorldMapFrame: Todes-Marker beim Öffnen
    if WorldMapFrame then
        WorldMapFrame:HookScript("OnShow", function()
            -- 0.3s warten bis Canvas gerendert ist
            C_Timer.After(0.3, function() self:UpdateMarkers() end)
        end)
        -- ScrollContainer.SetMapID Hook (neuere Classic / Retail)
        if WorldMapFrame.ScrollContainer and WorldMapFrame.ScrollContainer.SetMapID then
            hooksecurefunc(WorldMapFrame.ScrollContainer, "SetMapID", function()
                C_Timer.After(0.2, function() self:UpdateMarkers() end)
            end)
        end
    end

    -- Eigenen Tod für Koordinaten tracken
    self:SetupSelfDeathTracking()
    -- Minimap Skulls (Todes-Orte)
    self:InitializeMinimapSkulls()
    -- Live-Position Tracking für Gildenmitglieder starten
    C_Timer.After(3, function() self:StartLiveBroadcast() end)
end

function MapMarkers:SetupSelfDeathTracking()
    local deathFrame = CreateFrame("Frame")
    deathFrame:RegisterEvent("PLAYER_DEAD")
    deathFrame:SetScript("OnEvent", function(_, event)
        if event == "PLAYER_DEAD" then
            self:CaptureOwnDeathLocation()
        end
    end)
end

function MapMarkers:CaptureOwnDeathLocation()
    -- Koordinaten des eigenen Todes erfassen
    local mapId = C_Map.GetBestMapForUnit("player")
    if not mapId then return end
    
    local position = C_Map.GetPlayerMapPosition(mapId, "player")
    if not position then return end
    
    local posX, posY = position:GetXY()
    
    -- Speichere die letzte bekannte Position für den Sync
    GDL.lastDeathLocation = {
        mapId = mapId,
        posX = posX,
        posY = posY,
        timestamp = time()
    }
    
    if GuildDeathLogDB.settings.debugPrint then
        GDL:Print(string.format("Todesort erfasst: Map %d @ %.2f, %.2f", mapId, posX * 100, posY * 100))
    end
end

function MapMarkers:GetLastDeathLocation()
    return GDL.lastDeathLocation
end

function MapMarkers:OnEvent(event)
    if event == "PLAYER_READY" then
        C_Timer.After(3, function() self:UpdateMarkers() end)
    end
end

function MapMarkers:GetCanvas()
    -- 1) Modernes Classic / Retail: ScrollContainer.Child
    if WorldMapFrame and WorldMapFrame.ScrollContainer then
        local child = WorldMapFrame.ScrollContainer.Child
        if child and child:GetWidth() > 0 then return child end
    end
    -- 2) GetCanvas()-API (manche Classic-Versionen)
    if WorldMapFrame and WorldMapFrame.GetCanvas then
        local c = WorldMapFrame:GetCanvas()
        if c and c:GetWidth() > 0 then return c end
    end
    -- 3) Vanilla Classic Fallback: WorldMapDetailFrame
    if _G["WorldMapDetailFrame"] then
        return _G["WorldMapDetailFrame"]
    end
    return nil
end

function MapMarkers:GetCurrentMapID()
    if WorldMapFrame and WorldMapFrame.GetMapID then
        local ok, id = pcall(function() return WorldMapFrame:GetMapID() end)
        if ok and id then return id end
    end
    if WorldMapFrame and WorldMapFrame.mapID then
        return WorldMapFrame.mapID
    end
    return nil
end

function MapMarkers:UpdateMarkers()
    -- Alle Pins verstecken
    for _, pin in pairs(mapPins) do pin:Hide() end

    if not GuildDeathLogDB.settings.mapMarkers then return end
    if not WorldMapFrame or not WorldMapFrame:IsShown() then return end

    local guildData = GDL:GetGuildData()
    if not guildData then return end

    local currentMapId = self:GetCurrentMapID()
    if not currentMapId then return end

    local canvas = self:GetCanvas()
    if not canvas then return end

    local canvasWidth  = canvas:GetWidth()
    local canvasHeight = canvas:GetHeight()
    -- Canvas noch nicht bereit → nochmal versuchen
    if canvasWidth == 0 or canvasHeight == 0 then
        C_Timer.After(0.5, function() self:UpdateMarkers() end)
        return
    end

    local now = time()
    local Sync = GDL:GetModule("Sync")
    local pinCount = 0

    for _, death in ipairs(guildData.deaths or {}) do
        if death.mapId and death.mapId == currentMapId
           and death.posX and death.posX > 0 and death.posX < 1
           and death.posY and death.posY > 0 and death.posY < 1
           and (now - (death.timestamp or 0)) < MARKER_DURATION then

            local key = (Sync and Sync:GetDeathKey(death.name, death.timestamp))
                        or (death.name .. (death.timestamp or 0))
            local pin = mapPins[key]
            if not pin then
                pin = self:GetOrCreatePin(canvas, death)
                mapPins[key] = pin
            end

            pin.deathData = death
            pin:SetParent(canvas)
            pin:ClearAllPoints()
            pin:SetPoint("CENTER", canvas, "TOPLEFT",
                         death.posX * canvasWidth, -death.posY * canvasHeight)

            -- Grabstein bleibt natursteinfarben (neutral); die Klassenfarbe
            -- liegt als dezenter Aura-Schein dahinter.
            local col = GDL:GetModule("UI"):GetClassColor(death.classId)
            if pin.icon then pin.icon:SetVertexColor(1, 1, 1, 1) end
            if pin.glow and col then pin.glow:SetVertexColor(col[1], col[2], col[3], 1) end
            pin:Show()
            pinCount = pinCount + 1
        end
    end

    if GuildDeathLogDB.settings.debugPrint and pinCount > 0 then
        GDL:Print(string.format("Map %d: %d Todesmarker", currentMapId, pinCount))
    end
end

function MapMarkers:GetOrCreatePin(parent, death)
    -- Aus Pool holen oder neu erstellen
    local pin = table.remove(pinPool)
    if not pin then
        pin = self:CreatePin(parent)
    end
    return pin
end

function MapMarkers:CreatePin(parent)
    -- WICHTIG: Button statt Frame für OnClick Support!
    local pin = CreateFrame("Button", nil, parent)
    pin:SetSize(24, 28)  -- an Grabstein angepasst
    pin:SetFrameStrata("HIGH")
    pin:SetFrameLevel(100)
    
    -- Sanfter Aura-Glow hinter dem Grabstein (wird in UpdateMarkers
    -- klassengefaerbt -> jeder Grabstein hat einen dezenten Klassen-Schein)
    local glow = pin:CreateTexture(nil, "BACKGROUND")
    glow:SetTexture("Interface\\Cooldown\\star4")
    glow:SetBlendMode("ADD")
    glow:SetSize(26, 26)
    glow:SetPoint("CENTER", 0, 2)
    glow:SetAlpha(0.30)
    glow:SetVertexColor(0.7, 0.1, 0.1, 1)
    pin.glow = glow

    -- Grabstein-Icon (Basis sitzt etwa auf der Todeskoordinate)
    local icon = pin:CreateTexture(nil, "ARTWORK")
    icon:SetSize(22, 22)
    icon:SetPoint("CENTER", 0, 6)
    icon:SetTexture("Interface\\AddOns\\GuildDeathLog\\Textures\\GDL_Tombstone.png")
    pin.icon = icon
    
    -- Interaktiv
    pin:EnableMouse(true)
    pin:SetScript("OnEnter", function(self)
        local d = self.deathData
        if not d then return end
        
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        
        -- Name mit Klassenfarbe
        local col = GDL:GetModule("UI"):GetClassColor(d.classId)
        if col then
            GameTooltip:AddLine(d.name, col[1], col[2], col[3])
        else
            GameTooltip:AddLine(d.name, 1, 0.84, 0)
        end
        
        -- Details
        GameTooltip:AddLine(GDL:L("LEVEL") .. " " .. (d.level or "?") .. " " .. GDL:GetClassName(d.classId), 0.8, 0.8, 0.8)
        
        if d.zone and d.zone ~= "" then
            GameTooltip:AddLine(d.zone, 0.6, 0.6, 0.6)
        end
        
        if d.timestamp and d.timestamp > 0 then
            local timeAgo = time() - d.timestamp
            local timeStr
            if timeAgo < 3600 then
                timeStr = string.format("vor %d Minuten", math.floor(timeAgo / 60))
            elseif timeAgo < 86400 then
                timeStr = string.format("vor %d Stunden", math.floor(timeAgo / 3600))
            else
                timeStr = date("%d.%m.%Y %H:%M", d.timestamp)
            end
            GameTooltip:AddLine("+ " .. timeStr, 0.5, 0.5, 0.5)
        end
        
        -- Koordinaten
        if d.posX and d.posY then
            GameTooltip:AddLine(string.format(GDL:T("Koordinaten: %.1f, %.1f"), d.posX * 100, d.posY * 100), 0.4, 0.4, 0.4)
        end

        GameTooltip:AddLine(" ")
        GameTooltip:AddLine("|cff888888" .. GDL:T("Klick für Details") .. "|r", 0.5, 0.5, 0.5)
        
        GameTooltip:Show()
        
        -- Glow Animation
        if self.glow then
            self.glow:SetAlpha(0.8)
        end
    end)
    
    pin:SetScript("OnLeave", function(self)
        GameTooltip:Hide()
        if self.glow then
            self.glow:SetAlpha(0.5)
        end
    end)
    
    -- Button muss für Klicks registriert werden
    pin:RegisterForClicks("LeftButtonUp", "RightButtonUp")
    pin:SetScript("OnClick", function(self, button)
        local d = self.deathData
        if d and button == "LeftButton" then
            local UI = GDL:GetModule("UI")
            if UI then UI:ShowOverlay(d, d.syncedFrom ~= nil) end
        end
    end)
    
    return pin
end

function MapMarkers:RecyclePin(pin)
    pin:Hide()
    pin:ClearAllPoints()
    table.insert(pinPool, pin)
end

-- Anzahl der Marker mit Koordinaten
function MapMarkers:GetMarkerCount()
    local guildData = GDL:GetGuildData()
    if not guildData then return 0, 0 end
    
    local total = 0
    local withCoords = 0
    
    for _, death in ipairs(guildData.deaths or {}) do
        total = total + 1
        if death.posX and death.posX > 0 and death.posY and death.posY > 0 then
            withCoords = withCoords + 1
        end
    end
    
    return total, withCoords
end

-- Debug-Info über Koordinaten
function MapMarkers:PrintCoordDebug()
    local guildData = GDL:GetGuildData()
    if not guildData then 
        GDL:Print("|cffFF6666Keine Gildendaten gefunden!|r")
        return 
    end
    
    local total, withCoords = self:GetMarkerCount()
    
    GDL:Print("=== Karten-Marker Debug ===")
    GDL:Print(string.format("Tode gesamt: %d", total))
    GDL:Print(string.format("Mit Koordinaten: |cff00FF00%d|r (%.0f%%)", withCoords, total > 0 and (withCoords/total*100) or 0))
    GDL:Print(string.format("Ohne Koordinaten: |cffFF6666%d|r", total - withCoords))
    GDL:Print(" ")
    
    if total - withCoords > 0 then
        GDL:Print("|cffFFFF00Hinweis:|r Koordinaten werden nur erfasst wenn:")
        GDL:Print("- Der sterbende Spieler dieses Addon hat")
        GDL:Print("- ODER das Deathlog-Addon Koordinaten liefert")
        GDL:Print(" ")
    end
    
    -- Zeige die letzten 5 Tode mit/ohne Koordinaten
    local count = 0
    for i = #(guildData.deaths or {}), 1, -1 do
        if count >= 5 then break end
        local d = guildData.deaths[i]
        local hasCoords = d.posX and d.posX > 0 and d.posY and d.posY > 0
        local coordStr = hasCoords 
            and string.format("|cff00FF00%.1f, %.1f|r", (d.posX or 0)*100, (d.posY or 0)*100)
            or "|cffFF6666keine|r"
        GDL:Print(string.format("  %s - Coords: %s", d.name or "?", coordStr))
        count = count + 1
    end
end

-- ============================================================
-- LIVE POSITION TRACKING (Gildenmitglieder auf der Weltkarte)
-- Classic Era 1.15.8 + TBC 2.5.5 kompatibel
-- ============================================================

local GDL_POS_PREFIX     = "GDLPos"
local POS_BROADCAST_SEC  = 8
local POS_STALE_SEC      = 30
local livePositions      = {}
local liveMarkerFrames   = {}
local liveMiniDots       = {}

-- Einziger geteilter Puls-Ticker für ALLE Live-Marker (kein Leak)
local livePulseTicker = nil
local function EnsureLivePulseTicker()
    if livePulseTicker then return end
    livePulseTicker = C_Timer.NewTicker(2.0, function()
        for _, frame in pairs(liveMarkerFrames) do
            if frame:IsShown() and frame.glow then
                frame.glow:SetAlpha(0.72)
                C_Timer.After(0.45, function()
                    if frame.glow then frame.glow:SetAlpha(0.35) end
                end)
            end
        end
    end)
end

function MapMarkers:StartLiveBroadcast()
    -- Prefix registrieren
    C_ChatInfo.RegisterAddonMessagePrefix(GDL_POS_PREFIX)

    -- Eingehende Positionsnachrichten empfangen
    local posFrame = CreateFrame("Frame")
    posFrame:RegisterEvent("CHAT_MSG_ADDON")
    posFrame:SetScript("OnEvent", function(_, _, prefix, message, channel, sender)
        if prefix == GDL_POS_PREFIX and (channel == "GUILD" or channel == "PARTY") then
            self:HandlePositionMessage(message, sender)
        end
    end)

    -- Eigene Position zyklisch senden
    C_Timer.NewTicker(POS_BROADCAST_SEC, function()
        if IsInGuild() then self:BroadcastOwnPosition() end
    end)

    -- Sofort einmal senden (nach kurzer Verzögerung für Init)
    C_Timer.After(6, function()
        if IsInGuild() then self:BroadcastOwnPosition() end
    end)

    -- WorldMap: Live-Marker bei Kartenwechsel aktualisieren
    if WorldMapFrame then
        WorldMapFrame:HookScript("OnShow", function()
            C_Timer.After(0.15, function() self:UpdateLiveMarkers() end)
        end)
    end
end

function MapMarkers:BroadcastOwnPosition()
    local mapId = C_Map.GetBestMapForUnit("player")
    if not mapId then return end

    local pos = C_Map.GetPlayerMapPosition(mapId, "player")
    if not pos then return end

    local posX, posY = pos:GetXY()
    if posX == 0 and posY == 0 then return end

    local _, classToken = UnitClass("player")
    local CLASS_ID_MAP = {WARRIOR=1,PALADIN=2,HUNTER=3,ROGUE=4,PRIEST=5,DEATHKNIGHT=6,SHAMAN=7,MAGE=8,WARLOCK=9,DRUID=11}
    local classId = CLASS_ID_MAP[classToken] or 0

    -- Format: POS|mapId|posX|posY|classId
    local msg = string.format("POS|%d|%.4f|%.4f|%d", mapId, posX, posY, classId)
    C_ChatInfo.SendAddonMessage(GDL_POS_PREFIX, msg, "GUILD")
end

function MapMarkers:HandlePositionMessage(message, sender)
    local parts = {strsplit("|", message)}
    if parts[1] ~= "POS" or #parts < 5 then return end

    local mapId  = tonumber(parts[2])
    local posX   = tonumber(parts[3])
    local posY   = tonumber(parts[4])
    local classId = tonumber(parts[5])
    if not mapId or not posX or not posY then return end

    local name = strsplit("-", sender)
    livePositions[name] = {
        mapId     = mapId,
        posX      = posX,
        posY      = posY,
        classId   = classId,
        timestamp = time(),
    }

    -- Live aktualisieren wenn Map gerade gezeigt wird
    if WorldMapFrame and WorldMapFrame:IsShown() then
        C_Timer.After(0, function() self:UpdateLiveMarkers() end)
    end
end

function MapMarkers:UpdateLiveMarkers()
    for _, f in pairs(liveMarkerFrames) do f:Hide() end

    if not WorldMapFrame or not WorldMapFrame:IsShown() then return end

    local currentMapId = self:GetCurrentMapID()
    if not currentMapId then return end

    local canvas = self:GetCanvas()
    if not canvas then return end

    local canvasW = canvas:GetWidth()
    local canvasH = canvas:GetHeight()
    if canvasW == 0 or canvasH == 0 then return end

    local myName = UnitName("player")
    local now = time()

    for name, data in pairs(livePositions) do
        -- Nur diese Karte, nicht sich selbst, nicht veraltet
        if name ~= myName
           and data.mapId == currentMapId
           and (now - data.timestamp) < POS_STALE_SEC then

            local frame = self:GetOrCreateLiveMarker(name, data)
            frame:SetParent(canvas)
            frame:ClearAllPoints()
            local x =  data.posX * canvasW
            local y = -data.posY * canvasH
            frame:SetPoint("CENTER", canvas, "TOPLEFT", x, y)
            frame:Show()
        end
    end
end

function MapMarkers:GetOrCreateLiveMarker(name, data)
    if not liveMarkerFrames[name] then
        liveMarkerFrames[name] = self:CreateLiveMarkerFrame(name, data)
    end
    -- Klassenfarbe aktualisieren (könnte sich geändert haben)
    local frame = liveMarkerFrames[name]
    local UI    = GDL:GetModule("UI")
    local col   = UI and UI:GetClassColor(data.classId) or {0.8, 0.8, 0.8}

    if frame.dot  then frame.dot:SetVertexColor(col[1], col[2], col[3], 1.0) end
    if frame.ring then frame.ring:SetVertexColor(col[1]*0.7, col[2]*0.7, col[3]*0.7, 0.55) end
    if frame.glow then frame.glow:SetVertexColor(col[1], col[2], col[3], 0.35) end
    frame.liveData = data
    return frame
end

function MapMarkers:CreateLiveMarkerFrame(name, data)
    local frame = CreateFrame("Button", nil, UIParent)
    frame:SetSize(20, 20)
    frame:SetFrameStrata("HIGH")
    frame:SetFrameLevel(110)

    -- Äußerer Glow (ADD Blend – klassische WoW Aura-Optik)
    local glow = frame:CreateTexture(nil, "BACKGROUND")
    glow:SetTexture("Interface\\Cooldown\\star4")
    glow:SetBlendMode("ADD")
    glow:SetSize(30, 30)
    glow:SetPoint("CENTER")
    glow:SetAlpha(0.35)
    frame.glow = glow

    -- Klassen-farbiger Ring
    local ring = frame:CreateTexture(nil, "BORDER")
    ring:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder")
    ring:SetSize(22, 22)
    ring:SetPoint("CENTER")
    frame.ring = ring

    -- Klassen-Icon als Dot (16x16)
    local CLASS_ICONS_MAP = {
        [1]="Interface\\Icons\\ClassIcon_Warrior",
        [2]="Interface\\Icons\\ClassIcon_Paladin",
        [3]="Interface\\Icons\\ClassIcon_Hunter",
        [4]="Interface\\Icons\\ClassIcon_Rogue",
        [5]="Interface\\Icons\\ClassIcon_Priest",
        [6]="Interface\\Icons\\ClassIcon_DeathKnight",
        [7]="Interface\\Icons\\ClassIcon_Shaman",
        [8]="Interface\\Icons\\ClassIcon_Mage",
        [9]="Interface\\Icons\\ClassIcon_Warlock",
        [11]="Interface\\Icons\\ClassIcon_Druid",
    }
    local dot = frame:CreateTexture(nil, "ARTWORK")
    dot:SetSize(16, 16)
    dot:SetPoint("CENTER")
    dot:SetTexture(CLASS_ICONS_MAP[data.classId] or "Interface\\Icons\\INV_Misc_QuestionMark")
    frame.dot = dot

    -- Name-Label (klein, über dem Marker)
    local nameLabel = frame:CreateFontString(nil, "OVERLAY")
    nameLabel:SetFont("Fonts\\FRIZQT__.TTF", 7, "OUTLINE")
    nameLabel:SetPoint("BOTTOM", frame, "TOP", 0, 1)
    nameLabel:SetText(name)
    nameLabel:SetShadowOffset(1, -1)
    frame.nameLabel = nameLabel

    -- Shared Puls-Ticker aktivieren (einmalig für alle Marker)
    EnsureLivePulseTicker()

    -- Hover Tooltip
    frame:EnableMouse(true)
    frame:SetScript("OnEnter", function(self)
        local d = self.liveData or {}
        local UI2 = GDL:GetModule("UI")
        local col2 = UI2 and UI2:GetClassColor(d.classId) or {0.9,0.9,0.9}

        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        GameTooltip:ClearLines()
        GameTooltip:SetText("|cff00FF88[Live]|r " .. name, col2[1], col2[2], col2[3], true)
        GameTooltip:AddLine(GDL:T("Online Gildenmitglied"), 0.7, 0.7, 0.7)
        if d.posX and d.posY then
            GameTooltip:AddLine(string.format(GDL:T("Koordinaten: %.1f, %.1f"), d.posX * 100, d.posY * 100), 0.5, 0.9, 0.5)
        end
        local age = d.timestamp and (time() - d.timestamp) or 999
        GameTooltip:AddLine(string.format(GDL:T("Letzte Aktualisierung: vor %ds"), age), 0.4, 0.4, 0.4)
        GameTooltip:Show()
    end)
    frame:SetScript("OnLeave", function() GameTooltip:Hide() end)

    return frame
end

-- Minimap: Lebende Spieler als farbige Punkte
function MapMarkers:UpdateLiveMiniDots()
    for _, d in ipairs(liveMiniDots) do d:Hide() end

    if not self.minimapFrame then return end

    local playerMap = C_Map.GetBestMapForUnit("player")
    if not playerMap then return end
    local playerPos = C_Map.GetPlayerMapPosition(playerMap, "player")
    if not playerPos then return end
    local px, py = playerPos:GetXY()
    local myName = UnitName("player")
    local now = time()
    local idx = 0

    for name, data in pairs(livePositions) do
        if name ~= myName
           and data.mapId == playerMap
           and (now - data.timestamp) < POS_STALE_SEC then

            local dx = (data.posX - px) * 10000
            local dy = (data.posY - py) * 10000
            local dist = math.sqrt(dx * dx + dy * dy)
            if dist < 250 then
                idx = idx + 1
                local dot = liveMiniDots[idx]
                if not dot then
                    dot = CreateFrame("Frame", nil, self.minimapFrame)
                    dot:SetSize(10, 10)
                    dot:SetFrameStrata("HIGH")
                    local tex = dot:CreateTexture(nil, "OVERLAY")
                    tex:SetAllPoints()
                    tex:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder")
                    dot.tex = tex
                    dot:EnableMouse(true)
                    dot:SetScript("OnEnter", function(self)
                        GameTooltip:SetOwner(self, "ANCHOR_TOP")
                        GameTooltip:SetText("|cff00FF88[Live]|r " .. (self.liveName or "?"), 0.2, 1.0, 0.4)
                        GameTooltip:Show()
                    end)
                    dot:SetScript("OnLeave", function() GameTooltip:Hide() end)
                    liveMiniDots[idx] = dot
                end

                local UI3 = GDL:GetModule("UI")
                local col3 = UI3 and UI3:GetClassColor(data.classId) or {0, 1, 0}
                dot.tex:SetVertexColor(col3[1], col3[2], col3[3], 0.9)
                dot.liveName = name

                local miniRadius = Minimap:GetWidth() / 2
                local zoom = Minimap:GetZoom()
                local scale = miniRadius / (300 / (zoom + 1))
                local offX = dx * scale
                local offY = -dy * scale
                if math.abs(offX) < miniRadius - 5 and math.abs(offY) < miniRadius - 5 then
                    dot:SetPoint("CENTER", Minimap, "CENTER", offX, offY)
                    dot:Show()
                end
            end
        end
    end
end

function MapMarkers:InitializeMinimapSkulls()
    self.minimapSkulls = {}
    self.minimapFrame = CreateFrame("Frame", "GDLMinimapSkulls", Minimap)
    self.minimapFrame:SetAllPoints()
    self.minimapFrame:SetFrameStrata("MEDIUM")
    self.minimapFrame:SetFrameLevel(5)

    -- 3s-Update als echter Timer statt OnUpdate-Polling pro Frame
    C_Timer.NewTicker(3, function()
        self:UpdateMinimapSkulls()
        self:UpdateLiveMiniDots()
    end)
end

function MapMarkers:UpdateMinimapSkulls()
    if not self.minimapFrame then return end
    for _, skull in ipairs(self.minimapSkulls) do
        skull:Hide()
    end

    local guildData = GDL:GetGuildData()
    if not guildData or not guildData.deaths then return end

    local playerMap = C_Map.GetBestMapForUnit("player")
    if not playerMap then return end
    local playerPos = C_Map.GetPlayerMapPosition(playerMap, "player")
    if not playerPos then return end
    local px, py = playerPos:GetXY()

    local now = time()
    local idx = 0
    for _, d in ipairs(guildData.deaths) do
        if d.mapId and d.mapId == playerMap and d.posX and d.posX > 0 and d.posY and d.posY > 0
           and d.timestamp and (now - d.timestamp) < MARKER_DURATION then
            local dx = (d.posX - px) * 10000
            local dy = (d.posY - py) * 10000
            local dist = math.sqrt(dx * dx + dy * dy)
            if dist < 300 then
                idx = idx + 1
                local skull = self.minimapSkulls[idx]
                if not skull then
                    skull = CreateFrame("Frame", nil, self.minimapFrame)
                    skull:SetSize(18, 18)
                    skull:SetFrameStrata("HIGH")
                    local tex = skull:CreateTexture(nil, "OVERLAY")
                    tex:SetAllPoints()
                    tex:SetTexture("Interface\\AddOns\\GuildDeathLog\\Textures\\GDL_Tombstone.png")
                    tex:SetVertexColor(1, 1, 1, 1)
                    skull.tex = tex
                    skull:EnableMouse(true)
                    skull:SetScript("OnEnter", function(self)
                        GameTooltip:SetOwner(self, "ANCHOR_TOP")
                        GameTooltip:SetText(self.deathName or "?", 0.8, 0.1, 0.1)
                        GameTooltip:AddLine("Level " .. (self.deathLevel or "?") .. " - " .. (self.deathZone or ""), 1, 1, 1)
                        if self.deathDate then
                            GameTooltip:AddLine(self.deathDate, 0.7, 0.7, 0.7)
                        end
                        GameTooltip:Show()
                    end)
                    skull:SetScript("OnLeave", function() GameTooltip:Hide() end)
                    self.minimapSkulls[idx] = skull
                end
                skull.deathName = d.name
                skull.deathLevel = d.level
                skull.deathZone = d.zone
                skull.deathDate = d.timestamp and date("%d.%m.%y", d.timestamp) or nil

                local miniRadius = Minimap:GetWidth() / 2
                local zoom = Minimap:GetZoom()
                local scale = miniRadius / (300 / (zoom + 1))
                local offX = dx * scale
                local offY = -dy * scale
                if math.abs(offX) < miniRadius - 8 and math.abs(offY) < miniRadius - 8 then
                    skull:SetPoint("CENTER", Minimap, "CENTER", offX, offY)
                    skull:Show()
                else
                    skull:Hide()
                end
            end
        end
    end
end

-- WICHTIG (9.9.10): Das Gedenkbuch ist ein MEMORIAL — Gefallene bleiben fuer
-- immer erhalten. Frueher loeschte diese Funktion Tode > 30 Tage komplett aus
-- guildData.deaths und damit AUCH aus dem Buch. Die 30-Tage-Grenze gilt jetzt
-- ausschliesslich fuer die KARTEN-/MINIMAP-MARKER (Render-Altersfilter in
-- UpdateMarkers + UpdateMinimapSkulls). Hier wird nichts mehr geloescht.
function MapMarkers:PruneOldDeaths()
    -- absichtlich No-Op: Buch-Eintraege werden nie automatisch entfernt.
end

GDL:RegisterModule("MapMarkers", MapMarkers)
