--[[
    Core/Events.lua

    Two things, deliberately kept in one small module:

      1. A single hidden frame that receives Blizzard game events and fans them
         out to subscribers. One frame for the whole addon instead of one per
         module.
      2. An internal message bus so modules can talk to each other without
         holding references to each other (e.g. the UI reacts to
         "COLLECTION_CHANGED" without knowing who raised it).

    Nothing here polls: no OnUpdate script is installed.
]]

local _, ns = ...

local Events = ns.NewModule("Events")
ns.Events = Events

local listener = CreateFrame("Frame")

-- gameHandlers[event][owner] = function(event, ...)
local gameHandlers = {}
-- messageHandlers[message][owner] = function(message, ...)
local messageHandlers = {}

listener:SetScript("OnEvent", function(_, event, ...)
    local handlers = gameHandlers[event]
    if not handlers then
        return
    end

    for owner, handler in pairs(handlers) do
        local ok, err = pcall(handler, event, ...)
        if not ok then
            ns.Debug:ReportError(("event %s (%s)"):format(event, tostring(owner)), err)
        end
    end
end)

--=============================================================================
-- Game events
--=============================================================================

--- Subscribes to a Blizzard game event.
-- @param event string Event name, e.g. "PLAYER_LOGIN".
-- @param owner any Unique subscriber key, used to unregister later.
-- @param handler function Receives (event, ...).
function Events:RegisterEvent(event, owner, handler)
    if not gameHandlers[event] then
        gameHandlers[event] = {}
        listener:RegisterEvent(event)
    end

    gameHandlers[event][owner] = handler
end

--- Removes one subscriber from an event, unregistering the event entirely
-- once nobody is listening any more.
function Events:UnregisterEvent(event, owner)
    local handlers = gameHandlers[event]
    if not handlers then
        return
    end

    handlers[owner] = nil

    if next(handlers) == nil then
        gameHandlers[event] = nil
        listener:UnregisterEvent(event)
    end
end

--=============================================================================
-- Internal messages
--=============================================================================

--- Subscribes to an internal addon message.
-- @param message string Message name, e.g. "COLLECTION_CHANGED".
-- @param owner any Unique subscriber key.
-- @param handler function Receives (message, ...).
function Events:Subscribe(message, owner, handler)
    if not messageHandlers[message] then
        messageHandlers[message] = {}
    end

    messageHandlers[message][owner] = handler
end

--- Removes one subscriber from an internal message.
function Events:Unsubscribe(message, owner)
    local handlers = messageHandlers[message]
    if handlers then
        handlers[owner] = nil
    end
end

--- Broadcasts an internal addon message.
function Events:Send(message, ...)
    local handlers = messageHandlers[message]
    if not handlers then
        return
    end

    for owner, handler in pairs(handlers) do
        local ok, err = pcall(handler, message, ...)
        if not ok then
            ns.Debug:ReportError(("message %s (%s)"):format(message, tostring(owner)), err)
        end
    end
end
