local _, ns = ...
local oUF = ns.oUF

-- sourced from Blizzard_UnitFrame/Mainline/TargetFrame.lua
local MAX_BOSS_FRAMES = _G.MAX_BOSS_FRAMES or 5

-- sourced from Blizzard_FrameXMLBase/Shared/Constants.lua
local MEMBERS_PER_RAID_GROUP = _G.MEMBERS_PER_RAID_GROUP or 5

local hookedFrames = {}
local suppressedFrames = {}
local frameStates = {}
local isArenaHooked = false
local isBossHooked = false
local isPartyHooked = false

local function handleFrame(baseName, doNotReparent)
	local frame
	if(type(baseName) == 'string') then
		frame = _G[baseName]
	else
		frame = baseName
	end

	if(frame) then
		if(not frameStates[frame]) then
			frameStates[frame] = {
				alpha = frame:GetAlpha(),
				mouse = frame.IsMouseEnabled and frame:IsMouseEnabled() or true,
			}
		end
		suppressedFrames[frame] = true
		-- Keep Blizzard's frame shown but invisible so its secure health and
		-- power bars continue receiving native updates. Hiding the parent leaves
		-- secret-valued bars blank when restored, and manually refreshing them
		-- from addon code taints Blizzard's status-bar calculations.
		frame:SetAlpha(0)
		if(frame.EnableMouse) then frame:EnableMouse(false) end

		if(not hookedFrames[frame]) then
			frame:HookScript('OnShow', function(self)
				if(suppressedFrames[self] and not InCombatLockdown()) then
					self:SetAlpha(0)
					if(self.EnableMouse) then self:EnableMouse(false) end
				end
			end)
			hookedFrames[frame] = true
		end
	end
end

local function restoreFrame(frame, shouldShow)
	if(not frame) then return end
	local state = frameStates[frame]
	suppressedFrames[frame] = nil
	frame:SetAlpha(state and state.alpha or 1)
	if(frame.EnableMouse) then frame:EnableMouse(not state or state.mouse) end
	if(shouldShow) then
		frame:Show()
	end
end

function oUF:EnableBlizzard(unit)
	if(not unit or InCombatLockdown()) then return false end
	if(unit == 'player') then
		restoreFrame(PlayerFrame, true)
	elseif(unit == 'pet') then
		restoreFrame(PetFrame, UnitExists('pet'))
	elseif(unit == 'target') then
		restoreFrame(TargetFrame, UnitExists('target'))
	elseif(unit == 'focus') then
		restoreFrame(FocusFrame, UnitExists('focus'))
	elseif(unit:match('boss%d?$')) then
		restoreFrame(BossTargetFrameContainer, true)
		for i = 1, MAX_BOSS_FRAMES do
			restoreFrame(_G['Boss' .. i .. 'TargetFrame'], UnitExists('boss' .. i))
		end
	elseif(unit:match('party%d?$')) then
		restoreFrame(PartyFrame, IsInGroup() and not IsInRaid())
		if(PartyFrame and PartyFrame.PartyMemberFramePool) then
			-- Pooled member frames are initialized and shown by PartyFrame itself.
			-- Forcing them visible can expose an incomplete frame whose tooltip
			-- data (including buffs) has not been created yet.
			for frame in PartyFrame.PartyMemberFramePool:EnumerateActive() do restoreFrame(frame, false) end
		end
		for i = 1, MEMBERS_PER_RAID_GROUP do restoreFrame(_G['CompactPartyFrameMember' .. i], false) end
	end
	return true
end

function oUF:DisableBlizzard(unit)
	if(not unit) then return end

	if(unit == 'player') then
		handleFrame(PlayerFrame)
	elseif(unit == 'pet') then
		handleFrame(PetFrame)
	elseif(unit == 'target') then
		handleFrame(TargetFrame)
	elseif(unit == 'focus') then
		handleFrame(FocusFrame)
	elseif(unit:match('boss%d?$')) then
		handleFrame(BossTargetFrameContainer)
		for i = 1, MAX_BOSS_FRAMES do
			handleFrame('Boss' .. i .. 'TargetFrame', true)
		end
	elseif(unit:match('party%d?$')) then
		handleFrame(PartyFrame)
		if PartyFrame and PartyFrame.PartyMemberFramePool then
			for frame in PartyFrame.PartyMemberFramePool:EnumerateActive() do
				handleFrame(frame, true)
			end
		end
		for i = 1, MEMBERS_PER_RAID_GROUP do
			handleFrame('CompactPartyFrameMember' .. i)
		end
	elseif(unit:match('arena%d?$')) then
		if(not isArenaHooked) then
			isArenaHooked = true

			handleFrame(CompactArenaFrame)

			for _, frame in next, CompactArenaFrame.memberUnitFrames do
				handleFrame(frame, true)
			end

			-- old arena frames, they're still used for flag carriers etc in battlegrounds
			handleFrame(ArenaEnemyMatchFramesContainer)

			for _, frame in next, ArenaEnemyMatchFramesContainer.UnitFrames do
				handleFrame(frame, true)
			end
		end
	elseif(unit:match('nameplate%d?%d?%d?$')) then
		local frame = C_NamePlate.GetNamePlateForUnit(unit)
		handleFrame(frame.UnitFrame, true)
	end
end
