local pluginName, pluginShared = ...
local TooltipPreview = {}
pluginShared.TooltipPreview = TooltipPreview

local guarded = pluginShared.guarded
local reported = pluginShared.reported or guarded

local PREVIEW_TOOLTIPS = { "GameTooltip", "ItemRefTooltip" }
local SIDE_TOOLTIPS = { "ShoppingTooltip1", "ShoppingTooltip2" }

function TooltipPreview:Enabled()
    local profile = pluginShared.db and pluginShared.db.profile
    local general = profile and profile.generalSettings
    return not (general and general.itemTooltipPreview == false)
end

-- The bare item link inside a hyperlink, the form the widget's hover preview takes
function TooltipPreview:ItemLink(hyperlink)
    if type(hyperlink) ~= "string" or (type(issecretvalue) == "function" and issecretvalue(hyperlink)) then
        return nil
    end
    return hyperlink:match("|H(item:[^|]+)|h") or hyperlink:match("^(item:[^|]+)")
end

function TooltipPreview:Wanted(tooltip)
    for _, name in ipairs(PREVIEW_TOOLTIPS) do
        if tooltip == _G[name] then
            return true
        end
    end
    return false
end

-- Every item tooltip (bags, vendors, loot, chat links, quest rewards) gets the widget's try-on model
function TooltipPreview:OnItemTooltip(tooltip, tooltipData)
    local lockouts = pluginShared.Lockouts
    if not (lockouts and lockouts.ShowHoverPreview and self:Wanted(tooltip)) then
        return
    end
    if type(tooltip.IsForbidden) == "function" and tooltip:IsForbidden() then
        return
    end
    if not self:Enabled() then
        return
    end
    local hyperlink
    if type(TooltipUtil) == "table" and type(TooltipUtil.GetDisplayedItem) == "function" then
        local ok, _, found = reported(TooltipUtil.GetDisplayedItem, tooltip)
        if ok then hyperlink = found end
    end
    if not hyperlink and type(tooltipData) == "table" then
        hyperlink = tooltipData.hyperlink
    end
    local link = self:ItemLink(hyperlink)
    -- Exactly the call the panel's own hover makes (LockoutsUI.lua:1536). ShowHoverPreview already
    -- works out whether the item can be worn and hides itself when it cannot, so there is no second
    -- opinion here to disagree with it: a gate in front of it could only ever refuse things the widget
    -- happily shows, which is what it was doing.
    if link then
        lockouts:ShowHoverPreview(link, tooltip)
    elseif lockouts.HideHoverPreview then
        lockouts:HideHoverPreview(tooltip)
    end
end

function TooltipPreview:Install()
    if self.installed then
        return false
    end
    if not (type(TooltipDataProcessor) == "table" and type(TooltipDataProcessor.AddTooltipPostCall) == "function"
        and Enum and Enum.TooltipDataType and Enum.TooltipDataType.Item) then
        return false
    end
    self.installed = true
    TooltipDataProcessor.AddTooltipPostCall(Enum.TooltipDataType.Item, function(tooltip, tooltipData)
        TooltipPreview:OnItemTooltip(tooltip, tooltipData)
    end)
    for _, name in ipairs(PREVIEW_TOOLTIPS) do
        local tooltip = _G[name]
        if type(tooltip) == "table" and type(tooltip.HookScript) == "function" then
            tooltip:HookScript("OnHide", function(self)
                local lockouts = pluginShared.Lockouts
                if lockouts and lockouts.HideHoverPreview then
                    lockouts:HideHoverPreview(self)
                end
            end)
            -- the model is placed while the tooltip is still being built; the tooltip says when it
            -- has settled, and the model is placed again then
            for _, event in ipairs({ "OnShow", "OnSizeChanged" }) do
                tooltip:HookScript(event, function(self)
                    local lockouts = pluginShared.Lockouts
                    if lockouts and lockouts.SettleHoverPreview then
                        lockouts:SettleHoverPreview(self)
                    end
                end)
            end
        end
    end
    -- the comparison tooltips beside an item tooltip come, go and grow after it: every model on screen
    -- moves clear of them when they do
    for _, name in ipairs(SIDE_TOOLTIPS) do
        local side = _G[name]
        if type(side) == "table" and type(side.HookScript) == "function" then
            for _, event in ipairs({ "OnShow", "OnHide", "OnSizeChanged" }) do
                side:HookScript(event, function()
                    local lockouts = pluginShared.Lockouts
                    if lockouts and lockouts.SettleHoverPreview then
                        lockouts:SettleHoverPreview()
                    end
                end)
            end
        end
    end
    -- a dungeon score or rating link rewrites the open chat-link tooltip without hiding it and
    -- without an item tooltip behind it: the model it was showing goes when that happens
    if type(hooksecurefunc) == "function" and type(_G.SetItemRef) == "function" then
        hooksecurefunc("SetItemRef", function(clicked)
            TooltipPreview:OnChatLink(clicked)
        end)
    end
    return true
end

function TooltipPreview:OnChatLink(clicked)
    local lockouts = pluginShared.Lockouts
    if not (lockouts and lockouts.HideHoverPreview) then
        return
    end
    local kind
    if type(clicked) == "string" and not (type(issecretvalue) == "function" and issecretvalue(clicked)) then
        -- the click hands over the link body ("item:19019:..."); a whole hyperlink is read too
        kind = clicked:match("^|H([^:]+)") or clicked:match("^([^:]+)")
    end
    if kind ~= "item" then
        lockouts:HideHoverPreview(_G.ItemRefTooltip)
    end
end

TooltipPreview:Install()
