--[[
    UI/MinimapButton.lua

    A button on the minimap, so the addon can be opened without typing.

    Written here rather than pulled in with LibDBIcon: this is about fifty lines
    of verified widget calls, and the project's rule is minimal dependencies.
    The texture file ids are the ones LibDBIcon itself uses, read from a copy
    that runs on this client:

        136430  MiniMap-TrackingBorder
        136467  UI-Minimap-Background
        136477  UI-Minimap-ZoomButton-Highlight

    The button is draggable around the minimap and remembers where it was put.
    Dragging installs an OnUpdate for exactly as long as the button is held —
    the same bounded pattern the model viewer uses, and the only other place in
    the addon that needs one.
]]

local _, ns = ...

local MinimapButton = ns.NewModule("MinimapButton")
ns.UI = ns.UI or {}
ns.UI.MinimapButton = MinimapButton

local BUTTON_NAME = "ShinyHunterMinimapButton"

-- How far outside the minimap's edge the button sits. The distance from the
-- centre is derived from the minimap's CURRENT size, never a fixed number: the
-- minimap is a different size in modern retail than the 80 pixels a hardcoded
-- radius would assume, and addons resize it further. A constant radius puts the
-- button inside the map.
local EDGE_OFFSET = 5

local BORDER_TEXTURE = 136430
local BACKGROUND_TEXTURE = 136467
local HIGHLIGHT_TEXTURE = 136477

-- A native Hunter pet icon reads as a beast companion at minimap size and is
-- less ambiguous than the old spirit-beast portrait.
-- Our own paw, the same mark that labels a hunt on the world map. A stock bear
-- portrait said "hunter pet"; this says "Shiny Hunter", and on a crowded
-- minimap the player recognises the shape they already follow to their quarry.
local ICON_TEXTURE =
    "Interface\\AddOns\\ShinyHunter\\Assets\\UI\\shiny-hunter-map-paw.png"

local button

--=============================================================================
-- Placement
--=============================================================================

local function MoveToAngle(self, angle)
    local radians = math.rad(angle)
    local x, y = math.cos(radians), math.sin(radians)

    local halfWidth = Minimap:GetWidth() / 2 + EDGE_OFFSET
    local halfHeight = Minimap:GetHeight() / 2 + EDGE_OFFSET

    -- GetMinimapShape is a community convention supplied by minimap addons,
    -- not a Blizzard API, so its absence means the default round map.
    local shape = GetMinimapShape and GetMinimapShape() or "ROUND"

    if shape == "ROUND" then
        x, y = x * halfWidth, y * halfHeight
    else
        -- Square and corner shapes: ride the edge rather than an ellipse.
        -- LibDBIcon distinguishes fifteen shapes quadrant by quadrant; this
        -- clamp is the same idea without the table, and is a close enough fit
        -- for the reshaping addons that exist.
        local diagonalWidth = math.sqrt(2 * halfWidth * halfWidth) - 10
        local diagonalHeight = math.sqrt(2 * halfHeight * halfHeight) - 10

        x = math.max(-halfWidth, math.min(x * diagonalWidth, halfWidth))
        y = math.max(-halfHeight, math.min(y * diagonalHeight, halfHeight))
    end

    self:ClearAllPoints()
    self:SetPoint("CENTER", Minimap, "CENTER", x, y)
end

local function AngleFromCursor()
    local scale = Minimap:GetEffectiveScale()
    local centerX, centerY = Minimap:GetCenter()
    local cursorX, cursorY = GetCursorPosition()

    if not centerX or not cursorX then
        return nil
    end

    return math.deg(math.atan2(cursorY / scale - centerY, cursorX / scale - centerX))
end

local function OnDragUpdate(self)
    local angle = AngleFromCursor()
    if angle then
        MoveToAngle(self, angle)
    end
end

--=============================================================================
-- Construction
--=============================================================================

local function Build()
    local self = CreateFrame("Button", BUTTON_NAME, Minimap)

    self:SetSize(31, 31)
    self:SetFrameStrata("MEDIUM")
    self:SetFrameLevel(8)
    self:RegisterForClicks("LeftButtonUp")
    self:RegisterForDrag("LeftButton")

    local background = self:CreateTexture(nil, "BACKGROUND")
    background:SetSize(20, 20)
    background:SetPoint("TOPLEFT", 7, -5)
    background:SetTexture(BACKGROUND_TEXTURE)

    local icon = self:CreateTexture(nil, "ARTWORK")
    icon:SetSize(18, 18)
    icon:SetPoint("TOPLEFT", 7, -6)
    icon:SetTexture(ICON_TEXTURE)
    -- A stock icon is cropped to hide the border its file carries. The paw is
    -- our own art with transparent edges, so trimming it would only cut claws.
    icon:SetTexCoord(0, 1, 0, 1)

    local border = self:CreateTexture(nil, "OVERLAY")
    border:SetSize(56, 56)
    border:SetPoint("TOPLEFT", 0, 0)
    border:SetTexture(BORDER_TEXTURE)

    self:SetHighlightTexture(HIGHLIGHT_TEXTURE)

    self:SetScript("OnClick", function()
        ns.UI.MainWindow:Toggle()
    end)

    self:SetScript("OnDragStart", function(dragged)
        dragged:SetScript("OnUpdate", OnDragUpdate)
    end)

    self:SetScript("OnDragStop", function(dragged)
        dragged:SetScript("OnUpdate", nil)

        local angle = AngleFromCursor()
        if angle then
            ns.Config:Set("minimapAngle", angle)
        end
    end)

    self:SetScript("OnEnter", function(hovered)
        GameTooltip:SetOwner(hovered, "ANCHOR_LEFT")
        GameTooltip:AddLine(ns.L["ADDON_TITLE"])
        GameTooltip:AddLine(ns.L["MINIMAP_TOOLTIP"], 1, 1, 1, true)
        GameTooltip:Show()
    end)

    self:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)

    MoveToAngle(self, ns.Config:Get("minimapAngle") or 200)

    ns.Debug:Log("UI", "minimap button created")

    return self
end

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

--- Shows or hides the button, and remembers the choice.
function MinimapButton:SetShown(shown)
    ns.Config:Set("minimapEnabled", shown and true or false)

    if button then
        button:SetShown(shown)
    elseif shown then
        button = Build()
    end
end

function MinimapButton:IsShown()
    return button ~= nil and button:IsShown()
end

function MinimapButton:OnEnable()
    if ns.Config:Get("minimapEnabled") == false then
        return
    end

    button = Build()
end
