-- Left_Right_Helper.lua (Left Right Helper) - Classic-safe
-- Quick Options window opens automatically in Drag Mode (small, movable, left by default).
-- WoW Settings panel stays minimal: controls + "Start Drag Mode" + "Reset (with confirmation)".

local addonName, LRH = ...
local L = (LRH and LRH.L) or {}

-------------------------------------------------
-- Strings (fallbacks)
-------------------------------------------------
L.ADDON_NAME       = L.ADDON_NAME       or "Left Right Helper"
L.DRAG_ON          = L.DRAG_ON          or "Drag mode enabled"
L.DRAG_OFF         = L.DRAG_OFF         or "Drag mode disabled"
L.SHOWN            = L.SHOWN            or "shown"
L.HIDDEN           = L.HIDDEN           or "hidden"
L.MINIMAP_LEFT     = L.MINIMAP_LEFT     or "Left-click: Toggle drag mode"
L.MINIMAP_RIGHT    = L.MINIMAP_RIGHT    or "Right-click: Show / Hide"

L.OPTIONS_TITLE    = L.OPTIONS_TITLE    or "Left Right Helper"
L.OPTIONS_DESC     = L.OPTIONS_DESC     or
    "Controls:\n" ..
    "  Minimap Left-click: Toggle Drag Mode\n" ..
    "  Minimap Right-click: Show / Hide (saved)\n" ..
    "  Drag Mode: Drag to move, Mousewheel to resize, ESC to exit\n\n" ..
    "Tip: Use the button below to start Drag Mode."

L.START_DRAG       = L.START_DRAG       or "Start Drag Mode"
L.CLOSE            = L.CLOSE            or "Close"

L.RESET_BUTTON     = L.RESET_BUTTON     or "Reset to defaults"
L.RESET_DONE       = L.RESET_DONE       or "Defaults restored"

L.TRANSPARENCY     = L.TRANSPARENCY     or "Transparency"
L.ARROW_SPACING    = L.ARROW_SPACING    or "Arrow spacing"
L.ARROW_DESIGN     = L.ARROW_DESIGN     or "Arrow design"
L.LEFT_COLOR       = L.LEFT_COLOR       or "Left fill color"
L.RIGHT_COLOR      = L.RIGHT_COLOR      or "Right fill color"
L.BORDER_COLOR     = L.BORDER_COLOR     or "Border color"
L.BORDER_GOLD      = L.BORDER_GOLD      or "Gold"
L.BORDER_SILVER    = L.BORDER_SILVER    or "Silver"
L.BORDER_BROWN      = L.BORDER_BROWN      or "Gray"

L.FONT             = L.FONT             or "Font"
L.FONT_OUTLINE     = L.FONT_OUTLINE     or "Font outline"
L.OUTLINE_NONE     = L.OUTLINE_NONE     or "None"
L.OUTLINE_NORMAL   = L.OUTLINE_NORMAL   or "Outline"
L.OUTLINE_THICK    = L.OUTLINE_THICK    or "Thick outline"

L.H_CONTROLS       = L.H_CONTROLS       or "Controls"
L.H_ARROWS         = L.H_ARROWS         or "Arrows"
L.H_COLORS         = L.H_COLORS         or "Colors"
L.H_ALPHA          = L.H_ALPHA          or "Transparency"
L.H_TEXT           = L.H_TEXT           or "Text"

L.RESET_CONFIRM_TEXT  = L.RESET_CONFIRM_TEXT  or "Do you really want to reset Left Right Helper to defaults?"
L.RESET_CONFIRM_YES   = L.RESET_CONFIRM_YES   or "Reset"
L.RESET_CONFIRM_NO    = L.RESET_CONFIRM_NO    or "Cancel"

-------------------------------------------------
-- SavedVariables
-------------------------------------------------
LeftRightDB = LeftRightDB or {}
LeftRightDB.enabled = (LeftRightDB.enabled ~= false)
LeftRightDB.dragEnabled = (LeftRightDB.dragEnabled == true)

LeftRightConfig = LeftRightConfig or {}
LeftRightConfig.xOffsetPx   = LeftRightConfig.xOffsetPx
LeftRightConfig.yOffsetPx   = LeftRightConfig.yOffsetPx
LeftRightConfig.sizeMul     = LeftRightConfig.sizeMul or 1
LeftRightConfig.alpha       = LeftRightConfig.alpha or 1
LeftRightConfig.arrowGapPx  = LeftRightConfig.arrowGapPx or 700
LeftRightConfig.arrowDesign = LeftRightConfig.arrowDesign or "CLASSIC"
LeftRightConfig.borderStyle = LeftRightConfig.borderStyle or "GOLD"
LeftRightConfig.fontKey     = LeftRightConfig.fontKey     or "STANDARD"
LeftRightConfig.fontOutline = LeftRightConfig.fontOutline or "OUTLINE"

local function EnsureColorTable(t, dr, dg, db)
    if type(t) ~= "table" then return { r = dr, g = dg, b = db } end
    if type(t.r) ~= "number" or type(t.g) ~= "number" or type(t.b) ~= "number" then
        return { r = dr, g = dg, b = db }
    end
    return t
end
LeftRightConfig.leftColor  = EnsureColorTable(LeftRightConfig.leftColor,  0.20, 0.60, 1.00)
LeftRightConfig.rightColor = EnsureColorTable(LeftRightConfig.rightColor, 1.00, 0.20, 0.20)

-------------------------------------------------
-- Constants / Paths
-------------------------------------------------
local ADDON_PATH  = "Interface\\AddOns\\Left_Right_Helper\\"
local TEX_MINIMAP = ADDON_PATH .. "Images\\Left Right Helper.blp"

local BASE_W, BASE_H = 120, 120

local SNAP_PX = 16
local TOP_MARGIN_PX = 20

local MIN_MUL, MAX_MUL = 0.2, 3.0
local MUL_STEP = 0.05

local MIN_GAP, MAX_GAP = 0, 700
local GAP_STEP = 1

local BOX_CENTER_Y_FACTOR = 0.25

local BASE_FONT = 26
local MIN_FONT  = 12
local MAX_FONT  = 64

local BORDER_COLORS = {
    GOLD   = { r = 1.00, g = 0.82, b = 0.20 },
    SILVER = { r = 0.80, g = 0.80, b = 0.85 },
    BROWN   = { r = 0.55, g = 0.40, b = 0.25 },
}

local FONT_PRESETS = {
    STANDARD  = { name = "Standard",  path = STANDARD_TEXT_FONT },
    UNITNAME  = { name = "Unit Name", path = UNIT_NAME_FONT },
    DAMAGE    = { name = "Damage",    path = DAMAGE_TEXT_FONT },
    NAMEPLATE = { name = "Nameplate", path = NAMEPLATE_FONT },
}
if type(CHAT_FONT_NORMAL) == "string" then
    FONT_PRESETS.-- CHAT = { name = "Chat", path = CHAT_FONT_NORMAL }
end



-- Expose presets so optional generated font lists can extend them.
LRH.FONT_PRESETS = FONT_PRESETS
if type(LRH._pendingFonts) == "table" then
    for k, v in pairs(LRH._pendingFonts) do
        if type(v) == "table" and type(v.path) == "string" and type(v.name) == "string" then
            FONT_PRESETS[k] = { name = v.name, path = v.path }
        end
    end
endlocal OUTLINE_PRESETS = {
    NONE = { name = L.OUTLINE_NONE,  flag = "" },
    OUTLINE = { name = L.OUTLINE_NORMAL, flag = "OUTLINE" },
    THICKOUTLINE = { name = L.OUTLINE_THICK, flag = "THICKOUTLINE" },
}

local DESIGNS = {
    CLASSIC = {
        name = "Classic",
        leftFill    = ADDON_PATH .. "Images\\ArrowLeftClassicFill.blp",
        leftBorder  = ADDON_PATH .. "Images\\ArrowLeftClassicBorder.blp",
        rightFill   = ADDON_PATH .. "Images\\ArrowRightClassicFill.blp",
        rightBorder = ADDON_PATH .. "Images\\ArrowRightClassicBorder.blp",
    },
    MODERN = {
        name = "Modern",
        leftFill    = ADDON_PATH .. "Images\\ArrowLeftModernFill.blp",
        leftBorder  = ADDON_PATH .. "Images\\ArrowLeftModernBorder.blp",
        rightFill   = ADDON_PATH .. "Images\\ArrowRightModernFill.blp",
        rightBorder = ADDON_PATH .. "Images\\ArrowRightModernBorder.blp",
    },
    SIMPLE = {
        name = "Simple",
        leftFill    = ADDON_PATH .. "Images\\ArrowLeftSimpleFill.blp",
        leftBorder  = ADDON_PATH .. "Images\\ArrowLeftSimpleBorder.blp",
        rightFill   = ADDON_PATH .. "Images\\ArrowRightSimpleFill.blp",
        rightBorder = ADDON_PATH .. "Images\\ArrowRightSimpleBorder.blp",
    },
    SHARP = {
        name = "Sharp",
        leftFill    = ADDON_PATH .. "Images\\ArrowLeftSharpFill.blp",
        leftBorder  = ADDON_PATH .. "Images\\ArrowLeftSharpBorder.blp",
        rightFill   = ADDON_PATH .. "Images\\ArrowRightSharpFill.blp",
        rightBorder = ADDON_PATH .. "Images\\ArrowRightSharpBorder.blp",
    },
}

local function GetDesignKey()
    local k = LeftRightConfig.arrowDesign
    if k and DESIGNS[k] then return k end
    return "CLASSIC"
end

-------------------------------------------------
-- Helpers
-------------------------------------------------
local function Clamp(v, lo, hi)
    if v < lo then return lo end
    if v > hi then return hi end
    return v
end

local function SnapPx(v) return math.floor(v / SNAP_PX + 0.5) * SNAP_PX end
local function PxToUI(px) return px / (UIParent:GetEffectiveScale() or 1) end

local function GetUIParentCenterPx()
    local l, b, w, h = UIParent:GetRect()
    if not l then return end
    local s = UIParent:GetEffectiveScale() or 1
    return (l + w/2) * s, (b + h/2) * s, w * s, h * s
end

local function GetScaledSize()
    local mul = Clamp(LeftRightConfig.sizeMul or 1, MIN_MUL, MAX_MUL)
    LeftRightConfig.sizeMul = mul
    return BASE_W * mul, BASE_H * mul
end

local function TrySetTexture(tex, path)
    local ok = tex:SetTexture(path)
    return ok ~= false
end

-------------------------------------------------
-- Main Frame (Arrows)
-------------------------------------------------
local frame = CreateFrame("Frame", "LeftRightFrame", UIParent, "BackdropTemplate")
frame:SetMovable(true)
frame:SetFrameStrata("MEDIUM")
frame:SetFrameLevel(10)
frame:SetClampedToScreen(true)
frame:EnableMouse(false)
frame:EnableMouseWheel(false)

local leftFill    = frame:CreateTexture(nil, "ARTWORK")
local leftBorder  = frame:CreateTexture(nil, "OVERLAY")
local rightFill   = frame:CreateTexture(nil, "ARTWORK")
local rightBorder = frame:CreateTexture(nil, "OVERLAY")

local leftLabel  = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
local rightLabel = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
leftLabel:SetJustifyH("CENTER");  leftLabel:SetJustifyV("MIDDLE")
rightLabel:SetJustifyH("CENTER"); rightLabel:SetJustifyV("MIDDLE")

-------------------------------------------------
-- Apply functions
-------------------------------------------------
local function ApplyAlpha()
    local a = Clamp(LeftRightConfig.alpha or 1, 0, 1)
    LeftRightConfig.alpha = a
    frame:SetAlpha(a)
end

local function ApplySizeAndLayout()
    local w, h = GetScaledSize()
    local gap = Clamp(LeftRightConfig.arrowGapPx or 700, MIN_GAP, MAX_GAP)
    LeftRightConfig.arrowGapPx = gap

    frame:SetSize((2*w) + gap, h)

    leftFill:ClearAllPoints()
    leftFill:SetPoint("LEFT", frame, "LEFT", 0, 0)
    leftFill:SetSize(w, h)
    leftBorder:ClearAllPoints()
    leftBorder:SetAllPoints(leftFill)

    rightFill:ClearAllPoints()
    rightFill:SetPoint("RIGHT", frame, "RIGHT", 0, 0)
    rightFill:SetSize(w, h)
    rightBorder:ClearAllPoints()
    rightBorder:SetAllPoints(rightFill)

    leftLabel:ClearAllPoints()
    leftLabel:SetPoint("CENTER", leftFill, "BOTTOM", 0, h * BOX_CENTER_Y_FACTOR)
    rightLabel:ClearAllPoints()
    rightLabel:SetPoint("CENTER", rightFill, "BOTTOM", 0, h * BOX_CENTER_Y_FACTOR)
end

local function ApplyDesign()
    local key = GetDesignKey()
    local d = DESIGNS[key] or DESIGNS.CLASSIC

    local okL1 = TrySetTexture(leftFill, d.leftFill)
    local okL2 = TrySetTexture(leftBorder, d.leftBorder)
    local okR1 = TrySetTexture(rightFill, d.rightFill)
    local okR2 = TrySetTexture(rightBorder, d.rightBorder)

    if (not okL1) or (not okL2) or (not okR1) or (not okR2) then
        local c = DESIGNS.CLASSIC
        leftFill:SetTexture(c.leftFill)
        leftBorder:SetTexture(c.leftBorder)
        rightFill:SetTexture(c.rightFill)
        rightBorder:SetTexture(c.rightBorder)
        LeftRightConfig.arrowDesign = "CLASSIC"
    end
end

local function ApplyFillTint()
    local lc = EnsureColorTable(LeftRightConfig.leftColor,  0.20, 0.60, 1.00)
    local rc = EnsureColorTable(LeftRightConfig.rightColor, 1.00, 0.20, 0.20)
    LeftRightConfig.leftColor, LeftRightConfig.rightColor = lc, rc
    leftFill:SetVertexColor(lc.r, lc.g, lc.b, 1)
    rightFill:SetVertexColor(rc.r, rc.g, rc.b, 1)
end

local function ApplyBorderTint()
    local style = LeftRightConfig.borderStyle
    if not BORDER_COLORS[style] then style = "GOLD" end
    LeftRightConfig.borderStyle = style
    local c = BORDER_COLORS[style]
    leftBorder:SetVertexColor(c.r, c.g, c.b, 1)
    rightBorder:SetVertexColor(c.r, c.g, c.b, 1)
end

local function ApplyFont()
    local preset = FONT_PRESETS[LeftRightConfig.fontKey] or FONT_PRESETS.STANDARD
    local mul = Clamp(LeftRightConfig.sizeMul or 1, MIN_MUL, MAX_MUL)

    local size = math.floor(BASE_FONT * mul + 0.5)
    size = Clamp(size, MIN_FONT, MAX_FONT)

    local o = OUTLINE_PRESETS[LeftRightConfig.fontOutline] or OUTLINE_PRESETS.OUTLINE
    local flag = o.flag or ""

    leftLabel:SetFont(preset.path, size, flag)
    rightLabel:SetFont(preset.path, size, flag)

    leftLabel:SetShadowOffset(1, -1)
    rightLabel:SetShadowOffset(1, -1)
    leftLabel:SetShadowColor(0, 0, 0, 0.6)
    rightLabel:SetShadowColor(0, 0, 0, 0.6)

    leftLabel:SetTextColor(1, 1, 1, 1)
    rightLabel:SetTextColor(1, 1, 1, 1)

    leftLabel:SetText("L")
    rightLabel:SetText("R")
end

-------------------------------------------------
-- Position
-------------------------------------------------
local function GetFrameCenterPx()
    local l, b = frame:GetLeft(), frame:GetBottom()
    if not l then return end
    local s = frame:GetEffectiveScale() or 1
    local w, h = frame:GetWidth() * s, frame:GetHeight() * s
    return (l * s) + w/2, (b * s) + h/2, w, h
end

local function ClampOffsetPx(xPx, yPx)
    local _, _, pw, ph = GetUIParentCenterPx()
    local _, _, fw, fh = GetFrameCenterPx()
    if not pw or not fw then return xPx, yPx end

    local minX = -pw/2 + fw/2
    local maxX =  pw/2 - fw/2
    local minY = -ph/2 + fh/2
    local maxY =  ph/2 - fh/2

    return Clamp(xPx, minX, maxX), Clamp(yPx, minY, maxY)
end

local function EnsureDefaultPosition()
    if LeftRightConfig.xOffsetPx ~= nil and LeftRightConfig.yOffsetPx ~= nil then return end

    local _, _, _, ph = GetUIParentCenterPx()
    local _, _, _, fh = GetFrameCenterPx()
    if not ph or not fh then
        LeftRightConfig.xOffsetPx, LeftRightConfig.yOffsetPx = 0, 0
        return
    end

    LeftRightConfig.xOffsetPx = 0
    LeftRightConfig.yOffsetPx = ph/2 - fh/2 - TOP_MARGIN_PX
end

local function ApplyPosition()
    EnsureDefaultPosition()
    local x = LeftRightConfig.xOffsetPx or 0
    local y = LeftRightConfig.yOffsetPx or 0
    x, y = ClampOffsetPx(x, y)

    frame:ClearAllPoints()
    frame:SetPoint("CENTER", UIParent, "CENTER", PxToUI(x), PxToUI(y))
end

-------------------------------------------------
-- Visibility
-------------------------------------------------
local function UpdateVisibility()
    frame:SetShown(LeftRightDB.enabled)
end

-------------------------------------------------
-- Drag highlight + grid
-------------------------------------------------
local highlight = CreateFrame("Frame", nil, frame, "BackdropTemplate")
highlight:SetAllPoints(frame)
highlight:SetBackdrop({ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", edgeSize = 16 })
highlight:SetBackdropBorderColor(0, 0.44, 0.87, 1)
highlight:Hide()

local grid = CreateFrame("Frame", "LeftRightGrid", UIParent)
grid:SetFrameStrata("BACKGROUND")
grid:Hide()

local function ClearGridTextures()
    for _, r in ipairs({ grid:GetRegions() }) do
        if r and r:GetObjectType() == "Texture" then
            r:Hide()
            r:SetTexture(nil)
        end
    end
end

local function DrawGrid()
    grid:SetAllPoints(UIParent)
    local sw, sh = UIParent:GetWidth(), UIParent:GetHeight()
    ClearGridTextures()

    for x = 0, sw, SNAP_PX do
        local t = grid:CreateTexture(nil, "ARTWORK")
        t:SetColorTexture(0, 0.44, 0.87, 0.25)
        t:SetSize(1, sh)
        t:SetPoint("TOPLEFT", grid, "TOPLEFT", x, 0)
    end

    for y = 0, sh, SNAP_PX do
        local t = grid:CreateTexture(nil, "ARTWORK")
        t:SetColorTexture(0, 0.44, 0.87, 0.25)
        t:SetSize(sw, 1)
        t:SetPoint("TOPLEFT", grid, "TOPLEFT", 0, -y)
    end

    local midH = grid:CreateTexture(nil, "OVERLAY")
    midH:SetColorTexture(1, 0, 0, 0.8)
    midH:SetSize(sw, 2)
    midH:SetPoint("TOPLEFT", grid, "TOPLEFT", 0, -sh / 2)

    local midV = grid:CreateTexture(nil, "OVERLAY")
    midV:SetColorTexture(1, 0, 0, 0.8)
    midV:SetSize(2, sh)
    midV:SetPoint("TOPLEFT", grid, "TOPLEFT", sw / 2, 0)
end

local function UpdateDragVisuals()
    if LeftRightDB.dragEnabled then
        highlight:Show()
        frame:SetBackdrop({
            bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
            edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
            edgeSize = 16,
        })
        frame:SetBackdropColor(0, 0, 0, 0.35)
        DrawGrid()
        grid:Show()
    else
        highlight:Hide()
        grid:Hide()
        frame:SetBackdrop(nil)
    end
end

-------------------------------------------------
-- Reset + confirmation
-------------------------------------------------
local function ResetToDefaults()
    LeftRightConfig.xOffsetPx   = nil
    LeftRightConfig.yOffsetPx   = nil
    LeftRightConfig.sizeMul     = 1
    LeftRightConfig.alpha       = 1

    LeftRightConfig.arrowGapPx  = 700
    LeftRightConfig.arrowDesign = "CLASSIC"

    LeftRightConfig.leftColor   = { r = 0.20, g = 0.60, b = 1.00 }
    LeftRightConfig.rightColor  = { r = 1.00, g = 0.20, b = 0.20 }

    LeftRightConfig.borderStyle = "GOLD"
    LeftRightConfig.fontKey     = "STANDARD"
    LeftRightConfig.fontOutline = "OUTLINE"

    LeftRightDB.enabled = true
    LeftRightDB.dragEnabled = false

    ApplyDesign()
    ApplyFillTint()
    ApplyBorderTint()
    ApplySizeAndLayout()
    ApplyAlpha()
    ApplyFont()
    ApplyPosition()
    UpdateVisibility()
    UpdateDragVisuals()

    print("|cFF00FF00"..L.ADDON_NAME..": "..L.RESET_DONE.."|r")
end

local function ConfirmReset(onAccept)
    if StaticPopupDialogs and StaticPopup_Show then
        StaticPopupDialogs["LRH_RESET_CONFIRM"] = StaticPopupDialogs["LRH_RESET_CONFIRM"] or {
            text = L.RESET_CONFIRM_TEXT,
            button1 = L.RESET_CONFIRM_YES,
            button2 = L.RESET_CONFIRM_NO,
            timeout = 0,
            whileDead = 1,
            hideOnEscape = 1,
            preferredIndex = 3,
            OnAccept = function() if onAccept then onAccept() end end,
        }
        StaticPopup_Show("LRH_RESET_CONFIRM")
        return
    end
    if onAccept then onAccept() end
end

-------------------------------------------------
-- Quick Options Window (Classic-safe, no BasicFrameTemplate)
-------------------------------------------------
local quick
local SetDragMode -- forward declaration

local function RestoreQuickPos(f)
    local p = LeftRightConfig.quickPos
    f:ClearAllPoints()
    if type(p) == "table" and p.point and p.x and p.y then
        f:SetPoint(p.point, UIParent, p.point, p.x, p.y)
    else
        f:SetPoint("LEFT", UIParent, "LEFT", 40, 40)
    end
end

local function SaveQuickPos(f)
    local point, _, _, x, y = f:GetPoint(1)
    LeftRightConfig.quickPos = { point = point or "LEFT", x = x or 40, y = y or 40 }
end

local function CreateHeader(parent, text, anchor, yOff)
    local h = parent:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
    h:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, yOff or -14)
    h:SetText(text)
    return h
end

local function CreateSmallText(parent, text, anchor, yOff, width)
    local fs = parent:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
    fs:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, yOff or -6)
    fs:SetJustifyH("LEFT")
    fs:SetWidth(width or 360)
    fs:SetText(text)
    return fs
end

local function CreateButton(parent, text, anchor, yOff, width)
    local b = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
    b:SetSize(width or 160, 22)
    b:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, yOff or -10)
    b:SetText(text)
    return b
end

local function CreateSlider(parent, anchor, yOff, minV, maxV, step, width)
    local s = CreateFrame("Slider", nil, parent, "OptionsSliderTemplate")
    s:SetWidth(width or 260)
    s:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, yOff or -24)
    s:SetMinMaxValues(minV, maxV)
    s:SetValueStep(step)
    s:SetObeyStepOnDrag(true)
    return s
end

local function CreateDropDown(parent, name, anchor, yOff)
    local dd = CreateFrame("Frame", name, parent, "UIDropDownMenuTemplate")
    dd:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", -16, yOff or -10)
    return dd
end

local function CreateColorButton(parent, labelText, anchor, yOff)
    local btn = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
    btn:SetSize(180, 22)
    btn:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, yOff or -8)
    btn:SetText(labelText)

    btn.swatch = btn:CreateTexture(nil, "OVERLAY")
    btn.swatch:SetSize(18, 18)
    btn.swatch:SetPoint("RIGHT", btn, "RIGHT", -6, 0)
    return btn
end

local function UpdateColorSwatch(btn, color)
    if not btn or not btn.swatch then return end
    color = EnsureColorTable(color, 0.2, 0.2, 0.2)
    btn.swatch:SetColorTexture(color.r, color.g, color.b, 1)
end

local function OpenColorPicker(targetKey, btn)
    LeftRightConfig[targetKey] = EnsureColorTable(LeftRightConfig[targetKey], 1, 1, 1)
    local c = LeftRightConfig[targetKey]

    local function ApplyRGB(r,g,b)
        c.r, c.g, c.b = r,g,b
        UpdateColorSwatch(btn, c)
        ApplyFillTint()
    end

    if ColorPickerFrame and ColorPickerFrame.SetupColorPickerAndShow then
        local info = {
            r = c.r, g = c.g, b = c.b,
            hasOpacity = false,
            swatchFunc = function()
                local r,g,b = ColorPickerFrame:GetColorRGB()
                ApplyRGB(r,g,b)
            end,
            cancelFunc = function(previousValues)
                if type(previousValues) == "table" then
                    local r = previousValues.r or previousValues[1] or c.r
                    local g = previousValues.g or previousValues[2] or c.g
                    local b = previousValues.b or previousValues[3] or c.b
                    ApplyRGB(r,g,b)
                end
            end,
        }
        ColorPickerFrame:SetupColorPickerAndShow(info)
    end
end

local function BorderName(key)
    if key == "GOLD" then return L.BORDER_GOLD end
    if key == "SILVER" then return L.BORDER_SILVER end
    if key == "BROWN" then return L.BORDER_BROWN end
    return L.BORDER_SILVER
end

local function EnsureQuickWindow()
    if quick and quick._Refresh then return end
    if quick and (not quick._Refresh) then
        quick:Hide()
        quick = nil
    end

    quick = CreateFrame("Frame", "LeftRightQuickOptions", UIParent, "BackdropTemplate")
    quick:SetSize(420, 640)
    quick:SetFrameStrata("DIALOG")
    quick:SetClampedToScreen(true)
    quick:Hide()

    quick:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        edgeSize = 16,
        insets = { left = 4, right = 4, top = 4, bottom = 4 },
    })
    quick:SetBackdropColor(0, 0, 0, 0.85)
    quick:SetBackdropBorderColor(0, 0.44, 0.87, 1)

    RestoreQuickPos(quick)

    -- movable
    quick:SetMovable(true)
    quick:EnableMouse(true)
    quick:RegisterForDrag("LeftButton")
    quick:SetScript("OnDragStart", function(self) self:StartMoving() end)
    quick:SetScript("OnDragStop", function(self)
        self:StopMovingOrSizing()
        SaveQuickPos(self)
    end)

    -- Title bar
    local titleBar = CreateFrame("Frame", nil, quick, "BackdropTemplate")
    titleBar:SetPoint("TOPLEFT", quick, "TOPLEFT", 4, -4)
    titleBar:SetPoint("TOPRIGHT", quick, "TOPRIGHT", -4, -4)
    titleBar:SetHeight(28)
    titleBar:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Header",
        edgeFile = nil,
        tile = false,
    })
    titleBar:EnableMouse(true)
    titleBar:RegisterForDrag("LeftButton")
    titleBar:SetScript("OnDragStart", function() quick:StartMoving() end)
    titleBar:SetScript("OnDragStop", function()
        quick:StopMovingOrSizing()
        SaveQuickPos(quick)
    end)

    local titleText = titleBar:CreateFontString(nil, "ARTWORK", "GameFontNormal")
    titleText:SetPoint("CENTER", titleBar, "CENTER", 0, 0)
    titleText:SetText(L.ADDON_NAME .. " - Quick Options")

    local closeX = CreateFrame("Button", nil, titleBar, "UIPanelCloseButton")
    closeX:SetPoint("RIGHT", titleBar, "RIGHT", 2, 0)

    local contentAnchor = titleBar

    quick:SetScript("OnHide", function()
        if LeftRightDB.dragEnabled and SetDragMode then
            SetDragMode(false)
        end
    end)

    -- Controls
    local hControls = CreateHeader(quick, L.H_CONTROLS, contentAnchor, -12)
    local tControls = CreateSmallText(quick,
        "• Drag: move\n• Mousewheel: resize\n• ESC: exit drag mode\n• Minimap: Left=Drag, Right=Show/Hide",
        hControls, -6, 380)
    local btnExit = CreateButton(quick, "Exit Drag Mode", tControls, -12, 180)

    -- Arrows
    local hArrows = CreateHeader(quick, L.H_ARROWS, btnExit, -18)
    local ddDesign = CreateDropDown(quick, "LRH_Q_DesignDD", hArrows, -6)
    local lblDesign = quick:CreateFontString(nil, "ARTWORK", "GameFontNormal")
    lblDesign:SetPoint("BOTTOMLEFT", ddDesign, "TOPLEFT", 20, 4)
    lblDesign:SetText(L.ARROW_DESIGN)

    local sGap = CreateSlider(quick, ddDesign, -30, MIN_GAP, MAX_GAP, GAP_STEP, 300)
    sGap.Low:SetText(tostring(MIN_GAP))
    sGap.High:SetText(tostring(MAX_GAP))

    -- Colors
    local hColors = CreateHeader(quick, L.H_COLORS, sGap, -18)
    local btnLeftColor  = CreateColorButton(quick, L.LEFT_COLOR, hColors, -10)
    local btnRightColor = CreateColorButton(quick, L.RIGHT_COLOR, btnLeftColor, -8)

    local ddBorder = CreateDropDown(quick, "LRH_Q_BorderDD", btnRightColor, -18)
    local lblBorder = quick:CreateFontString(nil, "ARTWORK", "GameFontNormal")
    lblBorder:SetPoint("BOTTOMLEFT", ddBorder, "TOPLEFT", 20, 4)
    lblBorder:SetText(L.BORDER_COLOR)

    -- Alpha
    local hAlpha = CreateHeader(quick, L.H_ALPHA, ddBorder, -22)
    local sAlpha = CreateSlider(quick, hAlpha, -18, 0, 1, 0.01, 300)
    sAlpha.Low:SetText("0%")
    sAlpha.High:SetText("100%")

    -- Text
    local hText = CreateHeader(quick, L.H_TEXT, sAlpha, -18)

    local ddFont = CreateDropDown(quick, "LRH_Q_FontDD", hText, -6)
    local lblFont = quick:CreateFontString(nil, "ARTWORK", "GameFontNormal")
    lblFont:SetPoint("BOTTOMLEFT", ddFont, "TOPLEFT", 20, 4)
    lblFont:SetText(L.FONT)

    local ddOutline = CreateDropDown(quick, "LRH_Q_OutlineDD", ddFont, -18)
    local lblOutline = quick:CreateFontString(nil, "ARTWORK", "GameFontNormal")
    lblOutline:SetPoint("BOTTOMLEFT", ddOutline, "TOPLEFT", 20, 4)
    lblOutline:SetText(L.FONT_OUTLINE)

    -- Close button inside
    local btnClose = CreateFrame("Button", nil, quick, "UIPanelButtonTemplate")
    btnClose:SetSize(360, 22)
    btnClose:SetPoint("BOTTOM", quick, "BOTTOM", 0, 16)
    btnClose:SetText(L.CLOSE)

    -------------------------------------------------
    -- Behaviour
    -------------------------------------------------
    local function RefreshQuickUI()
        local dk = GetDesignKey()
        UIDropDownMenu_SetText(ddDesign, (DESIGNS[dk] and DESIGNS[dk].name) or "Classic")

        local g = Clamp(LeftRightConfig.arrowGapPx or 700, MIN_GAP, MAX_GAP)
        sGap:SetValue(g)
        sGap.Text:SetText(L.ARROW_SPACING .. " (" .. math.floor(g) .. " px)")

        UpdateColorSwatch(btnLeftColor, LeftRightConfig.leftColor)
        UpdateColorSwatch(btnRightColor, LeftRightConfig.rightColor)

        local bs = LeftRightConfig.borderStyle
        if not BORDER_COLORS[bs] then bs = "GOLD" end
        UIDropDownMenu_SetText(ddBorder, BorderName(bs))

        local a = Clamp(LeftRightConfig.alpha or 1, 0, 1)
        sAlpha:SetValue(a)
        sAlpha.Text:SetText(L.TRANSPARENCY .. " (" .. math.floor(a*100) .. "%)")

        local fk = LeftRightConfig.fontKey
        if not FONT_PRESETS[fk] then fk = "STANDARD" end
        UIDropDownMenu_SetText(ddFont, FONT_PRESETS[fk].name)

        local ok = LeftRightConfig.fontOutline
        if not OUTLINE_PRESETS[ok] then ok = "OUTLINE" end
        UIDropDownMenu_SetText(ddOutline, OUTLINE_PRESETS[ok].name)
    end

    local function SetDesign(key)
        if not DESIGNS[key] then key = "CLASSIC" end
        LeftRightConfig.arrowDesign = key
        ApplyDesign()
        ApplyFillTint()
        ApplyBorderTint()
        RefreshQuickUI()
    end

    UIDropDownMenu_Initialize(ddDesign, function(_, level)
        for _, key in ipairs({ "CLASSIC", "MODERN", "SIMPLE", "SHARP" }) do
            local d = DESIGNS[key]
            local info = UIDropDownMenu_CreateInfo()
            info.text = d.name
            info.func = function() SetDesign(key) end
            info.checked = (GetDesignKey() == key)
            UIDropDownMenu_AddButton(info, level)
        end
    end)

    local function SetBorderStyle(key)
        if not BORDER_COLORS[key] then key = "SILVER" end
        LeftRightConfig.borderStyle = key
        ApplyBorderTint()
        RefreshQuickUI()
    end

    UIDropDownMenu_Initialize(ddBorder, function(_, level)
        for _, key in ipairs({ "GOLD", "SILVER", "BROWN" }) do
            local info = UIDropDownMenu_CreateInfo()
            info.text = BorderName(key)
            info.func = function() SetBorderStyle(key) end
            info.checked = (LeftRightConfig.borderStyle == key)
            UIDropDownMenu_AddButton(info, level)
        end
    end)

    local function SetFontKey(key)
        if not FONT_PRESETS[key] then key = "STANDARD" end
        LeftRightConfig.fontKey = key
        ApplyFont()
        RefreshQuickUI()
    end

    UIDropDownMenu_Initialize(ddFont, function(_, level)
        local baseOrder = { "STANDARD", "UNITNAME", "DAMAGE", "NAMEPLATE" }
local baseSet = { STANDARD=true, UNITNAME=true, DAMAGE=true, NAMEPLATE=true, CHAT=true }
local keys = {}
for _, k in ipairs(baseOrder) do
    if FONT_PRESETS[k] then table.insert(keys, k) end
end
if FONT_PRESETS.CHAT then table.insert(keys) end

-- Add any custom/generated font presets (sorted by display name)
local customKeys = {}
for k, v in pairs(FONT_PRESETS) do
    if type(v) == "table" and v.name and v.path and not baseSet[k] then
        table.insert(customKeys, k)
    end
end
table.sort(customKeys, function(a,b)
    return tostring(FONT_PRESETS[a].name):lower() < tostring(FONT_PRESETS[b].name):lower()
end)
for _, k in ipairs(customKeys) do
    table.insert(keys, k)
end

        for _, key in ipairs(keys) do
            local info = UIDropDownMenu_CreateInfo()
            info.text = FONT_PRESETS[key].name
            info.func = function() SetFontKey(key) end
            info.checked = (LeftRightConfig.fontKey == key)
            UIDropDownMenu_AddButton(info, level)
        end
    end)

    local function SetOutlineKey(key)
        if not OUTLINE_PRESETS[key] then key = "OUTLINE" end
        LeftRightConfig.fontOutline = key
        ApplyFont()
        RefreshQuickUI()
    end

    UIDropDownMenu_Initialize(ddOutline, function(_, level)
        for _, key in ipairs({ "NONE", "OUTLINE", "THICKOUTLINE" }) do
            local info = UIDropDownMenu_CreateInfo()
            info.text = OUTLINE_PRESETS[key].name
            info.func = function() SetOutlineKey(key) end
            info.checked = (LeftRightConfig.fontOutline == key)
            UIDropDownMenu_AddButton(info, level)
        end
    end)

    sGap:SetScript("OnValueChanged", function(_, value)
        LeftRightConfig.arrowGapPx = Clamp(value, MIN_GAP, MAX_GAP)
        ApplySizeAndLayout()
        ApplyPosition()
        RefreshQuickUI()
    end)

    sAlpha:SetScript("OnValueChanged", function(_, value)
        LeftRightConfig.alpha = Clamp(value, 0, 1)
        ApplyAlpha()
        RefreshQuickUI()
    end)

    btnLeftColor:SetScript("OnClick", function() OpenColorPicker("leftColor", btnLeftColor) end)
    btnRightColor:SetScript("OnClick", function() OpenColorPicker("rightColor", btnRightColor) end)

    btnExit:SetScript("OnClick", function()
        if SetDragMode then SetDragMode(false) end
    end)

    btnClose:SetScript("OnClick", function()
        quick:Hide()
    end)

    quick._Refresh = RefreshQuickUI
end

local function ShowQuickOptions()
    EnsureQuickWindow()
    if not quick then return end
    quick:Show()
    if quick._Refresh then quick._Refresh() end
end

local function HideQuickOptions()
    if quick then quick:Hide() end
end

-------------------------------------------------
-- Drag Mode + ESC
-------------------------------------------------
local escCatcher = CreateFrame("Frame", "LeftRightEscCatcher", UIParent)
escCatcher:Hide()

UISpecialFrames = UISpecialFrames or {}
do
    local exists = false
    for _, n in ipairs(UISpecialFrames) do
        if n == "LeftRightEscCatcher" then exists = true break end
    end
    if not exists then table.insert(UISpecialFrames, "LeftRightEscCatcher") end
end

SetDragMode = function(state)
    LeftRightDB.dragEnabled = state

    if state then
        frame:EnableMouse(true)
        frame:EnableMouseWheel(true)
        frame:RegisterForDrag("LeftButton")
        escCatcher:Show()

        UpdateDragVisuals()
        ShowQuickOptions()

        print("|cFF00FF00"..L.ADDON_NAME..": "..L.DRAG_ON.."|r")
    else
        frame:EnableMouse(false)
        frame:EnableMouseWheel(false)
        frame:RegisterForDrag()
        escCatcher:Hide()

        UpdateDragVisuals()
        HideQuickOptions()

        print("|cFFFF0000"..L.ADDON_NAME..": "..L.DRAG_OFF.."|r")
    end
end

escCatcher:SetScript("OnHide", function()
    if LeftRightDB.dragEnabled then SetDragMode(false) end
end)

frame:SetScript("OnDragStart", function()
    if not LeftRightDB.dragEnabled then return end
    ApplyPosition()
    frame:StartMoving()
end)

frame:SetScript("OnDragStop", function()
    if not LeftRightDB.dragEnabled then return end
    frame:StopMovingOrSizing()

    local pcx, pcy = GetUIParentCenterPx()
    local fcx, fcy = GetFrameCenterPx()
    if not pcx or not fcx then return end

    local x = SnapPx(fcx - pcx)
    local y = SnapPx(fcy - pcy)
    x, y = ClampOffsetPx(x, y)

    LeftRightConfig.xOffsetPx = x
    LeftRightConfig.yOffsetPx = y
    ApplyPosition()

    if quick and quick:IsShown() and quick._Refresh then quick._Refresh() end
end)

frame:SetScript("OnMouseWheel", function(_, delta)
    if not LeftRightDB.dragEnabled then return end

    local mul = (LeftRightConfig.sizeMul or 1) + (delta > 0 and MUL_STEP or -MUL_STEP)
    LeftRightConfig.sizeMul = Clamp(mul, MIN_MUL, MAX_MUL)

    ApplySizeAndLayout()
    ApplyFont()
    ApplyPosition()

    if quick and quick:IsShown() and quick._Refresh then quick._Refresh() end
end)

-------------------------------------------------
-- Minimap Button
-------------------------------------------------
local mini = CreateFrame("Button", "LeftRightMinimapButton", Minimap)
mini:SetSize(31, 31)
mini:SetHighlightTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight")
mini:RegisterForClicks("LeftButtonUp", "RightButtonUp")

local miniIcon = mini:CreateTexture(nil, "OVERLAY")
miniIcon:SetAllPoints()
miniIcon:SetTexture(TEX_MINIMAP)

mini:SetPoint("CENTER", Minimap, "CENTER", 80, 0)

mini:SetScript("OnClick", function(_, btn)
    if btn == "LeftButton" then
        SetDragMode(not LeftRightDB.dragEnabled)
    elseif btn == "RightButton" then
        LeftRightDB.enabled = not LeftRightDB.enabled
        UpdateVisibility()
        print("|cFFFFFF00"..L.ADDON_NAME..": "..(LeftRightDB.enabled and L.SHOWN or L.HIDDEN).."|r")
    end
end)

mini:SetScript("OnEnter", function(self)
    GameTooltip:SetOwner(self, "ANCHOR_LEFT")
    GameTooltip:AddLine(L.ADDON_NAME, 1, 1, 0)
    GameTooltip:AddLine(L.MINIMAP_LEFT, 0.7, 0.7, 0.7)
    GameTooltip:AddLine(L.MINIMAP_RIGHT, 0.7, 0.7, 0.7)
    GameTooltip:Show()
end)
mini:SetScript("OnLeave", GameTooltip_Hide)

-------------------------------------------------
-- Minimal WoW Options panel (Classic Interface Options)
-------------------------------------------------
local options = CreateFrame("Frame")
options.name = L.ADDON_NAME

local optTitle = options:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
optTitle:SetPoint("TOPLEFT", 16, -16)
optTitle:SetText(L.OPTIONS_TITLE or L.ADDON_NAME)

local optDesc = options:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
optDesc:SetPoint("TOPLEFT", optTitle, "BOTTOMLEFT", 0, -8)
optDesc:SetWidth(520)
optDesc:SetJustifyH("LEFT")
optDesc:SetText(L.OPTIONS_DESC or "")

local btnStart = CreateFrame("Button", nil, options, "UIPanelButtonTemplate")
btnStart:SetSize(220, 24)
btnStart:SetPoint("TOPLEFT", optDesc, "BOTTOMLEFT", 0, -16)
btnStart:SetText(L.START_DRAG)
btnStart:SetScript("OnClick", function()
    SetDragMode(true)
    if InterfaceOptionsFrame and InterfaceOptionsFrame:IsShown() then
        InterfaceOptionsFrame:Hide()
    end
end)

local resetBtn = CreateFrame("Button", nil, options, "UIPanelButtonTemplate")
resetBtn:SetSize(220, 24)
resetBtn:SetPoint("TOPLEFT", btnStart, "BOTTOMLEFT", 0, -12)
resetBtn:SetText(L.RESET_BUTTON)
resetBtn:SetScript("OnClick", function()
    ConfirmReset(function()
        ResetToDefaults()
        if LeftRightDB.dragEnabled then SetDragMode(false) end
    end)
end)

if InterfaceOptions_AddCategory then
    InterfaceOptions_AddCategory(options)
end

-------------------------------------------------
-- Init / Apply
-------------------------------------------------
local function FullApply()
    ApplyDesign()
    ApplyFillTint()
    ApplyBorderTint()
    ApplySizeAndLayout()
    ApplyAlpha()
    ApplyFont()
    ApplyPosition()
    UpdateVisibility()
    UpdateDragVisuals()
end

frame:RegisterEvent("PLAYER_LOGIN")
frame:SetScript("OnEvent", function()
    FullApply()
    SetDragMode(LeftRightDB.dragEnabled)
end)

FullApply()