-- TotemBar - bind.lua
-- Key-binding labels (for the Esc > Key Bindings menu) + the cast entry
-- points the Bindings.xml bodies call. Hover-bind mode is added below in a
-- later step. Loaded AFTER Bindings.xml (see the .toc).

TotemBar = TotemBar or {}

local ChatOut = DEFAULT_CHAT_FRAME or ChatFrame1

-- Section + fixed-action labels.
BINDING_HEADER_TOTEMBAR = "TotemBar"
BINDING_NAME_TOTEMBAR_DROPSET = "Drop all totems"
BINDING_NAME_TOTEMBAR_RECALL = "Totemic Recall"
BINDING_NAME_TOTEMBAR_TOGGLEBAR = "Toggle bar"
BINDING_NAME_TOTEMBAR_TOGGLEOPTIONS = "Toggle options"
BINDING_NAME_TOTEMBAR_TOGGLEBIND = "Toggle key-bind mode"

-- Per-element "cast the chosen totem" labels + per-element sub-headers.
do
    local elements = TotemBar.TOTEM_ELEMENTS
    for i = 1, table.getn(elements) do
        local e = elements[i]
        setglobal("BINDING_NAME_TOTEMBAR_CAST_" .. string.upper(e), "Cast " .. e .. " totem (chosen)")
        setglobal("BINDING_HEADER_TOTEMBAR_" .. string.upper(e), "TotemBar: " .. e .. " Totems")
    end
    -- Per-totem labels (must match Bindings.xml names via bindingSuffix).
    for i = 1, table.getn(elements) do
        local e = elements[i]
        local list = TotemBar.TOTEMS_BY_ELEMENT[e]
        for j = 1, table.getn(list) do
            local totem = list[j]
            setglobal("BINDING_NAME_TOTEMBAR_TOTEM_" .. TotemBar.bindingSuffix(totem), "Cast " .. totem)
        end
    end
end

-- Cast a specific totem by name (no-op in-game if not known).
function TotemBar.CastTotem(name)
    if name then
        CastSpellByName(name)
        -- keybind casts must feed the own-tracking timers exactly like the
        -- flyout click handler does (ui.lua) - without this, vanilla-only
        -- setups get no duration ring/pulse for keybind-cast totems.
        local element = TotemBar.elementOf(name)
        if element then
            TotemBar.recordCast(element, name)
        end
    end
end

-- Cast the currently-chosen totem for an element.
function TotemBar.CastElement(element)
    local n = TotemBarDB and TotemBarDB.chosen and TotemBarDB.chosen[element]
    if n then
        CastSpellByName(n)
        -- keybind casts must feed the own-tracking timers exactly like the
        -- flyout click handler does (ui.lua) - without this, vanilla-only
        -- setups get no duration ring/pulse for keybind-cast totems.
        TotemBar.recordCast(element, n)
    else
        ChatOut:AddMessage("TotemBar: no totem chosen for " .. tostring(element) .. ".")
    end
end

-- Cast Totemic Recall and clear own-tracking. Runs the exact same
-- TotemBar.manualRecall (core/cast.lua) as the Recall button's left-click, so
-- the keybind and the button can never diverge: it owns the "nothing is out"
-- gate, the "is Recall even castable right now" check (a press that cannot go
-- out must NOT wipe the timers of totems still standing), the queue-safe cast
-- that keeps nampower from firing a late Recall into a fresh set, the refund
-- snapshot and the own-tracking wipe. Only the chat feedback lives here.
function TotemBar.CastRecall()
    if not TotemBar.manualRecall then
        return
    end
    local action = TotemBar.manualRecall()
    if action == "none-out" then
        ChatOut:AddMessage("TotemBar: no totems out - not recalling (saves the 6s cooldown). Press again to recall anyway.")
    elseif action == "cooldown" then
        ChatOut:AddMessage("TotemBar: Totemic Recall isn't ready yet - totem timers kept.")
    end
end

-- Stub so the TOTEMBAR_TOGGLEBIND binding never calls nil before Task 4
-- fills it in. Replaced (same name) by the real implementation below.
if not TotemBar.ToggleBindMode then
    function TotemBar.ToggleBindMode()
        ChatOut:AddMessage("TotemBar: key-bind mode not available.")
    end
end

-- ===== Hover-bind mode =====
-- Toggle a mode where hovering a bar button / flyout totem and pressing a
-- key binds that key to the thing's action (buttons -> CLICK binding, flyout
-- totems -> the named per-totem binding). ESC clears the hovered thing's
-- binding. Bindings persist via the client (SaveBindings), no addon SV.

local bindMode = false
local captureFrame = nil
local infoBox = nil

-- Base keys that are pure modifiers - never bind these alone.
local BARE_MODIFIERS = {
    LSHIFT = true, RSHIFT = true, LCTRL = true, RCTRL = true,
    LALT = true, RALT = true, UNKNOWN = true,
}

function TotemBar.isBindMode()
    return bindMode
end

-- Resolves the mouse-focused frame to a binding action. Uses the pure
-- TotemBar.actionForButton with the frame's global name and, for a flyout
-- icon, its current .totemName.
local function actionForFocus(focus)
    if not focus then
        return nil
    end
    local totemName = focus.totemName   -- set on flyout icon buttons when shown
    local fname = focus.GetName and focus:GetName() or nil
    return TotemBar.actionForButton(fname, totemName)
end

local function ensureCaptureFrame()
    if captureFrame then
        return captureFrame
    end
    local f = CreateFrame("Frame", "TotemBarBindCapture", UIParent)
    f:SetAllPoints(UIParent)
    f:SetFrameStrata("FULLSCREEN_DIALOG")
    f:EnableKeyboard(false)
    f:Hide()
    f:SetScript("OnKeyUp", function()
        local key = arg1   -- NOTE: OnKeyUp arg1 = key name string (matches pfUI's hoverbind capture)
        if not key or BARE_MODIFIERS[key] then
            return
        end
        local focus = GetMouseFocus()
        local action = actionForFocus(focus)
        if key == "ESCAPE" and not action then
            -- Keyboard capture swallows all keys while bind mode is on, so
            -- ESC over nothing is the keyboard exit (the options button also
            -- toggles it off). ESC over a button clears that button below.
            TotemBar.ToggleBindMode()
            return
        end
        if not action then
            return
        end
        if key == "ESCAPE" then
            local k1, k2 = GetBindingKey(action)
            if k1 then SetBinding(k1) end
            if k2 then SetBinding(k2) end
        else
            local full = TotemBar.modifierPrefix(IsAltKeyDown(), IsControlKeyDown(), IsShiftKeyDown()) .. key
            SetBinding(full)          -- clear whatever `full` was bound to
            SetBinding(full, action)  -- bind it to this action
        end
        SaveBindings(GetCurrentBindingSet())
        if TotemBar.refreshBindOverlays then
            TotemBar.refreshBindOverlays()
        end
    end)
    captureFrame = f
    return f
end

-- Lazily builds a visible on-screen info box shown while bind mode is
-- active, so the mode (which otherwise only shows via a chat message and
-- stays on until ESC) is hard to miss. Rev 4 UI chrome: shared bespoke
-- panel skin (ui.lua's PANEL_BACKDROP), matching the rest of the addon's
-- chrome, regardless of pfUI presence.
local function ensureBindInfoBox()
    if infoBox then
        return infoBox
    end
    local f = CreateFrame("Frame", "TotemBarBindInfo", UIParent)
    f:SetWidth(380)
    f:SetHeight(80)
    f:SetPoint("TOP", UIParent, "TOP", 0, -160)
    f:SetFrameStrata("FULLSCREEN_DIALOG")
    -- Cheap guard: bind.lua loads LAST per the .toc (after ui.lua), so
    -- TotemBar.PANEL_BACKDROP is always set by the time this runs - the
    -- fallback only protects against a future load-order change.
    local backdrop = TotemBar.PANEL_BACKDROP or {
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
        edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
        tile = true, tileSize = 32, edgeSize = 32,
        insets = { left = 11, right = 12, top = 12, bottom = 11 },
    }
    f:SetBackdrop(backdrop)
    f:SetBackdropColor(1, 1, 1, 0.97)
    local t = f:CreateFontString("TotemBarBindInfoText", "OVERLAY", "GameFontNormal")
    t:SetPoint("CENTER", f, "CENTER", 0, 0)
    t:SetJustifyH("CENTER")
    t:SetText("|cff33ffccKey-Bind Mode ACTIVE|r\nHover a bar button or flyout totem and press a key to bind it.\nESC over a button clears its key.  ESC over empty space (or the options button) exits.")
    if pfUI and pfUI.font_default then
        t:SetFont(pfUI.font_default, 12)
    end
    f:Hide()
    infoBox = f
    return f
end

function TotemBar.ToggleBindMode()
    bindMode = not bindMode
    local f = ensureCaptureFrame()
    if bindMode then
        f:EnableKeyboard(true)
        f:Show()
        ensureBindInfoBox():Show()
        ChatOut:AddMessage("TotemBar: key-bind mode ON - hover a button or flyout totem and press a key to bind. ESC over a button clears its key; ESC over nothing (or the options button) exits.")
    else
        f:EnableKeyboard(false)
        f:Hide()
        if infoBox then
            infoBox:Hide()
        end
        ChatOut:AddMessage("TotemBar: key-bind mode OFF.")
    end
    if TotemBar.refreshBindOverlays then
        TotemBar.refreshBindOverlays()
    end
end

-- Refresh overlays whenever the client's bindings change.
local bindEvents = CreateFrame("Frame", "TotemBarBindEventFrame", UIParent)
bindEvents:RegisterEvent("UPDATE_BINDINGS")
bindEvents:SetScript("OnEvent", function()
    if TotemBar.refreshBindOverlays then
        TotemBar.refreshBindOverlays()
    end
end)
