local pluginName, pluginShared = ...
local L = LibStub("AceLocale-3.0"):GetLocale("AppearanceMatic")

-- The launcher: the medallion's hover hint and right-click menu on the panel, and the minimap
-- button (LibDataBroker object shown through LibDBIcon). Both open the same four things:
-- the Wardrobe, the Mount Journal, the change log popup and the settings.
local Launcher = {}
pluginShared.Launcher = Launcher

local say = pluginShared.say or print
local BROKER_NAME = "AppearanceMatic"
local BROKER_ICON = "Interface\\AddOns\\AppearanceMatic\\Media\\Textures\\AppearanceMatic-icon.png"
local TITLE = "AppearanceMatic"

function Launcher:OpenPanel()
    local lockouts = pluginShared.Lockouts
    if lockouts and lockouts.ToggleWidget then
        lockouts:ToggleWidget()
    end
end

function Launcher:OpenSettings()
    if pluginShared.OpenSettings then
        pluginShared.OpenSettings()
    end
end

function Launcher:OpenChangeLog()
    if pluginShared.ShowChangeLog then
        pluginShared.ShowChangeLog()
    end
end

-- The Collections Journal on one of its tabs (the game's own tab constants). Mid-fight the game
-- blocks an addon showing it (ADDON_ACTION_BLOCKED on MountJournal:SetShown from
-- CollectionsJournal_UpdateSelectedTab), so the player is told and it opens the moment the fight ends.
function Launcher:OpenJournal(tabIndex)
    if not (type(tabIndex) == "number" and type(ToggleCollectionsJournal) == "function") then
        return false
    end
    local inCombat = type(InCombatLockdown) == "function" and InCombatLockdown() or false
    if AppearanceMaticDB then
        AppearanceMaticDB.diag = AppearanceMaticDB.diag or {}
        AppearanceMaticDB.diag.journalOpenInCombat = inCombat
    end
    if inCombat then
        self.journalAfterCombat = tabIndex
        self:WatchCombatEnd()
        say(L["menu_journal_combat"])
        return false
    end
    ToggleCollectionsJournal(tabIndex)
    return true
end

function Launcher:WatchCombatEnd()
    if self.combatWatcher or type(CreateFrame) ~= "function" then
        return
    end
    local watcher = CreateFrame("Frame")
    watcher:RegisterEvent("PLAYER_REGEN_ENABLED")
    watcher:SetScript("OnEvent", function()
        Launcher:OnCombatEnd()
    end)
    self.combatWatcher = watcher
end

function Launcher:OnCombatEnd()
    local tabIndex = self.journalAfterCombat
    self.journalAfterCombat = nil
    if tabIndex then
        self:OpenJournal(tabIndex)
    end
end

function Launcher:OpenWardrobe()
    return self:OpenJournal(_G.COLLECTIONS_JOURNAL_TAB_INDEX_APPEARANCES)
end

function Launcher:OpenMounts()
    return self:OpenJournal(_G.COLLECTIONS_JOURNAL_TAB_INDEX_MOUNTS)
end

-- The menu, top to bottom
function Launcher:MenuEntries()
    return {
        { label = L["menu_wardrobe"], run = function() Launcher:OpenWardrobe() end },
        { label = L["menu_mounts"], run = function() Launcher:OpenMounts() end },
        { label = L["menu_changelog"], run = function() Launcher:OpenChangeLog() end },
        { label = L["menu_settings"], run = function() Launcher:OpenSettings() end },
    }
end

function Launcher:ShowMenu(owner)
    if not (type(MenuUtil) == "table" and type(MenuUtil.CreateContextMenu) == "function") then
        return false
    end
    MenuUtil.CreateContextMenu(owner, function(_, root)
        root:CreateTitle(TITLE)
        for _, entry in ipairs(Launcher:MenuEntries()) do
            root:CreateButton(entry.label, entry.run)
        end
    end)
    return true
end

-- The panel's medallion: hover for the hint, right-click for the menu; dragging it still moves
-- the panel (unless the panel is locked), and the drop saves the position.
function Launcher:AttachMedallion(emblem, panel, isLocked, onMoved)
    if not (emblem and panel and emblem.SetScript) then
        return false
    end
    emblem:EnableMouse(true)
    if emblem.RegisterForDrag then
        emblem:RegisterForDrag("LeftButton")
    end
    emblem:SetScript("OnDragStart", function()
        if isLocked and isLocked() then
            return
        end
        panel:StartMoving()
    end)
    emblem:SetScript("OnDragStop", function()
        panel:StopMovingOrSizing()
        if onMoved then
            onMoved()
        end
    end)
    emblem:SetScript("OnMouseUp", function(frame, button)
        if button == "RightButton" then
            Launcher:ShowMenu(frame)
        end
    end)
    emblem:SetScript("OnEnter", function(frame)
        GameTooltip:SetOwner(frame, "ANCHOR_RIGHT")
        GameTooltip:SetText(TITLE)
        GameTooltip:AddLine(L["medallion_hint"], 0.8, 0.8, 0.8)
        GameTooltip:Show()
    end)
    emblem:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)
    return true
end

-- The minimap button. profile.minimap holds LibDBIcon's state ({ hide, minimapPos }).
function Launcher:Setup(profile)
    local ldb = LibStub and LibStub("LibDataBroker-1.1", true)
    local icon = LibStub and LibStub("LibDBIcon-1.0", true)
    if not (ldb and icon and type(profile) == "table") then
        return false
    end
    if not self.broker then
        self.broker = ldb:NewDataObject(BROKER_NAME, {
            type = "launcher",
            text = TITLE,
            icon = BROKER_ICON,
            OnClick = function(_, button)
                if button == "RightButton" then
                    Launcher:OpenSettings()
                else
                    Launcher:OpenPanel()
                end
            end,
            OnTooltipShow = function(tooltip)
                tooltip:SetText(TITLE)
                tooltip:AddLine(L["minimap_left_click"], 1, 1, 1)
                tooltip:AddLine(L["minimap_right_click"], 1, 1, 1)
            end,
        }) or ldb:GetDataObjectByName(BROKER_NAME)
    end
    if not self.broker then
        return false
    end
    profile.minimap = profile.minimap or { hide = false }
    if not icon:IsRegistered(BROKER_NAME) then
        icon:Register(BROKER_NAME, self.broker, profile.minimap)
    elseif icon.Refresh then
        icon:Refresh(BROKER_NAME, profile.minimap)
    end
    if profile.minimap.hide then
        icon:Hide(BROKER_NAME)
    else
        icon:Show(BROKER_NAME)
    end
    return true
end

function Launcher:MinimapShown(profile)
    return not (type(profile) == "table" and type(profile.minimap) == "table" and profile.minimap.hide)
end

function Launcher:SetMinimapShown(profile, shown)
    if type(profile) ~= "table" then
        return
    end
    profile.minimap = profile.minimap or {}
    profile.minimap.hide = not shown
    local icon = LibStub and LibStub("LibDBIcon-1.0", true)
    if icon and icon:IsRegistered(BROKER_NAME) then
        if shown then
            icon:Show(BROKER_NAME)
        else
            icon:Hide(BROKER_NAME)
        end
    end
end
