local addonName, Addon = ...
local L = Addon.L

local eventFrame = CreateFrame("Frame")
local mapRefreshGeneration = 0

local mapChangeEvents = {
    PLAYER_ENTERING_WORLD = true,
    PLAYER_MAP_CHANGED = true,
    NEW_WMO_CHUNK = true,
    ZONE_CHANGED = true,
    ZONE_CHANGED_INDOORS = true,
    ZONE_CHANGED_NEW_AREA = true,
}

local contentEvents = {
    QUEST_LOG_UPDATE = true,
    QUEST_POI_UPDATE = true,
    QUESTLINE_UPDATE = true,
    QUEST_ACCEPTED = true,
    QUEST_REMOVED = true,
    QUEST_WATCH_LIST_CHANGED = true,
    TASK_PROGRESS_UPDATE = true,
    AREA_POIS_UPDATED = true,
    VIGNETTES_UPDATED = true,
    DYNAMIC_GOSSIP_POI_UPDATED = true,
    TAXI_NODE_STATUS_CHANGED = true,
    RESEARCH_ARTIFACT_DIG_SITE_UPDATED = true,
    ARCHAEOLOGY_TOGGLE = true,
    BOSS_KILL = true,
    ENCOUNTER_END = true,
    PORTRAITS_UPDATED = true,
}

local focusResetEvents = {
    PLAYER_ENTERING_WORLD = true,
    PLAYER_MAP_CHANGED = true,
    ZONE_CHANGED_NEW_AREA = true,
}

local deathEvents = {
    PLAYER_DEAD = true,
    PLAYER_ALIVE = true,
    PLAYER_UNGHOST = true,
    PLAYER_SKINNED = true,
    CORPSE_IN_RANGE = true,
    CORPSE_OUT_OF_RANGE = true,
    CORPSE_IN_INSTANCE = true,
}

local function ScheduleMapRefresh(resetFocus)
    mapRefreshGeneration = mapRefreshGeneration + 1
    local generation = mapRefreshGeneration

    if resetFocus then
        Addon.MapView:PrepareForMapChange()
    end

    -- Al entrar en una instancia, el mapa y su piso no siempre están listos
    -- en el primer evento. Reintentamos durante unos segundos y descartamos
    -- los temporizadores antiguos si llega un cambio de zona más reciente.
    for _, delay in ipairs({ 0.05, 0.35, 0.90, 1.80, 3.00, 5.50, 8.00 }) do
        C_Timer.After(delay, function()
            if generation ~= mapRefreshGeneration or not Addon.UI:IsMapShown() then
                return
            end
            Addon.MapView:Rebuild(false)
        end)
    end
end

local function RegisterRuntimeEvents(frame)
    for event in pairs(mapChangeEvents) do
        frame:RegisterEvent(event)
    end
    for event in pairs(contentEvents) do
        frame:RegisterEvent(event)
    end
    frame:RegisterEvent("GROUP_ROSTER_UPDATE")
    frame:RegisterEvent("MAP_EXPLORATION_UPDATED")
    frame:RegisterEvent("UNIT_DIED")
    frame:RegisterEvent("NAME_PLATE_UNIT_ADDED")
    frame:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
    frame:RegisterEvent("UNIT_HEALTH")
    frame:RegisterEvent("UNIT_FLAGS")
    frame:RegisterEvent("PLAYER_TARGET_CHANGED")
    for event in pairs(deathEvents) do
        frame:RegisterEvent(event)
    end
    if (select(4, GetBuildInfo()) or 0) < 120000 then
        frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
    end
    frame:RegisterEvent("CHALLENGE_MODE_START")
end

local function Initialize(frame)
    Addon:InitializeDatabase()
    Addon.Routes:InitializeStorage()
    Addon.UI:Initialize()
    Addon.Commands:Register()
    RegisterRuntimeEvents(frame)
    frame:UnregisterEvent("ADDON_LOADED")

    C_Timer.After(0.50, function()
        if not Addon.state.dungeonChoiceActive then
            Addon.UI:ApplyReplacementMode(Addon.db.replaceMinimap, false)
        end
    end)

    if not Addon.db.welcomeShown then
        Addon.db.welcomeShown = true
        Addon:Print(L.INSTALLED_HELP)
    end
end

eventFrame:RegisterEvent("ADDON_LOADED")
eventFrame:SetScript("OnEvent", function(self, event, ...)
    if event == "ADDON_LOADED" then
        local loadedAddon = ...
        if loadedAddon == addonName then
            Initialize(self)
        end
        return
    end

    if event == "PLAYER_ENTERING_WORLD" then
        Addon.UI:HandlePlayerEnteringWorld(...)
    end

    if event == "UNIT_DIED" then
        Addon.Routes:HandleUnitDied(...)
        return
    elseif event == "NAME_PLATE_UNIT_ADDED" then
        Addon.Routes:TrackUnit(...)
        return
    elseif event == "NAME_PLATE_UNIT_REMOVED" then
        Addon.Routes:HandleTrackedUnitRemoved(...)
        return
    elseif event == "UNIT_HEALTH" or event == "UNIT_FLAGS" then
        Addon.Routes:HandleTrackedUnitHealth(...)
        return
    elseif event == "PLAYER_TARGET_CHANGED" then
        Addon.Routes:TrackUnit("target")
        return
    elseif event == "COMBAT_LOG_EVENT_UNFILTERED" then
        Addon.Routes:HandleCombatLogEvent()
        return
    elseif event == "CHALLENGE_MODE_START" then
        local dungeonIndex = Addon.Routes:GetCurrentDungeonIndex()
        local route = dungeonIndex and Addon.Routes:GetSelectedRoute(dungeonIndex)
        if route then
            Addon.Routes:ResetProgress(route)
        end
        return
    elseif deathEvents[event] then
        if Addon.UI:IsMapShown() then
            Addon.Markers:UpdateCorpseMarker()
            Addon.Markers:UpdateShapeClipping()
        end
        return
    elseif event == "DYNAMIC_GOSSIP_POI_UPDATED" then
        Addon.Markers:ResetGossipPOIState()
        if Addon.UI:IsMapShown() then
            Addon.Markers:ScheduleContentRefresh()
        end
        return
    elseif mapChangeEvents[event] then
        Addon.Routes:ClearTrackedUnits()
        Addon.Coordinates:ClearCache()
        if Addon.UI:IsMapShown() then
            ScheduleMapRefresh(focusResetEvents[event])
        end
    elseif event == "GROUP_ROSTER_UPDATE" and Addon.UI:IsMapShown() then
        Addon.Markers:UpdateMarkers()
    elseif event == "MAP_EXPLORATION_UPDATED" and Addon.UI:IsMapShown() then
        Addon.MapView:BuildExplorationOverlays()
        Addon.MapView:UpdateGeometry()
    elseif contentEvents[event] and Addon.UI:IsMapShown() then
        Addon.Markers:ScheduleContentRefresh()
    end
end)
