-- TotemBar - options.lua
-- Standalone options panel (no Blizzard InterfaceOptions integration). Built
-- once (lazy) on first ToggleOptions; repopulated from TotemBarDB on show.
-- Opened by the minimap button's left-click and by /tb options. WoW-API
-- file (parse-checked only). Widget wiring uses 1.12 templates:
-- UICheckButtonTemplate (GetChecked() -> 1/nil) and OptionsSliderTemplate
-- (SetMinMaxValues before SetValue; $parentLow/$parentHigh/$parentText).

TotemBar = TotemBar or {}

local ChatOut = DEFAULT_CHAT_FRAME or ChatFrame1
local optionsFrame = nil

-- Applies pfUI's font to a FontString (the main "matches pfUI" tweak) at the
-- given size, or falls back to a small game font when pfUI is absent.
local function ApplyFont(fontString, size)
    if not fontString then
        return
    end
    if pfUI and pfUI.font_default then
        local sz = size or (pfUI_config and pfUI_config.global and pfUI_config.global.font_size) or 12
        fontString:SetFont(pfUI.font_default, sz)
    else
        fontString:SetFontObject(GameFontNormalSmall)
    end
end

-- Adds a wrapped explanatory GameTooltip on hover. Chains any existing
-- OnEnter/OnLeave (e.g. pfUI's SkinButton hover-highlight) instead of
-- replacing it, so skinning still works. No HookScript on 1.12.
local function AddTooltip(widget, text)
    if not widget or not text then
        return
    end
    widget.tbTip = text
    local oldEnter = widget:GetScript("OnEnter")
    local oldLeave = widget:GetScript("OnLeave")
    widget:SetScript("OnEnter", function()
        if oldEnter then oldEnter() end
        GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
        GameTooltip:SetText(this.tbTip, 1, 1, 1, 1, 1)
        GameTooltip:Show()
    end)
    widget:SetScript("OnLeave", function()
        if oldLeave then oldLeave() end
        GameTooltip:Hide()
    end)
end

-- Unique widget-name counters (template child labels resolve via getglobal,
-- so every widget needs a non-nil, unique frame name).
local cbIndex = 0
local slIndex = 0

-- Factory: a labeled checkbox wired to getter/setter. getter()->boolean,
-- setter(boolean). Returns the CheckButton.
local function CreateCheckbox(parent, label, getter, setter)
    cbIndex = cbIndex + 1
    local name = "TotemBarOptCheck" .. cbIndex
    local cb = CreateFrame("CheckButton", name, parent, "UICheckButtonTemplate")
    cb:SetWidth(24)
    cb:SetHeight(24)
    local lbl = getglobal(name .. "Text")
    if lbl then
        lbl:SetText(label)
        ApplyFont(lbl)
    end
    cb.tbGet = getter
    cb.tbSet = setter
    cb:SetScript("OnClick", function()
        this.tbSet(this:GetChecked() == 1)
    end)
    if pfUI and pfUI.api and pfUI.api.SkinCheckbox then
        pfUI.api.SkinCheckbox(cb)
    end
    return cb
end

-- Factory: a labeled slider wired to getter/setter over [minVal,maxVal] in
-- `step` increments. `fmt` is a printf pattern for the live value text
-- (e.g. "UI size: %.2f"). Returns the Slider.
local function CreateSlider(parent, label, minVal, maxVal, step, fmt, getter, setter)
    slIndex = slIndex + 1
    local name = "TotemBarOptSlider" .. slIndex
    local sl = CreateFrame("Slider", name, parent, "OptionsSliderTemplate")
    sl:SetWidth(200)
    sl:SetHeight(16)
    sl:SetMinMaxValues(minVal, maxVal)   -- BEFORE SetValue, else clamps to 0
    sl:SetValueStep(step)
    local low, high, txt = getglobal(name .. "Low"), getglobal(name .. "High"), getglobal(name .. "Text")
    if low then low:SetText(tostring(minVal)); ApplyFont(low) end
    if high then high:SetText(tostring(maxVal)); ApplyFont(high) end
    if txt then
        -- Left-align the value label above the slider (the template centers it),
        -- so it lines up with the left-aligned checkbox labels. pfUI font.
        txt:ClearAllPoints()
        txt:SetPoint("BOTTOMLEFT", sl, "TOPLEFT", 0, 2)
        txt:SetJustifyH("LEFT")
        ApplyFont(txt)
    end
    sl.tbLabel = label
    sl.tbFmt = fmt
    sl.tbText = txt
    sl.tbSet = setter
    sl.tbGet = getter
    sl:SetScript("OnValueChanged", function()
        local v = this:GetValue()
        if this.tbText then
            this.tbText:SetText(string.format(this.tbFmt, v))
        end
        this.tbSet(v)
    end)
    if pfUI and pfUI.api and pfUI.api.SkinSlider then
        pfUI.api.SkinSlider(sl)
    end
    return sl
end

-- Label text for the bar-layout cycle button, reflecting the current choice.
local function BarLayoutLabel()
    return "Layout: " .. (TotemBarDB.barLayout or "1x6")
end

-- Factory: a labeled push button wired to an onClick. Returns the Button.
-- Named (project convention: name all frames) so the pfDebug profiler can attribute it.
local function CreateButton(parent, name, label, onClick)
    local btn = CreateFrame("Button", name, parent, "UIPanelButtonTemplate")
    btn:SetWidth(140)
    btn:SetHeight(22)
    btn:SetText(label)
    btn:SetScript("OnClick", onClick)
    if pfUI and pfUI.api and pfUI.api.SkinButton then
        pfUI.api.SkinButton(btn)
    end
    return btn
end

-- Factory: a static hairline divider spanning the panel's content width at
-- cursor position y (Rev 4 UI chrome, section-break decoration). Anonymous
-- texture - it's a static decoration, not an interactive/named widget.
local function CreateDivider(parent, y)
    local d = parent:CreateTexture(nil, "ARTWORK")
    d:SetTexture(TotemBar.UI_TEXTURE)
    local c = TotemBar.uiCoords[TotemBar.UI_DIVIDER_CELL]
    d:SetTexCoord(c.l, c.r, c.t, c.b)
    d:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, y)
    d:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -14, y)
    d:SetHeight(7)
    return d
end

-- All widgets, stored so OnShow can repopulate them from TotemBarDB.
local widgets = {}

local function BuildOptionsFrame()
    if optionsFrame then
        return optionsFrame
    end

    local f = CreateFrame("Frame", "TotemBarOptionsFrame", UIParent)
    f:SetWidth(280)
    -- 472 + 44 + 28 + 28 + 44 + 28: room for the header emblem (+16, taller
    -- than the old title-only header), two section dividers (+14 each), the
    -- "Show pulse waves" checkbox row (+28, the pulse ring's countdown
    -- readout sits alongside the ripple), the bar-layout cycle button row
    -- (+28), the "Button spacing" slider row (+44, same slider-row
    -- height as guard/gap/scale above it), and the "Show drop-all button"
    -- checkbox row (+28) - see the layout-cursor deltas below (same
    -- mechanism as the Task-5 height bump). Not cosmetic: without the last
    -- +28 the "Key bind mode" button would land on the version footer.
    f:SetHeight(628)
    f:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
    f:SetFrameStrata("DIALOG")
    -- Rev 4 UI chrome: shared bespoke panel skin (see ui.lua's PANEL_BACKDROP)
    -- instead of the Blizzard dialog backdrop or pfUI's generic skin, so the
    -- options panel matches the bar's own art (emblem/dividers below pair
    -- with this border/bg specifically).
    f:SetBackdrop(TotemBar.PANEL_BACKDROP)
    f:SetBackdropColor(1, 1, 1, 0.97)
    f:SetMovable(true)
    f:EnableMouse(true)
    f:RegisterForDrag("LeftButton")
    f:SetScript("OnDragStart", function() this:StartMoving() end)
    f:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
    f:SetClampedToScreen(true)

    -- Plain gold title, no emblem (the Rev-4 totem glyph was removed on
    -- user request - the silhouette read, well, unfortunate).
    local title = f:CreateFontString("TotemBarOptionsTitle", "OVERLAY", "GameFontNormal")
    title:SetPoint("TOPLEFT", f, "TOPLEFT", 16, -16)
    title:SetText("TotemBar Options")
    ApplyFont(title, 14)
    title:SetTextColor(0.85, 0.66, 0.31)

    local close = CreateFrame("Button", "TotemBarOptionsClose", f, "UIPanelCloseButton")
    close:SetPoint("TOPRIGHT", f, "TOPRIGHT", -8, -8)
    if pfUI and pfUI.api and pfUI.api.SkinCloseButton then
        pfUI.api.SkinCloseButton(close, f)
    end

    -- Layout cursor (back to the title-only header spacing).
    local x = 24
    local y = -44
    local function place(w, dy)
        w:SetPoint("TOPLEFT", f, "TOPLEFT", x, y)
        y = y + dy
    end

    -- Checkboxes.
    widgets.lock = CreateCheckbox(f, "Lock bar",
        function() return TotemBarDB.locked end,
        function(v) TotemBarDB.locked = v end)
    place(widgets.lock, -28)
    AddTooltip(widgets.lock, "Locks the bar in place so you can't drag it by accident. Uncheck to reposition it.")

    widgets.autoRecall = CreateCheckbox(f, "Auto-recall before setting",
        function() return TotemBarDB.autoRecall end,
        function(v)
            TotemBarDB.autoRecall = v
            if TotemBar.RefreshRecallIndicator then TotemBar.RefreshRecallIndicator() end
        end)
    place(widgets.autoRecall, -28)
    AddTooltip(widgets.autoRecall, "When on, the Totems macro casts Totemic Recall before re-dropping your totems (refunds some mana when relocating).")

    widgets.show = CreateCheckbox(f, "Show bar",
        function() return not TotemBarDB.hidden end,
        function(v)
            if v then
                TotemBarDB.hidden = false
                if TotemBarFrame then TotemBarFrame:Show() end
            else
                TotemBarDB.hidden = true
                if TotemBarFrame then TotemBarFrame:Hide() end
            end
        end)
    place(widgets.show, -28)
    AddTooltip(widgets.show, "Shows or hides the totem bar. The choice is remembered between sessions.")

    -- Same kind of switch as "Show bar" above (visibility only, not a
    -- feature toggle), hence the same group. ApplyBarLayout hides/shows the
    -- button AND re-lays out the grid, so the bar resizes immediately -
    -- no /reload needed.
    widgets.dropSet = CreateCheckbox(f, "Show drop-all button",
        function() return TotemBarDB.showDropSetButton end,
        function(v)
            TotemBarDB.showDropSetButton = v
            if TotemBar.ApplyBarLayout then TotemBar.ApplyBarLayout() end
        end)
    place(widgets.dropSet, -40)
    AddTooltip(widgets.dropSet, "Shows or hides the 'Drop all totems' button on the bar. Its key binding keeps working while it is hidden - rebind it under Key Bindings in the game menu.")

    widgets.ring = CreateCheckbox(f, "Show duration ring",
        function() return TotemBarDB.showDurationRing end,
        function(v)
            TotemBarDB.showDurationRing = v
            if TotemBar.RefreshPulseUI then TotemBar.RefreshPulseUI() end
        end)
    place(widgets.ring, -28)
    AddTooltip(widgets.ring, "Shows a colored arc on each button's ring that empties as the totem's remaining time runs out. The ring frame itself always stays visible.")

    widgets.pulse = CreateCheckbox(f, "Show pulse ring",
        function() return TotemBarDB.showPulseBars end,
        function(v)
            TotemBarDB.showPulseBars = v
            if TotemBar.RefreshPulseUI then TotemBar.RefreshPulseUI() end
        end)
    place(widgets.pulse, -28)
    AddTooltip(widgets.pulse, "Shows a thin inner ring around the icon that fills up toward the totem's next pulse, then resets.")

    widgets.pulseWaves = CreateCheckbox(f, "Show pulse waves",
        function() return TotemBarDB.showPulseWaves end,
        function(v)
            TotemBarDB.showPulseWaves = v
            if TotemBar.RefreshPulseUI then TotemBar.RefreshPulseUI() end
        end)
    place(widgets.pulseWaves, -28)
    AddTooltip(widgets.pulseWaves, "Shows an expanding ripple in the totem's color each time a pulsing totem fires.")

    widgets.timerText = CreateCheckbox(f, "Show countdown text",
        function() return TotemBarDB.showTimerText end,
        function(v)
            TotemBarDB.showTimerText = v
            if TotemBar.RefreshPulseUI then TotemBar.RefreshPulseUI() end
        end)
    place(widgets.timerText, -28)
    AddTooltip(widgets.timerText, "Shows the remaining-seconds text under each element button (the ring shows the same information graphically).")

    -- Bar-layout cycle button: click steps through TotemBar.BAR_LAYOUTS
    -- (wrapping), saves the choice, and re-lays out the live bar.
    widgets.layout = CreateButton(f, "TotemBarOptLayoutButton", BarLayoutLabel(), function()
        local layouts = TotemBar.BAR_LAYOUTS
        local n = table.getn(layouts)
        local current = TotemBarDB.barLayout or "1x6"
        local currentIdx = 1
        for i = 1, n do
            if layouts[i] == current then
                currentIdx = i
            end
        end
        local nextIdx = math.mod(currentIdx, n) + 1
        TotemBarDB.barLayout = layouts[nextIdx]
        this:SetText(BarLayoutLabel())
        if TotemBar.ApplyBarLayout then
            TotemBar.ApplyBarLayout()
        end
    end)
    place(widgets.layout, -28)
    AddTooltip(widgets.layout, "Cycles the bar arrangement: one row of six, two rows of three, or three rows of two.")

    -- Section divider (Rev 4 UI chrome): checkboxes above, sliders below.
    CreateDivider(f, y)
    y = y - 14

    -- Sliders (leave headroom below each for its low/high/value text).
    widgets.guard = CreateSlider(f, "Recall guard (sec)", 0, 5, 0.5, "Recall guard: %.1fs",
        function() return TotemBarDB.recallGuardSeconds end,
        function(v) TotemBarDB.recallGuardSeconds = v end)
    place(widgets.guard, -44)
    AddTooltip(widgets.guard, "A second press of the Totems macro within this many seconds skips Totemic Recall, so a double-press won't pull the totems you just dropped. 0 = no guard.")

    widgets.gap = CreateSlider(f, "Cycle reset gap (sec)", 0.5, 5, 0.5, "Cycle gap: %.1fs",
        function() return TotemBarDB.gapSeconds end,
        function(v) TotemBarDB.gapSeconds = v end)
    place(widgets.gap, -44)
    AddTooltip(widgets.gap, "How long a pause (in seconds) restarts the one-per-press cast cycle back at the first totem.")

    widgets.scale = CreateSlider(f, "UI size", 0.5, 2.0, 0.05, "UI size: %.2f",
        function() return TotemBarDB.scale end,
        function(v)
            if TotemBar.SetBarScale then
                TotemBar.SetBarScale(v)
            elseif TotemBarFrame then
                TotemBarDB.scale = v
                TotemBarFrame:SetScale(v)
            end
        end)
    place(widgets.scale, -44)
    AddTooltip(widgets.scale, "Scales the whole bar. It stays anchored at its top-left corner, and the flyout scales with it.")

    widgets.buttonGap = CreateSlider(f, "Button spacing", 10, 30, 1, "Spacing: %.0fpx",
        function() return TotemBarDB.buttonGap end,
        function(v)
            if TotemBar.SetButtonGap then
                TotemBar.SetButtonGap(v)
            else
                TotemBarDB.buttonGap = v
            end
        end)
    place(widgets.buttonGap, -44)
    AddTooltip(widgets.buttonGap, "Distance between the bar buttons. Applies immediately.")

    -- Section divider (Rev 4 UI chrome): sliders above, action buttons below.
    CreateDivider(f, y)
    y = y - 14

    optionsFrame = f
    -- Buttons (Reset position, Create macro) are added in Task 4.
    if TotemBar.BuildOptionsButtons then
        TotemBar.BuildOptionsButtons(f, x, y)
    end

    -- Repopulate every widget from TotemBarDB whenever the panel shows.
    f:SetScript("OnShow", function()
        widgets.lock:SetChecked(TotemBarDB.locked and 1 or nil)
        widgets.autoRecall:SetChecked(TotemBarDB.autoRecall and 1 or nil)
        widgets.show:SetChecked((not TotemBarDB.hidden) and 1 or nil)
        widgets.dropSet:SetChecked(TotemBarDB.showDropSetButton and 1 or nil)
        widgets.ring:SetChecked(TotemBarDB.showDurationRing and 1 or nil)
        widgets.pulse:SetChecked(TotemBarDB.showPulseBars and 1 or nil)
        widgets.pulseWaves:SetChecked(TotemBarDB.showPulseWaves and 1 or nil)
        widgets.timerText:SetChecked(TotemBarDB.showTimerText and 1 or nil)
        widgets.layout:SetText(BarLayoutLabel())
        widgets.guard:SetValue(TotemBar.clampValue(TotemBarDB.recallGuardSeconds, 0, 5))
        widgets.gap:SetValue(TotemBar.clampValue(TotemBarDB.gapSeconds, 0.5, 5))
        widgets.scale:SetValue(TotemBar.clampValue(TotemBarDB.scale, 0.5, 2.0))
        widgets.buttonGap:SetValue(TotemBar.clampValue(TotemBarDB.buttonGap, 10, 30))
    end)

    -- Version footer (bottom-centre), read from the .toc so it always matches
    -- the real addon version instead of a hardcoded duplicate.
    local ver = (GetAddOnMetadata and GetAddOnMetadata("TotemBar", "Version")) or "0.1.0"
    local verFS = f:CreateFontString("TotemBarOptionsVersion", "OVERLAY")
    ApplyFont(verFS, 10)
    verFS:SetPoint("BOTTOM", f, "BOTTOM", 0, 6)
    verFS:SetText("v" .. ver)
    verFS:SetTextColor(0.6, 0.6, 0.6)

    -- ESC closes it.
    tinsert(UISpecialFrames, "TotemBarOptionsFrame")

    f:Hide()
    return f
end

-- Creates or updates the "Totems" convenience macro from TotemBar.macroSpec.
-- General (account) macro, capped at 18 in 1.12; if the cap is full and no
-- "Totems" macro exists yet, reports and does nothing. Never auto-places it
-- on the action bar (no 1.12 API for that - the player drags it).
local function ApplyTotemsMacro()
    local name, body, icon = TotemBar.macroSpec()
    local existing = GetMacroIndexByName(name)
    if existing and existing > 0 then
        EditMacro(existing, name, icon, body, 1, nil)
        ChatOut:AddMessage("TotemBar: '" .. name .. "' macro updated - drag it to your action bar.")
        return
    end
    local numGlobal = GetNumMacros()   -- returns global, perChar in 1.12
    if numGlobal and numGlobal >= 18 then
        ChatOut:AddMessage("TotemBar: macro slots full (18) - free one and retry.")
        return
    end
    CreateMacro(name, icon, body, nil)
    ChatOut:AddMessage("TotemBar: '" .. name .. "' macro created - drag it to your action bar.")
end

-- Adds the Reset-position and Create-macro buttons to the options frame.
-- Called by BuildOptionsFrame (Task 3) after the sliders, at layout cursor
-- (x, yStart). Kept separate so the panel body and the action buttons are
-- two focused units.
function TotemBar.BuildOptionsButtons(f, x, yStart)
    -- Full content width (frame width minus the left/right margin x), so the
    -- buttons read as a balanced block instead of a narrow left-biased pair.
    local w = f:GetWidth() - (x * 2)

    local reset = CreateButton(f, "TotemBarOptResetButton", "Reset position", function()
        if TotemBar.ResetPosition then TotemBar.ResetPosition() end
    end)
    reset:SetWidth(w)
    reset:SetPoint("TOPLEFT", f, "TOPLEFT", x, yStart)
    AddTooltip(reset, "Moves the bar back to the center of the screen.")

    local macro = CreateButton(f, "TotemBarOptMacroButton", "Create 'Totems' macro", function()
        ApplyTotemsMacro()
    end)
    macro:SetWidth(w)
    macro:SetPoint("TOPLEFT", f, "TOPLEFT", x, yStart - 28)
    AddTooltip(macro, "Creates or updates a 'Totems' macro that drops all four chosen totems in one press. Drag it from the macro window to your action bar.")

    local bind = CreateButton(f, "TotemBarOptBindButton", "Key bind mode", function()
        if TotemBar.ToggleBindMode then TotemBar.ToggleBindMode() end
    end)
    bind:SetWidth(w)
    bind:SetPoint("TOPLEFT", f, "TOPLEFT", x, yStart - 56)
    AddTooltip(bind, "Toggle bind mode, then hover any bar button or a flyout totem and press a key to bind it. ESC clears. Bindings are saved automatically.")
end

-- Show/hide the options panel (builds it lazily). Left-click on the minimap
-- button and /tb options both call this.
function TotemBar.ToggleOptions()
    local f = BuildOptionsFrame()
    if f:IsShown() then
        f:Hide()
    else
        f:Show()
    end
end
