-- HDG_HT_Editor.lua
-- Visual layout editor. Toggled from the picker. When ON, decorates each card
-- with drag/resize handles + gear icon. Mutates HDG_DB.houseTab.{order, width,
-- layoutOverrides} and triggers TestWindow.Render() after each change.

local _, privateDB = ...
privateDB.HDG_HT = privateDB.HDG_HT or {}
local Editor = {}
privateDB.HDG_HT.Editor = Editor

local HDG_HT = privateDB.HDG_HT
local _designMode = false
local _onChange = nil
local _grid = nil   -- guide-line overlay frame

local function GetSV()
    HDG_DB = HDG_DB or {}
    HDG_DB.houseTab = HDG_DB.houseTab or {}
    HDG_DB.houseTab.layoutOverrides = HDG_DB.houseTab.layoutOverrides or {}
    HDG_DB.houseTab.width = HDG_DB.houseTab.width or {}
    HDG_DB.houseTab.order = HDG_DB.houseTab.order or {}
    return HDG_DB.houseTab
end

local function Notify()
    if _onChange then _onChange() end
end

-- ============================================================================
-- Width snap helpers
-- ============================================================================

local function PixelsToWidth(px, grid)
    -- Snap to nearest of (third, twoThirds, full)
    local third     = (grid.totalWidth - 2 * grid.padding - 2 * grid.gutter) / 3
    local twoThirds = third * 2 + grid.gutter
    local full      = third * 3 + 2 * grid.gutter
    local diffs = {
        { name = "third",     d = math.abs(px - third) },
        { name = "twoThirds", d = math.abs(px - twoThirds) },
        { name = "full",      d = math.abs(px - full) },
    }
    table.sort(diffs, function(a, b) return a.d < b.d end)
    return diffs[1].name
end

-- ============================================================================
-- Per-card decorations
-- ============================================================================
-- Reordering lives in the Customise picker (up/down arrows). Design Mode
-- handles VISUAL tuning only: width snap (right-edge drag), height (bottom-
-- edge drag), and the gear popover for numeric dimension sliders.

local function AddResizeHandles(card, id)
    local cfg = HDG_HT.LayoutConfig

    -- Right-edge handle (width snap)
    local rh = CreateFrame("Frame", nil, card)
    rh:SetSize(8, card:GetHeight())
    rh:SetPoint("TOPRIGHT",    card, "TOPRIGHT",    0, 0)
    rh:SetPoint("BOTTOMRIGHT", card, "BOTTOMRIGHT", 0, 0)
    rh:EnableMouse(true)
    local tex = rh:CreateTexture(nil, "OVERLAY")
    tex:SetTexture("Interface\\Buttons\\WHITE8x8")
    tex:SetVertexColor(0.5, 0.7, 1, 0.6)
    tex:SetAllPoints(rh)
    rh:SetScript("OnMouseDown", function(self)
        self._dragStartX = GetCursorPosition()
        self._startWidth = card:GetWidth()
    end)
    rh:SetScript("OnMouseUp", function(self)
        if not self._dragStartX then return end
        local x = GetCursorPosition()
        local dx = (x - self._dragStartX) / self:GetEffectiveScale()
        self._dragStartX = nil
        local newPx   = self._startWidth + dx
        local snapName = PixelsToWidth(newPx, cfg.grid)
        local sv = GetSV()
        sv.width[id] = snapName
        Notify()
    end)
    card._editorRH = rh

    -- Bottom-edge handle (minHeight free, snaps to 4px)
    local bh = CreateFrame("Frame", nil, card)
    bh:SetHeight(8)
    bh:SetPoint("BOTTOMLEFT",  card, "BOTTOMLEFT",  0, 0)
    bh:SetPoint("BOTTOMRIGHT", card, "BOTTOMRIGHT", 0, 0)
    bh:EnableMouse(true)
    local tex2 = bh:CreateTexture(nil, "OVERLAY")
    tex2:SetTexture("Interface\\Buttons\\WHITE8x8")
    tex2:SetVertexColor(0.5, 1, 0.7, 0.6)
    tex2:SetAllPoints(bh)
    bh:SetScript("OnMouseDown", function(self)
        local _, y = GetCursorPosition()
        self._dragStartY = y
        self._startHeight = card:GetHeight()
    end)
    bh:SetScript("OnMouseUp", function(self)
        if not self._dragStartY then return end
        local _, y = GetCursorPosition()
        local dy = (self._dragStartY - y) / self:GetEffectiveScale()  -- down = positive
        self._dragStartY = nil
        local newH = math.max(40, math.floor((self._startHeight + dy) / 4 + 0.5) * 4)
        local sv = GetSV()
        sv.layoutOverrides[id] = sv.layoutOverrides[id] or {}
        sv.layoutOverrides[id].minHeight = newH
        Notify()
    end)
    card._editorBH = bh
end

-- ============================================================================
-- Dim popover
-- ============================================================================

local _popover = nil

local function ShowPopover(card, id)
    if _popover then _popover:Hide(); _popover:SetParent(nil) end

    local cfg  = HDG_HT.LayoutConfig
    local spec = cfg.widgets[id]
    if not spec then return end

    local pop = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
    pop:SetSize(280, 60)
    -- Anchor to absolute screen position (UIParent BOTTOMLEFT) so re-renders
    -- that destroy/recreate the source card don't displace the popover.
    local cardRight = (card:GetRight() or 0)
    local cardTop   = (card:GetTop()   or 0)
    pop:ClearAllPoints()
    pop:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", cardRight + 8, cardTop)
    pop:SetFrameStrata("TOOLTIP")
    -- Make popover draggable by its title bar so user can move it if it lands
    -- in an awkward spot
    pop:SetMovable(true)
    pop:EnableMouse(true)
    pop:RegisterForDrag("LeftButton")
    pop:SetScript("OnDragStart", pop.StartMoving)
    pop:SetScript("OnDragStop",  pop.StopMovingOrSizing)
    pop:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
        edgeSize = 12,
        insets   = { left = 4, right = 4, top = 4, bottom = 4 },
    })
    pop:SetBackdropColor(0.05, 0.05, 0.08, 0.97)

    local title = pop:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    title:SetPoint("TOPLEFT", pop, "TOPLEFT", 8, -8)
    title:SetText(id .. " - dimensions")

    -- Reset button — clears this widget's per-key overrides so all sliders
    -- snap back to the LayoutConfig spec defaults. Closes the popover (the
    -- next gear click rebuilds it with fresh slider values).
    local reset = CreateFrame("Button", nil, pop)
    reset:SetSize(60, 16)
    reset:SetPoint("TOPRIGHT", pop, "TOPRIGHT", -8, -6)
    local resetText = reset:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    resetText:SetAllPoints(reset)
    resetText:SetText("Reset")
    resetText:SetTextColor(1, 0.83, 0.30)  -- gold
    reset:SetScript("OnEnter", function()
        resetText:SetTextColor(1, 1, 0.6)
        GameTooltip:SetOwner(reset, "ANCHOR_TOP")
        GameTooltip:AddLine("Reset all dimensions to default", 1, 1, 1)
        GameTooltip:AddLine("|cFF888888Clears overrides for this widget only|r", 1, 1, 1, true)
        GameTooltip:Show()
    end)
    reset:SetScript("OnLeave", function()
        resetText:SetTextColor(1, 0.83, 0.30)
        GameTooltip:Hide()
    end)
    reset:SetScript("OnClick", function()
        local sv = GetSV()
        sv.layoutOverrides[id] = nil
        if _popover then _popover:Hide(); _popover:SetParent(nil); _popover = nil end
        Notify()
    end)

    local y = -28
    -- Collect numeric keys (sorted for stable order). Font sizes are
    -- intentionally hardcoded in widget code for visual consistency, so
    -- exposing them as sliders would be misleading — drag does nothing
    -- visible. Filter any *Font key out of the popover.
    local numKeys = {}
    for k, v in pairs(spec) do
        if type(v) == "number" and k ~= "order" and not k:match("Font$") then
            numKeys[#numKeys + 1] = k
        end
    end
    table.sort(numKeys)

    for _, k in ipairs(numKeys) do
        local v = spec[k]
        local label = pop:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        label:SetPoint("TOPLEFT", pop, "TOPLEFT", 8, y)
        label:SetText(k)

        local sv = GetSV()
        local current = (sv.layoutOverrides[id] and sv.layoutOverrides[id][k]) or v
        local valueText = pop:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        valueText:SetPoint("LEFT", pop, "LEFT", 110, 0)
        valueText:SetPoint("TOP",  label, "TOP", 0, 0)
        valueText:SetText(tostring(current))

        local slider = CreateFrame("Slider", nil, pop, "OptionsSliderTemplate")
        slider:SetSize(120, 12)
        slider:SetPoint("LEFT", pop, "LEFT", 150, 0)
        slider:SetPoint("TOP",  label, "TOP", 0, 0)
        local lo = math.max(1, math.floor(v * 0.5))
        local hi = math.max(lo + 1, math.floor(v * 3))
        slider:SetMinMaxValues(lo, hi)
        slider:SetValueStep(1)
        slider:SetValue(current)
        slider:SetObeyStepOnDrag(true)
        slider.Low:SetText(""); slider.High:SetText(""); slider.Text:SetText("")
        slider:SetScript("OnValueChanged", function(_, val)
            val = math.floor(val + 0.5)
            valueText:SetText(tostring(val))
            local sv2 = GetSV()
            sv2.layoutOverrides[id] = sv2.layoutOverrides[id] or {}
            sv2.layoutOverrides[id][k] = val
            Notify()
        end)

        y = y - 22
    end

    pop:SetHeight(math.max(60, -y + 8))
    _popover = pop
end

local function AddGearIcon(card, id)
    local g = CreateFrame("Button", nil, card)
    g:SetSize(16, 16)
    g:SetPoint("TOPRIGHT", card, "TOPRIGHT", -4, -4)
    local tex = g:CreateTexture(nil, "OVERLAY")
    tex:SetTexture("Interface\\Buttons\\UI-OptionsButton")
    tex:SetAllPoints(g)
    g:SetScript("OnClick", function() ShowPopover(card, id) end)
    card._editorGear = g
end

-- ============================================================================
-- Public API
-- ============================================================================

function Editor.Decorate(card, id)
    if not _designMode then return end
    AddResizeHandles(card, id)
    AddGearIcon(card, id)
end

function Editor.Undecorate(card)
    if card._editorRH   then card._editorRH:Hide();   card._editorRH   = nil end
    if card._editorBH   then card._editorBH:Hide();   card._editorBH   = nil end
    if card._editorGear then card._editorGear:Hide(); card._editorGear = nil end
end

function Editor.SetActive(active, host, onChange)
    _designMode = active
    _onChange   = onChange
    if active then
        -- One-time help tip so the user knows what to grab
        print("|cFFffd34d[HDG cockpit]|r Design Mode |cFF00FF00ON|r")
        print("  |cFFAAAAAA*|r Drag a card's |cFF8FB4FFright edge|r to change width (snaps to 1/3, 2/3, full)")
        print("  |cFFAAAAAA*|r Drag a card's |cFF8FFFB4bottom edge|r to change height")
        print("  |cFFAAAAAA*|r Click the |cFFffd34dgear|r icon for dimension sliders")
        print("  |cFFAAAAAA*|r To reorder cards, use the up/down arrows in |cFFffd34dCustomise|r")
        if not _grid then
            _grid = CreateFrame("Frame", nil, host)
            _grid:SetAllPoints(host)
            local cfg   = HDG_HT.LayoutConfig
            local third = (cfg.grid.totalWidth - 2 * cfg.grid.padding - 2 * cfg.grid.gutter) / 3
            for col = 1, 2 do
                local x = cfg.grid.padding + col * third + (col - 1) * cfg.grid.gutter + cfg.grid.gutter / 2
                local line = _grid:CreateTexture(nil, "BACKGROUND")
                line:SetTexture("Interface\\Buttons\\WHITE8x8")
                line:SetVertexColor(1, 1, 0, 0.10)
                line:SetSize(1, host:GetHeight())
                line:SetPoint("TOPLEFT", host, "TOPLEFT", x, -28)
            end
        end
        _grid:Show()
    else
        if _grid then _grid:Hide() end
        if _popover then _popover:Hide(); _popover:SetParent(nil); _popover = nil end
    end
end

function Editor.IsActive() return _designMode end
