-- Core.lua
-- "Where's me mouse?"
-- Draws a customizable ring that follows the mouse cursor so it's
-- easy to keep track of during hectic combat.

WheresMeMouse = WheresMeMouse or {}
local WMM = WheresMeMouse

local ADDON_NAME = "WheresMeMouse"

WMM.ADDON_NAME = ADDON_NAME

-- Available outline shapes. Each shape ships as 9 separate textures at
-- different baked-in stroke-to-radius ratios (thin ring up to fully
-- filled) rather than one texture stacked multiple times -- stacking
-- several semi-transparent copies of the same soft-edged texture close
-- together caused visible moire/banding, so instead we pick the single
-- closest-matching preset and draw it once.
WMM.SHAPE_FRACTIONS = { 0.05, 0.08, 0.12, 0.18, 0.26, 0.38, 0.55, 0.75, 1.0 }

local function BuildShapeTextures(shapeKey)
    local paths = {}
    for i = 1, #WMM.SHAPE_FRACTIONS do
        paths[i] = "Interface\\AddOns\\WheresMeMouse\\Media\\Shapes\\" .. shapeKey .. "_" .. i .. ".png"
    end
    return paths
end

WMM.SHAPES = {
    circle   = BuildShapeTextures("circle"),
    square   = BuildShapeTextures("square"),
    diamond  = BuildShapeTextures("diamond"),
    triangle = BuildShapeTextures("triangle"),
}

-- Display order + labels for the options panel's shape picker. Uses the
-- thinnest preset of each shape as the button icon.
WMM.SHAPE_LIST = {
    { key = "circle",   label = "Circle" },
    { key = "square",   label = "Square" },
    { key = "diamond",  label = "Diamond" },
    { key = "triangle", label = "Triangle" },
}

--------------------------------------------------------------------
-- Defaults / Saved Variables
--------------------------------------------------------------------

WMM.defaults = {
    enabled     = true,     -- master on/off switch
    combatOnly  = false,    -- true = only show while in combat
    shape       = "circle", -- one of the keys in WMM.SHAPES
    width       = 40,       -- ring width in pixels
    height      = 40,       -- ring height in pixels
    thickness   = 4,        -- ring stroke thickness in pixels
    hue         = 45,       -- 0-360
    saturation  = 100,      -- 0-100
    brightness  = 100,      -- 0-100
    opacity     = 90,       -- 0-100
    locked      = false,    -- reserved for future manual offset locking
    minimap = {
        hide       = false,
        minimapPos = 200,   -- angle in degrees, read/written by LibDBIcon
    },
}

local function CopyDefaults(src, dst)
    for k, v in pairs(src) do
        if type(v) == "table" then
            if type(dst[k]) ~= "table" then
                dst[k] = {}
            end
            CopyDefaults(v, dst[k])
        elseif dst[k] == nil then
            dst[k] = v
        end
    end
    return dst
end

--------------------------------------------------------------------
-- Ring frame
--------------------------------------------------------------------

local ring = CreateFrame("Frame", "WheresMeMouseRing", UIParent)
ring:SetFrameStrata("TOOLTIP")
ring:SetSize(40, 40)
ring:Hide()
WMM.ring = ring

local ringTexture = ring:CreateTexture(nil, "ARTWORK")
ringTexture:SetPoint("CENTER", ring, "CENTER", 0, 0)

-- Pick the shape preset whose baked-in stroke-to-radius ratio is closest
-- to what the current Width/Height/Thickness settings call for.
local function PickPresetIndex(fraction)
    local fractions = WMM.SHAPE_FRACTIONS
    local bestIndex, bestDiff = 1, math.huge
    for i, f in ipairs(fractions) do
        local diff = math.abs(f - fraction)
        if diff < bestDiff then
            bestIndex, bestDiff = i, diff
        end
    end
    return bestIndex
end

-- Redraw the ring texture from the current settings.
function WMM.ApplyAppearance()
    local db = WMM.db
    if not db then return end

    local shapeTextures = WMM.SHAPES[db.shape] or WMM.SHAPES.circle
    local width  = math.max(8, db.width or 40)
    local height = math.max(8, db.height or 40)
    local thickness = math.max(1, math.min(24, db.thickness or 4))
    local r, g, b = WMM.HSVToRGB(db.hue or 45, db.saturation or 100, db.brightness or 100)
    local alpha = math.max(0, math.min(100, db.opacity or 90)) / 100

    local outerW = width + thickness * 2
    local outerH = height + thickness * 2
    ring:SetSize(outerW, outerH)

    local avgRadius = (outerW + outerH) / 4
    local fraction = math.max(0.05, math.min(1.0, thickness / avgRadius))
    local presetIndex = PickPresetIndex(fraction)

    ringTexture:SetTexture(shapeTextures[presetIndex])
    ringTexture:SetSize(outerW, outerH)
    ringTexture:SetVertexColor(r, g, b, alpha)
end

--------------------------------------------------------------------
-- Follow the cursor
--------------------------------------------------------------------

ring:SetScript("OnUpdate", function(self)
    local x, y = GetCursorPosition()
    local scale = UIParent:GetEffectiveScale()
    self:ClearAllPoints()
    self:SetPoint("CENTER", UIParent, "BOTTOMLEFT", x / scale, y / scale)
end)

--------------------------------------------------------------------
-- Visibility logic
--------------------------------------------------------------------

function WMM.UpdateVisibility()
    local db = WMM.db
    if not db or not db.enabled then
        ring:Hide()
        return
    end

    if db.combatOnly then
        if InCombatLockdown() then
            ring:Show()
        else
            ring:Hide()
        end
    else
        ring:Show()
    end
end

--------------------------------------------------------------------
-- Events
--------------------------------------------------------------------

local eventFrame = CreateFrame("Frame")
eventFrame:RegisterEvent("ADDON_LOADED")
eventFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
eventFrame:RegisterEvent("PLAYER_REGEN_ENABLED")

eventFrame:SetScript("OnEvent", function(self, event, arg1)
    if event == "ADDON_LOADED" and arg1 == ADDON_NAME then
        WheresMeMouseDB = WheresMeMouseDB or {}
        WMM.db = CopyDefaults(WMM.defaults, WheresMeMouseDB)

        WMM.ApplyAppearance()
        WMM.UpdateVisibility()

        if WMM.RefreshOptionsPanel then
            WMM.RefreshOptionsPanel()
        end
        if WMM.CreateMinimapButton then
            WMM.CreateMinimapButton()
        end

    elseif event == "PLAYER_REGEN_DISABLED" or event == "PLAYER_REGEN_ENABLED" then
        WMM.UpdateVisibility()
    end
end)

--------------------------------------------------------------------
-- Slash commands
--------------------------------------------------------------------

SLASH_WHERESMEMOUSE1 = "/wheresmemouse"
SLASH_WHERESMEMOUSE2 = "/wmm"

SlashCmdList["WHERESMEMOUSE"] = function(msg)
    msg = (msg or ""):lower():trim()

    if msg == "toggle" then
        WMM.db.enabled = not WMM.db.enabled
        WMM.UpdateVisibility()
        print("|cff4de0ffWhere's me mouse?|r " .. (WMM.db.enabled and "enabled" or "disabled"))
    elseif msg == "reset" then
        WheresMeMouseDB = {}
        WMM.db = CopyDefaults(WMM.defaults, WheresMeMouseDB)
        WMM.ApplyAppearance()
        WMM.UpdateVisibility()
        if WMM.RefreshOptionsPanel then WMM.RefreshOptionsPanel() end
        print("|cff4de0ffWhere's me mouse?|r settings reset to defaults.")
    else
        if WMM.OpenOptions then
            WMM.OpenOptions()
        else
            print("|cff4de0ffWhere's me mouse?|r use /wmm toggle or /wmm reset.")
        end
    end
end
