-- CollectionLog_MinimapButton.lua
-- Minimap button with robust fallback: try Blizzard template, fall back to manual textures.
-- Goal: ALWAYS show an icon + ring, never disappear due to missing templates.

local ADDON, ns = ...

local BTN_NAME  = "CollectionLogMinimapButton"
local ICON_PATH = "Interface\\AddOns\\CollectionLog\\Media\\MinimapIcon.tga"

-- Match the de-facto minimap button contract used by many addons (and collectors):
-- 31x31 button, 23x23 icon, 53x53 tracking border anchored TOPLEFT.
local BTN_SIZE    = 31
local ICON_SIZE   = 23
local BORDER_SIZE = 53
local HIGHLIGHT_TEX = "Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight"

local DEFAULTS = {
  hide = false,
  angle = 220,
  minimapPos = 220, -- LibDBIcon compatibility (degrees, 0=east)

  -- Back-compat note: older builds used a fixed 80px radius, which often placed
  -- the button *inside* the minimap on many UI scales. We keep the field so
  -- users/addons can override it, but when it equals the legacy default we
  -- compute an edge-hugging dynamic radius.
  radius = 80,
}

-- Minimap shape handling (mirrors common addon practice, e.g. LibDBIcon).
-- Some minimap skins use non-round shapes. This keeps the button hugging the
-- edge without drifting into "cut" corners.
local MINIMAP_SHAPES = {
  ROUND = { true, true, true, true },
  SQUARE = { true, true, true, true },
  -- Order: top-right, top-left, bottom-left, bottom-right.
  CORNER_TOPLEFT = { false, false, true, false },
  CORNER_TOPRIGHT = { false, false, false, true },
  CORNER_BOTTOMLEFT = { false, true, false, false },
  CORNER_BOTTOMRIGHT = { true, false, false, false },
  SIDE_LEFT = { false, true, true, false },
  SIDE_RIGHT = { true, false, false, true },
  SIDE_TOP = { false, false, true, true },
  SIDE_BOTTOM = { true, true, false, false },
  TRICORNER_TOPLEFT = { false, true, true, true },
  TRICORNER_TOPRIGHT = { true, false, true, true },
  TRICORNER_BOTTOMLEFT = { true, true, false, true },
  TRICORNER_BOTTOMRIGHT = { true, true, true, false },
}

local function GetMinimapRadius()
  local w = (Minimap and Minimap.GetWidth) and Minimap:GetWidth() or 140
  return w / 2
end

local function ComputeEdgeHugRadius()
  -- Place the *center* of the 31x31 button slightly outside the minimap rim so
  -- the button appears on the edge rather than inside the map texture.
  local minimapR = GetMinimapRadius()
  return minimapR + (BTN_SIZE / 2) - 2
end

local function EnsureDB()
  CollectionLogDB = CollectionLogDB or {}
  CollectionLogDB.minimapButton = CollectionLogDB.minimapButton or {}
  for k, v in pairs(DEFAULTS) do
    if CollectionLogDB.minimapButton[k] == nil then
      CollectionLogDB.minimapButton[k] = v
    end
  end

  -- Back-compat sync: keep angle/minimapPos aligned for whichever system is active.
  if CollectionLogDB.minimapButton.minimapPos == nil and CollectionLogDB.minimapButton.angle ~= nil then
    CollectionLogDB.minimapButton.minimapPos = CollectionLogDB.minimapButton.angle
  elseif CollectionLogDB.minimapButton.angle == nil and CollectionLogDB.minimapButton.minimapPos ~= nil then
    CollectionLogDB.minimapButton.angle = CollectionLogDB.minimapButton.minimapPos
  end
end

local function UpdatePosition(btn)
  EnsureDB()
  local cfg = CollectionLogDB.minimapButton
  local angle = math.rad(cfg.angle)

  -- Back-compat: if radius is still the legacy default (80), use a dynamic
  -- value based on the current minimap size so the icon hugs the edge.
  local r = cfg.radius
  if not r or r == DEFAULTS.radius then
    r = ComputeEdgeHugRadius()
  end

  local x = math.cos(angle) * r
  local y = math.sin(angle) * r

  -- Respect minimap shape cutouts if present.
  local shape = (type(GetMinimapShape) == "function") and GetMinimapShape() or "ROUND"
  if type(shape) == "string" then
    shape = shape:gsub("-", "_"):upper()
  else
    shape = "ROUND"
  end
  local quad = (x > 0) and ((y > 0) and 1 or 4) or ((y > 0) and 2 or 3)
  local allowed = MINIMAP_SHAPES[shape] or MINIMAP_SHAPES.ROUND

  if not allowed[quad] then
    -- Clamp to the nearest edge of the minimap's square bounds.
    local clamp = GetMinimapRadius()
    x = math.max(-clamp, math.min(x, clamp))
    y = math.max(-clamp, math.min(y, clamp))
  end

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

local function GetAngleFromCursor()
  local mx, my = Minimap:GetCenter()
  local cx, cy = GetCursorPosition()
  local scale = Minimap:GetEffectiveScale()
  cx, cy = cx / scale, cy / scale
  return math.deg(math.atan2(cy - my, cx - mx))
end

local function ToggleMain()
  if ns and ns.UI and type(ns.UI.Toggle) == "function" then
    ns.UI.Toggle()
  elseif SlashCmdList and SlashCmdList["COLLECTIONLOG"] then
    SlashCmdList["COLLECTIONLOG"]("")
  end
end

local function AttachCommonScripts(btn)
  -- Many button collectors/skins expect "AnyUp".
  btn:RegisterForClicks("AnyUp")
  btn:RegisterForDrag("LeftButton")
  btn:SetHighlightTexture(HIGHLIGHT_TEX, "ADD")

  -- Force the highlight to match Blizzard minimap buttons.
  local hl = btn:GetHighlightTexture()
  if hl then
    hl:ClearAllPoints()
    hl:SetPoint("TOPLEFT", btn, "TOPLEFT", 1, -1)
    hl:SetSize(BTN_SIZE, BTN_SIZE)
  end

  btn:SetScript("OnDragStart", function(self)
    self:SetScript("OnUpdate", function()
      CollectionLogDB.minimapButton.angle = GetAngleFromCursor()
      UpdatePosition(self)
    end)
  end)

  btn:SetScript("OnDragStop", function(self)
    self:SetScript("OnUpdate", nil)
  end)

  btn:SetScript("OnClick", function(_, button)
    if button == "LeftButton" then
      ToggleMain()
    end
  end)

  btn:SetScript("OnEnter", function(self)
    GameTooltip:SetOwner(self, "ANCHOR_LEFT")
    GameTooltip:AddLine("Collection Log")
    GameTooltip:AddLine("Left-click: Open", 1,1,1)
    GameTooltip:Show()
  end)

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

local function CreateButton_Manual()
  local btn = CreateFrame("Button", BTN_NAME, Minimap)
  btn:SetSize(BTN_SIZE, BTN_SIZE)
  btn:SetFrameStrata("MEDIUM")
  btn:SetFrameLevel(Minimap:GetFrameLevel() + 10)
  btn:SetToplevel(true)
  btn:SetClampedToScreen(true)

  local icon = btn:CreateTexture(nil, "ARTWORK")
  icon:SetTexture(ICON_PATH)
  icon:SetSize(ICON_SIZE, ICON_SIZE)
  icon:SetPoint("CENTER", btn, "CENTER", -1, 0)

  local border = btn:CreateTexture(nil, "OVERLAY")
  border:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder")
  border:SetSize(BORDER_SIZE, BORDER_SIZE)
  border:SetPoint("TOPLEFT", btn, "TOPLEFT", 0, 0)

  btn.icon = icon
  btn.border = border

  AttachCommonScripts(btn)
  if btn.border then btn.border:SetDrawLayer("OVERLAY", 1) end
  if btn.icon then btn.icon:SetDrawLayer("ARTWORK", 1) end
  UpdatePosition(btn)
  return btn
end

local function CreateButton_Template()
  -- Template may not exist on some clients/skins; guard it.
  local ok, btn = pcall(CreateFrame, "Button", BTN_NAME, Minimap, "MiniMapTrackingButtonTemplate")
  if not ok or not btn then
    return nil
  end

  btn:SetSize(BTN_SIZE, BTN_SIZE)
  btn:SetFrameStrata("MEDIUM")
  btn:SetFrameLevel(Minimap:GetFrameLevel() + 10)
  btn:SetToplevel(true)
  btn:SetClampedToScreen(true)

  local icon = _G[BTN_NAME .. "Icon"]
  local border = _G[BTN_NAME .. "Border"]

  if icon then
    icon:SetTexture(ICON_PATH)
    icon:ClearAllPoints()
    icon:SetPoint("CENTER", btn, "CENTER", -1, 0)
    icon:SetSize(ICON_SIZE, ICON_SIZE)
  end

  if border then
    border:ClearAllPoints()
    border:SetPoint("TOPLEFT", btn, "TOPLEFT", 0, 0)
    border:SetSize(BORDER_SIZE, BORDER_SIZE)
  end

  btn.icon = icon
  btn.border = border

  AttachCommonScripts(btn)
  if btn.border then btn.border:SetDrawLayer("OVERLAY", 1) end
  if btn.icon then btn.icon:SetDrawLayer("ARTWORK", 1) end
  UpdatePosition(btn)
  return btn
end


local function TryLibDBIcon()
  -- If the user has LibDBIcon (often provided by minimap button collectors),
  -- register via the standard contract so collectors can manage this button
  -- without warning tooltips.
  EnsureDB()

  local LibStub = _G.LibStub
  if type(LibStub) ~= "function" then return nil end

  local LDB = LibStub("LibDataBroker-1.1", true)
  local DBIcon = LibStub("LibDBIcon-1.0", true)
  if not (LDB and DBIcon) then return nil end

  if not ns._clLDBObj then
    local obj = LDB:NewDataObject("CollectionLog", {
      type = "launcher",
      icon = ICON_PATH,
    })

    obj.OnClick = function(_, button)
      if button == "LeftButton" then
        ToggleMain()
      end
    end

    obj.OnTooltipShow = function(tt)
      tt:AddLine("Collection Log")
      tt:AddLine("Left-click: Open", 1, 1, 1)
    end

    ns._clLDBObj = obj
  end

  -- LibDBIcon reads: db.hide (bool), db.minimapPos (degrees). We store our config
  -- in CollectionLogDB.minimapButton; ensure it has the expected key.
  CollectionLogDB.minimapButton.minimapPos = CollectionLogDB.minimapButton.minimapPos or CollectionLogDB.minimapButton.angle or DEFAULTS.minimapPos

  return DBIcon
end

local function CreateButton()
  if _G[BTN_NAME] then return _G[BTN_NAME] end

  -- Prefer template ring when available; fallback to manual.
  local btn = CreateButton_Template()
  if not btn then
    btn = CreateButton_Manual()
  end
  return btn
end

function ns.MinimapButton_Init()
  EnsureDB()

  -- Prefer LibDBIcon if available (eliminates collector warnings and lets users
  -- manage the button via their minimap button bar addon).
  local DBIcon = TryLibDBIcon()
  if DBIcon and ns._clLDBObj then
    if not ns._clDBIconRegistered then
      DBIcon:Register("CollectionLog", ns._clLDBObj, CollectionLogDB.minimapButton)
      ns._clDBIconRegistered = true
    end
    if CollectionLogDB.minimapButton.hide then
      DBIcon:Hide("CollectionLog")
    else
      DBIcon:Show("CollectionLog")
    end
    return
  end

  -- Fallback: custom manual button
  local btn = CreateButton()
  if CollectionLogDB.minimapButton.hide then
    btn:Hide()
  else
    btn:Show()
  end
  UpdatePosition(btn)
end

function ns.MinimapButton_Show()
  EnsureDB()
  CollectionLogDB.minimapButton.hide = false

  local DBIcon = TryLibDBIcon()
  if DBIcon and ns._clLDBObj then
    if not ns._clDBIconRegistered then
      DBIcon:Register("CollectionLog", ns._clLDBObj, CollectionLogDB.minimapButton)
      ns._clDBIconRegistered = true
    end
    DBIcon:Show("CollectionLog")
    return
  end

  local btn = CreateButton()
  btn:Show()
  UpdatePosition(btn)
end

function ns.MinimapButton_Hide()
  EnsureDB()
  CollectionLogDB.minimapButton.hide = true

  local DBIcon = TryLibDBIcon()
  if DBIcon and ns._clLDBObj and ns._clDBIconRegistered then
    DBIcon:Hide("CollectionLog")
    return
  end

  local btn = CreateButton()
  btn:Hide()
end

function ns.MinimapButton_Toggle()
  EnsureDB()
  if CollectionLogDB.minimapButton.hide then
    ns.MinimapButton_Show()
  else
    ns.MinimapButton_Hide()
  end
end

function ns.MinimapButton_Reset()
  EnsureDB()
  for k,v in pairs(DEFAULTS) do
    CollectionLogDB.minimapButton[k] = v
  end
  -- Keep LibDBIcon key in sync
  CollectionLogDB.minimapButton.minimapPos = CollectionLogDB.minimapButton.minimapPos or CollectionLogDB.minimapButton.angle or DEFAULTS.minimapPos

  local DBIcon = TryLibDBIcon()
  if DBIcon and ns._clLDBObj then
    if not ns._clDBIconRegistered then
      DBIcon:Register("CollectionLog", ns._clLDBObj, CollectionLogDB.minimapButton)
      ns._clDBIconRegistered = true
    end
    DBIcon:Show("CollectionLog")
    return
  end

  local btn = CreateButton()
  btn:Show()
  UpdatePosition(btn)
end
