--[[
Name: Banzai-1.0
Revision: $Rev: 14544 $
Author(s): Rabbit (rabbit.magtheridon@gmail.com), maia
Documentation: http://www.wowace.com/index.php/Banzai-1.0_API_Documentation
SVN: http://svn.wowace.com/root/trunk/BanzaiLib/Banzai-1.0
Description: Aggro notification library.
Dependencies: AceLibrary, AceEvent-2.0, RosterLib-2.0
]]

-------------------------------------------------------------------------------
-- Locals
-------------------------------------------------------------------------------

local MAJOR_VERSION = "Banzai-1.0"
local MINOR_VERSION = "$Revision: 14544 $"

if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
if not AceLibrary:HasInstance("RosterLib-2.0") then error(MAJOR_VERSION .. " requires RosterLib-2.0.") end
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end

local lib = {}
AceLibrary("AceEvent-2.0"):embed(lib)

local RL = nil
local roster = nil
local playerName = nil

-------------------------------------------------------------------------------
-- Compost Heap, courtesy of Tekkub/SEEA.
-------------------------------------------------------------------------------

local table_setn
do
	local version = GetBuildInfo()
	if string.find(version, "^2%.") then
		-- 2.0.0
		table_setn = function() end
	else
		table_setn = table.setn
	end
end

local heap = {}
setmetatable(heap, {__mode = "kv"})

local function acquire()
	local t = next(heap)
	if t then
		heap[t] = nil
		assert(not next(t), "A table in the compost heap has been modified!")
	end
	t = t or {}
	return t
end


local function reclaim(t, d)
	if type(t) ~= "table" then return end
	if d and d > 0 then
		for i in pairs(t) do
			if type(t[i]) == "table" then reclaim(t[i], d - 1) end
		end
	end
	for i in pairs(t) do t[i] = nil end
	table_setn(t, 0)
	heap[t] = true
end

-------------------------------------------------------------------------------
-- Initialization
-------------------------------------------------------------------------------

-- Activate a new instance of this library
function activate(self, oldLib, oldDeactivate)
	if oldLib then
		self.vars = oldLib.vars
		if oldLib:IsEventScheduled("UpdateAggroList") then
			oldLib:CancelScheduledEvent("UpdateAggroList")
		end
	end

	RL = AceLibrary("RosterLib-2.0")
	roster = RL.roster
	playerName = UnitName("player")
	self:ScheduleRepeatingEvent("UpdateAggroList", self.UpdateAggroList, 0.2, self)

	if oldDeactivate then oldDeactivate(oldLib) end
end

-------------------------------------------------------------------------------
-- Library
-------------------------------------------------------------------------------

function lib:UpdateAggroList()
	local oldBanzai = acquire()

	for i, unit in pairs(roster) do
		oldBanzai[unit.unitid] = unit.banzai

		-- deduct aggro for all, increase it later for everyone with aggro
		if not unit.banzaiModifier then unit.banzaiModifier = 0 end
		unit.banzaiModifier = math.max(0, unit.banzaiModifier - 5)

		-- check for aggro
		local targetId = unit.unitid .. "target"
		local targetName = UnitName(targetId .. "target")
		if roster[targetName] and UnitCanAttack("player", targetId) and UnitCanAttack(targetId, "player") then
			if not roster[targetName].banzaiModifier then roster[targetName].banzaiModifier = 0 end
			roster[targetName].banzaiModifier = roster[targetName].banzaiModifier + 10
			if not roster[targetName].banzaiTarget then roster[targetName].banzaiTarget = acquire() end
			table.insert(roster[targetName].banzaiTarget, targetId)
		end

		-- cleanup
		unit.banzaiModifier = math.max(0, unit.banzaiModifier)
		unit.banzaiModifier = math.min(25, unit.banzaiModifier)

		-- set aggro
		unit.banzai = (unit.banzaiModifier > 15)
	end

	for i, unit in pairs(roster) do
		if oldBanzai[unit.unitid] ~= nil and oldBanzai[unit.unitid] ~= unit.banzai then
			-- Aggro status has changed.
			if unit.banzai == true and unit.banzaiTarget then
				-- Unit has aggro
				self:TriggerEvent("Banzai_UnitGainedAggro", unit.unitid, unit.banzaiTarget)
				if unit.name == playerName then
					self:TriggerEvent("Banzai_PlayerGainedAggro", unit.banzaiTarget)
				end
			elseif unit.banzai == false then
				-- Unit lost aggro
				self:TriggerEvent("Banzai_UnitLostAggro", unit.unitid)
				if unit.name == playerName then
					self:TriggerEvent("Banzai_PlayerLostAggro", unit.unitid)
				end
			end
		end
		reclaim(unit.banzaiTarget)
		unit.banzaiTarget = nil
	end

	reclaim(oldBanzai)
	oldBanzai = nil
end

-------------------------------------------------------------------------------
-- API
-------------------------------------------------------------------------------

function lib:GetUnitAggroByUnitId( unitId )
	local rosterUnit = RL:GetUnitObjectFromUnit(unitId)
	if not rosterUnit then return nil end
	return rosterUnit.banzai
end

function lib:GetUnitAggroByUnitName( unitName )
	local rosterUnit = RL:GetUnitObjectFromName(unitName)
	if not rosterUnit then return nil end
	return rosterUnit.banzai
end

-------------------------------------------------------------------------------
-- Register
-------------------------------------------------------------------------------
AceLibrary:Register(lib, MAJOR_VERSION, MINOR_VERSION, activate)

