-- TOG Tools — Shared GUI helpers
-- Loaded right after TOGTools.lua. Defines helpers used by every tab:
--   addon.Tooltip.Owner / AnchorFrame   — smart tooltip anchoring
--   addon.AceGUIFrameScripts            — leak-safe raw frame scripts on pooled widgets
--   addon.GUI.DetachPool                — safe cleanup for raw-frame virtual-scroll pools
--   addon.GUI.AttachTooltip             — wire OnEnter/OnLeave tooltip onto any AceGUI widget
--   addon.GUI.MakeColumnHeader          — brand-colored InteractiveLabel column headers

local _, addon = ...
local AceGUI = LibStub("AceGUI-3.0")

-- ---------------------------------------------------------------------------
-- Tooltip helpers
-- ---------------------------------------------------------------------------
-- Always call these instead of raw GameTooltip:SetOwner.
-- Anchors below the frame when in the top screen half (ANCHOR_BOTTOMLEFT),
-- above when in the bottom half (ANCHOR_TOPLEFT) — never clips off screen.

addon.Tooltip = {}

function addon.Tooltip.Owner(frame)
    local _, y = frame:GetCenter()
    local anchor = (y and y > GetScreenHeight() / 2) and "ANCHOR_BOTTOMLEFT" or "ANCHOR_TOPLEFT"
    GameTooltip:SetOwner(frame, anchor)
end

-- Anchor an arbitrary popup frame adjacent to a source widget/frame using
-- the same screen-half logic.  Pass either a raw Frame or an AceGUI widget.
function addon.Tooltip.AnchorFrame(frame, source)
    local sourceFrame = source.GetCenter and source or source.frame
    if not sourceFrame or not sourceFrame.GetCenter then return end
    frame:ClearAllPoints()
    local _, y = sourceFrame:GetCenter()
    if y and y > GetScreenHeight() / 2 then
        frame:SetPoint("TOPLEFT", sourceFrame, "BOTTOMLEFT", 0, 0)
    else
        frame:SetPoint("BOTTOMLEFT", sourceFrame, "TOPLEFT", 0, 0)
    end
end

-- ---------------------------------------------------------------------------
-- Leak-safe raw frame scripts on AceGUI widgets
-- ---------------------------------------------------------------------------
-- AceGUI pools widgets across the entire account. SetCallback entries are
-- cleared on Release, but raw widget.frame:SetScript calls are NOT — the
-- script survives into the next owner's UI.  Many widget Constructors also
-- install internal dispatch scripts on widget.frame (e.g. Button's
-- Control_OnEnter), so blindly nilling them on release would break the
-- recycled widget for whoever uses it next.
--
-- This helper SAVES the prior script and RESTORES it on OnRelease instead
-- of nilling it.
--
-- Prefer widget:SetCallback("OnEnter"/"OnLeave"/"OnClick"/...) when the
-- widget supports it. Only use this for widgets without native dispatch
-- (e.g. SimpleGroup) or events the widget doesn't expose (OnMouseDown).
--
-- Usage:
--   addon.AceGUIFrameScripts(widget, { OnMouseDown = function(f, btn) ... end })
function addon.AceGUIFrameScripts(widget, scripts)
    if not (widget and widget.frame and scripts) then return end
    local saved = {}
    for evt, fn in pairs(scripts) do
        saved[evt] = widget.frame:GetScript(evt)
        widget.frame:SetScript(evt, fn)
    end
    widget:SetCallback("OnRelease", function(self)
        if not self.frame then return end
        for evt, prior in pairs(saved) do
            self.frame:SetScript(evt, prior)
        end
    end)
end

-- ---------------------------------------------------------------------------
-- GUI namespace
-- ---------------------------------------------------------------------------
addon.GUI = addon.GUI or {}

-- ---------------------------------------------------------------------------
-- Minimum resize floor
-- ---------------------------------------------------------------------------
-- Port of FGI's UI.ApplyMinResize. Clamps the window's drag-resize floor
-- using the modern SetResizeBounds API with a SetMinResize fallback for
-- legacy WoW clients. Accepts either an AceGUI widget (uses widget.frame)
-- or a raw Frame.
function addon.GUI.ApplyMinResize(frameOrWidget, minW, minH)
    local raw = (frameOrWidget and frameOrWidget.frame) or frameOrWidget
    if not raw then return end
    if raw.SetResizeBounds then
        raw:SetResizeBounds(minW or 0, minH or 0)
    elseif raw.SetMinResize then
        raw:SetMinResize(minW or 0, minH or 0)
    end
end

-- ---------------------------------------------------------------------------
-- Pool detach
-- ---------------------------------------------------------------------------
-- Tabs that maintain a pool of raw CreateFrame rows (virtual-scroll perf
-- trick) parent those frames to an AceGUI widget's content frame. When
-- AceGUI recycles that widget into another addon's UI the pool frames bleed
-- into the other addon's window.  Call this from OnRelease to safely orphan
-- them: Hide + re-parent to UIParent + ClearAllPoints.  The frames stay in
-- the pool table and are re-attached on the next draw.
--
-- Usage:
--   widget:SetCallback("OnRelease", function(self) addon.GUI.DetachPool(self._myPool) end)
function addon.GUI.DetachPool(pool)
    if not pool then return end
    for _, f in ipairs(pool) do
        if f then
            f:Hide()
            f:SetParent(UIParent)
            f:ClearAllPoints()
        end
    end
end

-- ---------------------------------------------------------------------------
-- Tooltip wiring
-- ---------------------------------------------------------------------------
-- Attach a GameTooltip OnEnter/OnLeave to any AceGUI widget.
-- Uses SetCallback for most widgets (Button, CheckBox, InteractiveLabel, etc.)
-- For Dropdown and EditBox the label area sits outside the interactive body
-- and never fires Control_OnEnter, so we also wire the wrapper frame via
-- AceGUIFrameScripts (which restores the prior script on Release).
function addon.GUI.AttachTooltip(widget, title, desc)
    if not widget then return end

    local function show(anchor)
        addon.Tooltip.Owner(anchor or widget.frame)
        if title then GameTooltip:SetText(title, 1, 1, 1) end
        if desc  then GameTooltip:AddLine(desc, nil, nil, nil, true) end
        GameTooltip:Show()
    end
    local function hide() GameTooltip:Hide() end

    widget:SetCallback("OnEnter", function(_w) show(_w.frame) end)
    widget:SetCallback("OnLeave", hide)

    -- Dropdown and EditBox: also wire the wrapper frame so hovering the
    -- label text area (which AceGUI doesn't route to OnEnter) shows the tip.
    if (widget.type == "Dropdown" or widget.type == "EditBox") and widget.frame then
        if widget.frame.EnableMouse then widget.frame:EnableMouse(true) end
        addon.AceGUIFrameScripts(widget, {
            OnEnter = function(f) show(f) end,
            OnLeave = hide,
        })
    end
end

-- ---------------------------------------------------------------------------
-- Column header factory
-- ---------------------------------------------------------------------------
-- Creates a brand-colored, no-wrap InteractiveLabel for use as a column
-- header above sortable tables.
--
-- Required opts: parent (AceGUI container), label (string), width (number)
-- Optional opts:
--   justifyH      — "LEFT" | "CENTER" | "RIGHT"
--   tooltipTitle  — first tooltip line
--   tooltipDesc   — tooltip body
--   onClick       — called when the header is clicked (for sort toggling)
function addon.GUI.MakeColumnHeader(opts)
    assert(opts and opts.parent and opts.label and opts.width,
           "MakeColumnHeader: parent, label and width are required")

    local lbl = AceGUI:Create("InteractiveLabel")
    lbl:SetText("|c" .. (addon.BrandColor or "ffFF8000") .. opts.label .. "|r")
    lbl:SetWidth(opts.width)

    if opts.justifyH and lbl.SetJustifyH then
        lbl:SetJustifyH(opts.justifyH)
    end

    -- Prevent wrapping — a two-line header breaks alignment with data rows.
    if lbl.label and lbl.label.SetWordWrap then
        lbl.label:SetWordWrap(false)
    end

    if opts.tooltipTitle or opts.tooltipDesc then
        addon.GUI.AttachTooltip(lbl, opts.tooltipTitle, opts.tooltipDesc)
    end

    if opts.onClick then
        lbl:SetCallback("OnClick", function() opts.onClick() end)
    end

    opts.parent:AddChild(lbl)
    return lbl
end
