-- Options.lua
-- Builds the panel shown under Game Menu -> Options -> AddOns -> Where's me mouse?

local WMM = WheresMeMouse

local panel = CreateFrame("Frame")
panel.name = "Where's me mouse?"

-- All the actual content lives inside a scroll frame so it can never spill
-- outside the Settings window, no matter how many options we add later.
local scrollFrame = CreateFrame("ScrollFrame", "WheresMeMouseOptionsScroll", panel, "UIPanelScrollFrameTemplate")
scrollFrame:SetPoint("TOPLEFT", panel, "TOPLEFT", 0, -4)
scrollFrame:SetPoint("BOTTOMRIGHT", panel, "BOTTOMRIGHT", -26, 4)

local content = CreateFrame("Frame", nil, scrollFrame)
content:SetSize(1, 1) -- resized once we know the scroll frame's width
scrollFrame:SetScrollChild(content)

scrollFrame:SetScript("OnSizeChanged", function(self, width, height)
    content:SetWidth(width)
end)

local function CreateHeading(parent, text, anchorTo, yOffset)
    local fs = parent:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
    fs:SetPoint("TOPLEFT", anchorTo, "BOTTOMLEFT", 0, yOffset or -18)
    fs:SetText(text)
    return fs
end

local function CreateCheckbox(parent, label, tooltip, anchorTo, yOffset, getFn, setFn)
    local cb = CreateFrame("CheckButton", nil, parent, "UICheckButtonTemplate")
    cb:SetPoint("TOPLEFT", anchorTo, "BOTTOMLEFT", 0, yOffset or -12)
    cb.Text:SetText(label)
    cb.tooltip = tooltip
    cb:SetScript("OnEnter", function(self)
        if not self.tooltip then return end
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        GameTooltip:SetText(self.tooltip, nil, nil, nil, nil, true)
        GameTooltip:Show()
    end)
    cb:SetScript("OnLeave", GameTooltip_Hide)
    cb:SetScript("OnClick", function(self)
        setFn(self:GetChecked() and true or false)
        WMM.ApplyAppearance()
        WMM.UpdateVisibility()
    end)
    cb.refresh = function()
        cb:SetChecked(getFn())
    end
    return cb
end

local function CreateSlider(parent, label, tooltip, anchorTo, yOffset, minVal, maxVal, step, getFn, setFn, formatFn)
    local slider = CreateFrame("Slider", nil, parent, "OptionsSliderTemplate")
    slider:SetPoint("TOPLEFT", anchorTo, "BOTTOMLEFT", 12, yOffset or -30)
    slider:SetWidth(260)
    slider:SetMinMaxValues(minVal, maxVal)
    slider:SetValueStep(step)
    slider:SetObeyStepOnDrag(true)

    slider.Text:SetText(label)
    slider.Low:SetText(tostring(minVal))
    slider.High:SetText(tostring(maxVal))

    slider.valueText = slider:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
    slider.valueText:SetPoint("TOP", slider, "BOTTOM", 0, 1)

    slider.tooltip = tooltip
    slider:SetScript("OnEnter", function(self)
        if not self.tooltip then return end
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        GameTooltip:SetText(self.tooltip, nil, nil, nil, nil, true)
        GameTooltip:Show()
    end)
    slider:SetScript("OnLeave", GameTooltip_Hide)

    slider:SetScript("OnValueChanged", function(self, value)
        value = math.floor(value + 0.5)
        setFn(value)
        self.valueText:SetText(formatFn and formatFn(value) or tostring(value))
        WMM.ApplyAppearance()
    end)

    slider.refresh = function()
        local v = getFn()
        slider:SetValue(v)
        slider.valueText:SetText(formatFn and formatFn(v) or tostring(v))
    end

    return slider
end

local function CreateShapePicker(parent, anchorTo, yOffset, getFn, setFn)
    local row = CreateFrame("Frame", nil, parent)
    row:SetSize(400, 46)
    row:SetPoint("TOPLEFT", anchorTo, "BOTTOMLEFT", 0, yOffset or -14)

    local buttons = {}
    local size = 42
    local gap = 14

    for i, shape in ipairs(WMM.SHAPE_LIST) do
        local btn = CreateFrame("Button", nil, row)
        btn:SetSize(size, size)
        btn:SetPoint("LEFT", row, "LEFT", (i - 1) * (size + gap), 0)

        local bg = btn:CreateTexture(nil, "BACKGROUND")
        bg:SetAllPoints()
        bg:SetColorTexture(0, 0, 0, 0.35)

        local icon = btn:CreateTexture(nil, "ARTWORK")
        icon:SetPoint("CENTER")
        icon:SetSize(size - 6, size - 6)
        -- Use a bolder preset than the thinnest one for the button's own
        -- preview art -- a 5%-stroke outline reads fine on the big ring
        -- but is too thin to read clearly at 32px, especially on straight-
        -- edged shapes where the edges nearly vanish and only the corners
        -- stay visible. This is purely cosmetic for the picker; the actual
        -- ring still uses whatever preset the Thickness slider calls for.
        icon:SetTexture(WMM.SHAPES[shape.key][4])
        icon:SetVertexColor(1, 0.85, 0.4, 1)

        local hl = btn:CreateTexture(nil, "HIGHLIGHT")
        hl:SetAllPoints()
        hl:SetColorTexture(1, 1, 1, 0.15)

        local selected = CreateFrame("Frame", nil, btn, "BackdropTemplate")
        selected:SetAllPoints()
        selected:SetBackdrop({ edgeFile = "Interface\\Buttons\\WHITE8x8", edgeSize = 2 })
        selected:SetBackdropBorderColor(1, 0.82, 0, 1)
        selected:Hide()
        btn.selected = selected

        btn:SetScript("OnClick", function()
            setFn(shape.key)
            WMM.ApplyAppearance()
            for _, b in ipairs(buttons) do b.selected:Hide() end
            selected:Show()
        end)

        btn:SetScript("OnEnter", function(self)
            GameTooltip:SetOwner(self, "ANCHOR_TOP")
            GameTooltip:SetText(shape.label)
            GameTooltip:Show()
        end)
        btn:SetScript("OnLeave", GameTooltip_Hide)

        buttons[i] = btn
    end

    row.refresh = function()
        local cur = getFn()
        for i, shape in ipairs(WMM.SHAPE_LIST) do
            if shape.key == cur then
                buttons[i].selected:Show()
            else
                buttons[i].selected:Hide()
            end
        end
    end

    return row
end

--------------------------------------------------------------------
-- Build the panel once
--------------------------------------------------------------------

local widgets = {}
local built = false

local function BuildPanel()
    if built then return end
    built = true

    local icon = content:CreateTexture(nil, "ARTWORK")
    icon:SetSize(128, 128)
    icon:SetPoint("TOPLEFT", 16, -16)
    icon:SetTexture("Interface\\AddOns\\WheresMeMouse\\Media\\icon.png")

    local title = content:CreateFontString(nil, "ARTWORK", "GameFontNormalHuge")
    title:SetPoint("LEFT", icon, "RIGHT", 18, 24)
    title:SetText("Where's me mouse?")

    local subtitle = content:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
    subtitle:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -12)
    subtitle:SetWidth(420)
    subtitle:SetJustifyH("LEFT")
    subtitle:SetText("Draws a customizable ring around your cursor so you never lose track of it in combat.")

    -- General
    local generalHeading = CreateHeading(content, "General", icon, -20)

    local enabledCB = CreateCheckbox(content, "Enable circle", "Show or hide the mouse circle entirely.",
        generalHeading, -12,
        function() return WMM.db.enabled end,
        function(v) WMM.db.enabled = v end)

    local combatCB = CreateCheckbox(content, "Only show in combat", "Only display the circle while you're in combat.",
        enabledCB, -8,
        function() return WMM.db.combatOnly end,
        function(v) WMM.db.combatOnly = v end)

    -- Shape
    local shapeHeading = CreateHeading(content, "Shape", combatCB, -24)

    local shapePicker = CreateShapePicker(content, shapeHeading, -14,
        function() return WMM.db.shape end,
        function(v) WMM.db.shape = v end)

    local widthSlider = CreateSlider(content, "Width", "How wide the circle is, in pixels.",
        shapePicker, -14, 10, 200, 1,
        function() return WMM.db.width end,
        function(v) WMM.db.width = v end,
        function(v) return v .. " px" end)

    local heightSlider = CreateSlider(content, "Height", "How tall the circle is, in pixels. Set different from Width for an oval.",
        widthSlider, -38, 10, 200, 1,
        function() return WMM.db.height end,
        function(v) WMM.db.height = v end,
        function(v) return v .. " px" end)

    local thicknessSlider = CreateSlider(content, "Thickness", "How thick the ring's outline is, in pixels.",
        heightSlider, -38, 1, 24, 1,
        function() return WMM.db.thickness end,
        function(v) WMM.db.thickness = v end,
        function(v) return v .. " px" end)

    -- Color
    local colorHeading = CreateHeading(content, "Color", thicknessSlider, -38)

    local hueSlider = CreateSlider(content, "Hue", "The color of the ring.",
        colorHeading, -20, 0, 360, 1,
        function() return WMM.db.hue end,
        function(v) WMM.db.hue = v end,
        function(v) return v .. "\194\176" end)

    local satSlider = CreateSlider(content, "Saturation", "How rich/vivid the color is.",
        hueSlider, -38, 0, 100, 1,
        function() return WMM.db.saturation end,
        function(v) WMM.db.saturation = v end,
        function(v) return v .. "%" end)

    local brightSlider = CreateSlider(content, "Brightness", "How light or dark the color is.",
        satSlider, -38, 0, 100, 1,
        function() return WMM.db.brightness end,
        function(v) WMM.db.brightness = v end,
        function(v) return v .. "%" end)

    local opacitySlider = CreateSlider(content, "Opacity", "How transparent the ring is.",
        brightSlider, -38, 0, 100, 1,
        function() return WMM.db.opacity end,
        function(v) WMM.db.opacity = v end,
        function(v) return v .. "%" end)

    -- Minimap
    local minimapHeading = CreateHeading(content, "Minimap", opacitySlider, -38)

    local minimapCB = CreateCheckbox(content, "Show minimap button", "Show or hide the minimap button.",
        minimapHeading, -12,
        function() return not WMM.db.minimap.hide end,
        function(v)
            WMM.db.minimap.hide = not v
            if WMM.UpdateMinimapButton then WMM.UpdateMinimapButton() end
        end)

    -- Reset button
    local resetBtn = CreateFrame("Button", nil, content, "UIPanelButtonTemplate")
    resetBtn:SetSize(160, 22)
    resetBtn:SetPoint("TOPLEFT", minimapCB, "BOTTOMLEFT", 0, -24)
    resetBtn:SetText("Reset to defaults")
    resetBtn:SetScript("OnClick", function()
        SlashCmdList["WHERESMEMOUSE"]("reset")
        WMM.RefreshOptionsPanel()
    end)

    content.lastWidget = resetBtn

    widgets = { enabledCB, combatCB, shapePicker, widthSlider, heightSlider, thicknessSlider,
        hueSlider, satSlider, brightSlider, opacitySlider, minimapCB }
end

-- Give the scrollable content frame a concrete height that reaches past
-- the last widget, so nothing gets clipped and the scrollbar knows how
-- far it can travel.
local function ResizeContent()
    if not content.lastWidget then return end
    local top, bottom = content:GetTop(), content.lastWidget:GetBottom()
    if top and bottom then
        content:SetHeight((top - bottom) + 32)
    end
end

function WMM.RefreshOptionsPanel()
    if not WMM.db then return end
    BuildPanel()
    for _, w in ipairs(widgets) do
        if w.refresh then w.refresh() end
    end
    ResizeContent()
end

panel:SetScript("OnShow", function()
    BuildPanel()
    WMM.RefreshOptionsPanel()
end)

--------------------------------------------------------------------
-- Register with the Blizzard Settings menu
--------------------------------------------------------------------

local function RegisterPanel()
    if Settings and Settings.RegisterCanvasLayoutCategory then
        local category = Settings.RegisterCanvasLayoutCategory(panel, panel.name)
        Settings.RegisterAddOnCategory(category)
        WMM.categoryID = category:GetID()
    elseif InterfaceOptions_AddCategory then
        -- Fallback for very old clients.
        InterfaceOptions_AddCategory(panel)
    end
end

RegisterPanel()

-- Called from the minimap button (and the /wmm slash command) to open
-- straight to this panel. Falls back to just toggling the whole Settings
-- window open if, for some reason, the category couldn't be resolved.
function WMM.OpenOptions()
    if Settings and Settings.OpenToCategory and WMM.categoryID then
        Settings.OpenToCategory(WMM.categoryID)
        -- The very first call after login can occasionally no-op while the
        -- Settings UI finishes initializing; a second call reliably works.
        Settings.OpenToCategory(WMM.categoryID)
    elseif SettingsPanel and SettingsPanel.Open then
        SettingsPanel:Open()
    end
end
