-- Transmoggin - Copyright (c) 2026 Rooster_Roost. All Rights Reserved.
-- Proprietary software. Personal non-commercial use only; redistribution/repackaging/forks/reuse require permission.
-- Full license: Core.lua and LICENSE.txt

local T=Transmoggin


local function closePlayerBags()
    if CloseAllBags then pcall(CloseAllBags) end

    -- Popular bag addons often bypass Blizzard's OpenAllBags path and show their
    -- own top-level windows directly.  While the salon owns the transmogrifier,
    -- quietly hide only recognized bag windows.  Nothing is touched outside
    -- vendor mode.
    local knownFrames={
        "BagnonInventory1", "BagnonInventory", "BagnonFrameinventory",
        "AdiBagsContainer1", "AdiBagsContainer2",
        "ArkInventoryFrame1", "ArkInventoryFrame2",
        "BetterBagsBag", "BetterBagsBank",
        "Baganator_SingleViewBackpackViewFrame",
        "Baganator_CategoryViewBackpackViewFrame",
    }
    for _,name in ipairs(knownFrames) do
        local f=_G[name]
        if f and f.IsShown and f:IsShown() and f.Hide then pcall(f.Hide,f) end
    end

    -- Use public close APIs when an installed bag addon exposes one.  These are
    -- intentionally discovered at runtime so Transmoggin has no dependency on
    -- any particular bag addon.
    local closers={
        _G.Bagnon and _G.Bagnon.Frames and _G.Bagnon.Frames.Hide,
        _G.ArkInventory and _G.ArkInventory.Frame_Hide,
    }
    for _,fn in ipairs(closers) do if type(fn)=="function" then pcall(fn) end end
end

function T:InstallVendorBagGuard()
    if self._vendorBagGuardInstalled then return end
    self._vendorBagGuardInstalled=true

    -- Blizzard's transmog UI may request the bags after the interaction event.
    -- Let Blizzard initialize normally, then immediately reverse any bag-open request
    -- while Transmoggin owns the transmogrifier interaction.
    local function guard()
        if T.state and T.state.vendorOpen and C_Timer and C_Timer.After then
            C_Timer.After(0,closePlayerBags)
        end
    end
    if hooksecurefunc then
        if type(OpenAllBags)=="function" then hooksecurefunc("OpenAllBags",guard) end
        if type(ToggleAllBags)=="function" then hooksecurefunc("ToggleAllBags",guard) end
        if type(ToggleBackpack)=="function" then hooksecurefunc("ToggleBackpack",guard) end
        if type(OpenBackpack)=="function" then hooksecurefunc("OpenBackpack",guard) end
        if type(OpenBag)=="function" then hooksecurefunc("OpenBag",guard) end
        if type(ToggleBag)=="function" then hooksecurefunc("ToggleBag",guard) end
    end
end

function T:StartVendorBagGuard()
    self:InstallVendorBagGuard()
    closePlayerBags()

    -- Cover delayed bag opens performed by Blizzard's vendor setup.  This ticker
    -- expires automatically and does not interfere with bags outside Salon Mode.
    if self._vendorBagTicker then
        pcall(function() self._vendorBagTicker:Cancel() end)
        self._vendorBagTicker=nil
    end
    if C_Timer and C_Timer.NewTicker then
        local ticks=0
        -- Bag addons normally finish their vendor reaction within ~1 second. Six
        -- checks at 0.25s avoids the previous 40 CloseAllBags calls per vendor open.
        self._vendorBagTicker=C_Timer.NewTicker(0.25,function(ticker)
            ticks=ticks+1
            if not T.state.vendorOpen or ticks>=6 then
                ticker:Cancel()
                if T._vendorBagTicker==ticker then T._vendorBagTicker=nil end
                return
            end
            closePlayerBags()
        end)
    elseif C_Timer and C_Timer.After then
        for _,delay in ipairs({0.05,0.15,0.30,0.60,1.0,1.5,2.0,3.0}) do
            C_Timer.After(delay,function() if T.state.vendorOpen then closePlayerBags() end end)
        end
    end
end

local function getBlizzardFrame()
    return _G.TransmogFrame or _G.WardrobeTransmogFrame or _G.TransmogOutfitFrame
end

function T:SuppressBlizzardVendor()
    local f=getBlizzardFrame(); if not f then return end
    if not f.__TransmogginSaved then
        f.__TransmogginSaved={alpha=f:GetAlpha(),mouse=f:IsMouseEnabled()}
        if f.HookScript then
            f:HookScript("OnShow",function(self)
                if T.state.vendorOpen then self:SetAlpha(0); self:EnableMouse(false); if T.main then T.main:Show() end end
            end)
        end
    end
    f:SetAlpha(0); f:EnableMouse(false)
end


-- Photo Mode needs a stronger form of vendor suppression than SetAlpha(0).
-- On some Classic clients, Blizzard's transmog model children can continue to
-- render even while their parent frame is transparent, which produces large
-- translucent gear silhouettes over Transmoggin's photo model.  While Photo
-- Mode is active we keep the vendor session alive but shrink the stock vendor
-- frame almost to zero so its model viewports cannot leak onto the screen.
function T:SetPhotoVendorIsolation(enabled)
    local f=getBlizzardFrame()
    if not f then return end

    if enabled then
        if f.__TransmogginPhotoIsolated then return end
        f.__TransmogginPhotoIsolated={
            scale=(f.GetScale and f:GetScale()) or 1,
            alpha=(f.GetAlpha and f:GetAlpha()) or 1,
            mouse=(f.IsMouseEnabled and f:IsMouseEnabled()) or false,
        }
        if f.SetAlpha then f:SetAlpha(0) end
        if f.EnableMouse then f:EnableMouse(false) end
        if f.SetScale then pcall(f.SetScale,f,0.01) end
    else
        local s=f.__TransmogginPhotoIsolated
        if not s then return end
        if f.SetScale then pcall(f.SetScale,f,s.scale or 1) end
        f.__TransmogginPhotoIsolated=nil
        -- If the transmogrifier is still open, return to normal Transmoggin
        -- suppression. Otherwise restore Blizzard's original presentation.
        if T.state and T.state.vendorOpen then
            T:SuppressBlizzardVendor()
        else
            if f.SetAlpha then f:SetAlpha(s.alpha or 1) end
            if f.EnableMouse then f:EnableMouse(s.mouse~=false) end
        end
    end
end

function T:RestoreBlizzardVendor()
    local f=getBlizzardFrame(); if not f or not f.__TransmogginSaved then return end
    local s=f.__TransmogginSaved; f:SetAlpha(s.alpha or 1); f:EnableMouse(s.mouse~=false)
end

function T:EnterVendorMode(reason)
    self.state.vendorOpen=true
    self:EnsureUI()
    -- Transmoggin replaces the vendor UI; suppress Blizzard's automatic bag pop-open.
    self:StartVendorBagGuard()
    if C_Timer and C_Timer.After then
        C_Timer.After(0,function() if T.state.vendorOpen then T:SuppressBlizzardVendor(); if T.main then T.main:Show(); T:RequestRefresh(0); if T.MaybeShowFirstUseTutorial then T:MaybeShowFirstUseTutorial() end end end end)
        C_Timer.After(0.15,function()
            if T.state.vendorOpen then
                T:SuppressBlizzardVendor()
                if T.state.working and next(T.state.working.pieces or {}) then T:StageWorkingLook() end
                if T.ScheduleVendorCostRefresh then T:ScheduleVendorCostRefresh() end
            end
        end)
    else
        self:SuppressBlizzardVendor()
        if self.main then self.main:Show(); if self.MaybeShowFirstUseTutorial then self:MaybeShowFirstUseTutorial() end end
        if self.state.working and next(self.state.working.pieces or {}) then self:StageWorkingLook() end
        if self.ScheduleVendorCostRefresh then self:ScheduleVendorCostRefresh() end
    end
end

function T:LeaveVendorMode(reason)
    if self.SetPhotoVendorIsolation then self:SetPhotoVendorIsolation(false) end
    self.state.vendorOpen=false
    if self._vendorBagTicker then pcall(function() self._vendorBagTicker:Cancel() end); self._vendorBagTicker=nil end
    self:RestoreBlizzardVendor()
    if self.main and self.main:IsShown() then self.main:Hide() end
end

function T:CloseSalon()
    if self.state.vendorOpen then
        if CloseTransmogrifyFrame then pcall(CloseTransmogrifyFrame) end
        local typ=(Enum and Enum.PlayerInteractionType and Enum.PlayerInteractionType.Transmogrifier) or 24
        if C_PlayerInteractionManager and C_PlayerInteractionManager.ClearInteraction then pcall(C_PlayerInteractionManager.ClearInteraction,typ) end
        self.state.vendorOpen=false; self:RestoreBlizzardVendor()
    end
    if self.main then self.main:Hide() end
end
