-- This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License.
-- To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/
-- or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.

local MereHealingFrames, privateVars = ...

MereHealingFrames.BuffManager = {}
MereHealingFrames.BuffManager.PlayerBuffs = {}

MereHealingFrames.BuffManager.BlockedBuff = {}
MereHealingFrames.BuffManager.CleansableBuff = {}
MereHealingFrames.BuffManager.PriorityBuff = {}

MereHealingFrames.BuffManager.TickingTrackers = {}

local TickingTrackers = MereHealingFrames.BuffManager.TickingTrackers
local TotalTickers = 0

local InspectTimeFrame = Inspect.Time.Frame

local function FrameTick()
    MereHealingFrames.Events.BumpEventCounter("Event.System.Update.Begin")
    local currentTime = InspectTimeFrame()
    for tracker, value in pairs(TickingTrackers) do
        if value then
            tracker:Tick(currentTime)
        end
    end
end

local function ReferenceTickEvent()
    if (TotalTickers == 0) then
        Command.Event.Attach(Event.System.Update.Begin, FrameTick, "MereHealingFrame:FrameTick");
    end
    TotalTickers = TotalTickers + 1
end

local function DeReferenceTickEvent()
    TotalTickers = TotalTickers - 1

    if (TotalTickers == 0) then
        Command.Event.Detach(Event.System.Update.Begin, FrameTick, "MereHealingFrame:FrameTick", nil, "MereHealingFrames");
    end
end

function MereHealingFrames.BuffManager:RegisterForTick(tracker)
    if (not TickingTrackers[tracker]) then
        TickingTrackers[tracker] = true
        ReferenceTickEvent()
    end
end

function MereHealingFrames.BuffManager:DeregisterForTick(tracker)
    if TickingTrackers[tracker] then
        TickingTrackers[tracker] = nil
        DeReferenceTickEvent()
    end
end

local PlayerBuffs = MereHealingFrames.BuffManager.PlayerBuffs

function PlayerBuffs:new(playerInfo)
	local this = {}
	setmetatable(this, self)
	self.__index = self

	this.playerInfo = playerInfo
	this.BuffTracking = {}
	this.NameTracking = {}
	this.CleanseTracking = {}
	this.UIHandlers = {}
	this.BuffTrackers = {}
	this:RefreshBuffSet()
	return this
end

function PlayerBuffs:RefreshBuffSet()
	local newBuffSet = MereHealingFrames.BuffSets.CurrentSet
	-- first have to clear out all the current UI
	MereHealingFrames.Debug(2, "Updating with new buffs")

	if self.playerInfo.isFake then
		return
	end

	for i=1, 20 do
		local Handlers = self.UIHandlers[i] or {}
		for _, BuffIcon in pairs(Handlers) do
			BuffIcon:Clear()
		end
	end
	-- then clear down the internal tables
	for slot, tracker in pairs(self.BuffTrackers) do
        MereHealingFrames.BuffManager:DeregisterForTick(tracker)
	end
	self.BuffTracking = {}
	self.NameTracking = {}
	self.CleanseTracking = {}
	self.BuffTrackers = {}

	-- then build new tables
	for trackerName, buffConfig in pairs(newBuffSet.Trackers) do
		if buffConfig.buffType == "priority" then
			local slots = buffConfig.buffSlots or {}
			local tracker = MereHealingFrames.BuffManager.PriorityBuff:new(self, buffConfig.buffList, buffConfig.debuffList, buffConfig.debuffsOverBuffs, buffConfig.buffCaster, buffConfig.debuffCaster, slots)
			for i, value in pairs(slots) do
				if value then
					self.BuffTrackers[i] = tracker
				end
			end
		elseif buffConfig.buffType == "cleansable" then
			local slots = buffConfig.buffSlots or {}
			local tracker = MereHealingFrames.BuffManager.CleansableBuff:new(self, slots)
			for i, value in pairs(slots) do
				if value then
					self.BuffTrackers[i] = tracker
				end
			end
		end
	end

	-- and push any existing buffs into the system
	local buffs = Inspect.Buff.List(self.playerInfo.unitId)
	self:BuffAdd(buffs)
end

function PlayerBuffs:ResetBuffs()
	for buffId, buffTracker in pairs(self.BuffTracking or {}) do
		for _, tracker in ipairs(buffTracker or {}) do
			tracker:BuffRemove(buffId)
		end
	end

	self.BuffTracking = {}

	local buffs = Inspect.Buff.List(self.playerInfo.unitId)
	self:BuffAdd(buffs)
end

function PlayerBuffs:BuffAdd(buffs)
	if not buffs then return end

	local buffDetails = Inspect.Buff.Detail(self.playerInfo.unitId, buffs)
	for buffId, buffDetail in pairs(buffDetails) do

		if self.BuffTracking[buffId] then
			-- this buff is already tracked, so nothing to do here
			break
		end
		MereHealingFrames.BuffCache.UpdateCache(buffDetail)

		local trackers = self.NameTracking[buffDetail.name] or {}
		for i, tracker in ipairs(trackers) do
			tracker:BuffAdd(buffId, buffDetail)
		end

		if buffDetail.curse or buffDetail.disease or buffDetail.poison then
			self:CleansableBuffAdd(buffId, buffDetail)
		end
	end
end

function PlayerBuffs:RegisterBuffId(buffId, tracker)
	local buffTracking = self.BuffTracking[buffId] or {}
	table.insert(buffTracking, tracker)
	self.BuffTracking[buffId] = buffTracking
end

function PlayerBuffs:BuffChange(buffs)
	local buffDetails = Inspect.Buff.Detail(self.playerInfo.unitId, buffs)
	for buffId, buffDetail in pairs(buffDetails) do
		for id, tracker in ipairs(self.BuffTracking[buffId] or {}) do
			tracker:BuffChange(buffId, buffDetail)
		end
	end
end

function PlayerBuffs:BuffRemove(buffs)
	for buffId, v in pairs(buffs) do
		for id, tracker in ipairs(self.BuffTracking[buffId] or {}) do
			tracker:BuffRemove(buffId)
		end
		self.BuffTracking[buffId] = nil
	end
end

function PlayerBuffs:CleansableBuffAdd(buffId, buffDetail)
	for buffSetId, cleanseTracker in pairs(self.CleanseTracking or {}) do
		cleanseTracker:BuffAdd(buffId, buffDetail)
	end
end

function PlayerBuffs:AddUIBuff(buffIcon, buffSetId)
	local handlers = self.UIHandlers[buffSetId] or {}
	handlers[buffIcon] = buffIcon
	self.UIHandlers[buffSetId] = handlers

	if self.BuffTrackers[buffSetId] then
		self.BuffTrackers[buffSetId]:UpdateIcons()
	end
end

function PlayerBuffs:RemoveUIBuff(buffIcon, buffSetId)
	local handlers = self.UIHandlers[buffSetId]
    if handlers == nil then return end

    handlers[buffIcon] = nil
end

function PlayerBuffs:Tick(currentTime)
	for key, buffTracker in pairs(self.BuffTrackers) do
		buffTracker:Tick(currentTime)
	end
end