local _, ns = ...
local SlickUI = ns.SlickUI

local GroupDeathAlerts = {
	States = {},
}
SlickUI.GroupDeathAlerts = GroupDeathAlerts

local MAN_DOWN_SOUND = "Interface\\AddOns\\SlickUI\\Media\\Sounds\\SlickUI_ManDown.wav"

local function ReadBoolean(func, ...)
	if type(func) ~= "function" then return nil end
	local ok, value = pcall(func, ...)
	if not ok then return nil end
	if canaccessvalue and not canaccessvalue(value) then return nil end
	if issecretvalue and issecretvalue(value) then return nil end
	if value == true then return true end
	if value == false then return false end
	return nil
end

local function ReadValue(func, ...)
	if type(func) ~= "function" then return nil end
	local ok, value = pcall(func, ...)
	if not ok then return nil end
	if canaccessvalue and not canaccessvalue(value) then return nil end
	if issecretvalue and issecretvalue(value) then return nil end
	return value
end

local function IsReadableUnitToken(unit)
	if canaccessvalue and not canaccessvalue(unit) then return false end
	if issecretvalue and issecretvalue(unit) then return false end
	return type(unit) == "string"
end

local function IsGroupMember(unit)
	if not IsReadableUnitToken(unit) or unit == "player" then return false end
	local inRaid = ReadValue(UnitInRaid, unit)
	local inParty = ReadBoolean(UnitInParty, unit)
	return inRaid ~= nil and inRaid ~= false or inParty == true
end

function GroupDeathAlerts:IsEnabled()
	local settings = SlickUI.DB and SlickUI.DB.alerts and SlickUI.DB.alerts.groupDeath
	return settings and settings.sound ~= false
end

function GroupDeathAlerts:PlaySound()
	if self:IsEnabled() then
		PlaySoundFile(MAN_DOWN_SOUND, "Master")
	end
end

function GroupDeathAlerts:UpdateUnit(unit, silent)
	if not IsGroupMember(unit) then return end
	local dead = ReadBoolean(UnitIsDeadOrGhost, unit)
	if dead == nil then return end
	-- UnitGUID can be a secret value in WoW 12.1 and therefore cannot safely
	-- be used as a Lua table key. Roster tokens are stable between roster
	-- updates, and SeedRoster clears this state whenever membership changes.
	local wasDead = self.States[unit]
	self.States[unit] = dead
	if not silent and dead and wasDead == false then
		self:PlaySound()
	end
end

function GroupDeathAlerts:SeedRoster()
	wipe(self.States)
	if IsInRaid() then
		for index = 1, GetNumGroupMembers() do
			self:UpdateUnit("raid" .. index, true)
		end
	elseif IsInGroup() then
		for index = 1, GetNumSubgroupMembers() do
			self:UpdateUnit("party" .. index, true)
		end
	end
end

function GroupDeathAlerts:Initialize()
	local events = CreateFrame("Frame")
	events:RegisterEvent("PLAYER_ENTERING_WORLD")
	events:RegisterEvent("GROUP_ROSTER_UPDATE")
	events:RegisterEvent("UNIT_HEALTH")
	events:RegisterEvent("UNIT_CONNECTION")
	events:SetScript("OnEvent", function(_, event, unit)
		if event == "PLAYER_ENTERING_WORLD" or event == "GROUP_ROSTER_UPDATE" then
			GroupDeathAlerts:SeedRoster()
		else
			GroupDeathAlerts:UpdateUnit(unit, false)
		end
	end)
	self:SeedRoster()
end

SlickUI:RegisterModule("GroupDeathAlerts", GroupDeathAlerts)
