--[[----------------------------------------------------------
	Srendarr - Aura (Buff & Debuff) Tracker
	----------------------------------------------------------
	*
	* Version 2.1.6
	* Kith, Garkin, silentgecko
	*
	*
]]--
local Srendarr				= _G['Srendarr'] -- grab addon table from global
local L						= Srendarr:GetLocale()

Srendarr.name				= 'Srendarr'
Srendarr.slash				= '/srendarr'
Srendarr.version			= '2.1.6'
Srendarr.versionDB			= 3

Srendarr.displayFrames		= {}
Srendarr.displayFramesScene	= {}

Srendarr.slotData			= {}

Srendarr.auraLookup			= {
	['player']				= {},
	['reticleover']			= {},
	['groundaoe']			= {}
}

Srendarr.auraGroupStrings = {		-- used in several places to display the aura grouping text
	[Srendarr.GROUP_PLAYER_SHORT]	= L.Group_Player_Short,
	[Srendarr.GROUP_PLAYER_LONG]	= L.Group_Player_Long,
	[Srendarr.GROUP_PLAYER_TOGGLED]	= L.Group_Player_Toggled,
	[Srendarr.GROUP_PLAYER_PASSIVE]	= L.Group_Player_Passive,
	[Srendarr.GROUP_PLAYER_DEBUFF]	= L.Group_Player_Debuff,
	[Srendarr.GROUP_PLAYER_GROUND]	= L.Group_Player_Ground,
	[Srendarr.GROUP_PLAYER_MAJOR]	= L.Group_Player_Major,
	[Srendarr.GROUP_PLAYER_MINOR]	= L.Group_Player_Minor,
	[Srendarr.GROUP_TARGET_BUFF]	= L.Group_Target_Buff,
	[Srendarr.GROUP_TARGET_DEBUFF]	= L.Group_Target_Debuff,
	[Srendarr.GROUP_PROMINENT]		= L.Group_Prominent
}

Srendarr.uiLocked			= true -- flag for whether the UI is current drag enabled


-- ------------------------
-- ADDON INITIALIZATION
-- ------------------------
function Srendarr.OnInitialize(code, addon)
	if (addon ~= Srendarr.name) then return end

	local self = Srendarr

	EVENT_MANAGER:UnregisterForEvent(self.name, EVENT_ADD_ON_LOADED)
	SLASH_COMMANDS[self.slash] = self.SlashCommand

	self.db = ZO_SavedVars:NewAccountWide('SrendarrDB', self.versionDB, nil, self:GetDefaults())

	if (not self.db.useAccountWide) then -- not using global settings, generate (or load) character specific settings
		self.db = ZO_SavedVars:New('SrendarrDB', self.versionDB, nil, self:GetDefaults())
	end

	local displayBase

	-- create display frames
	for x = 1, self.NUM_DISPLAY_FRAMES do
		displayBase = self.db.displayFrames[x].base

		self.displayFrames[x] = self.DisplayFrame:Create(x, displayBase.point, displayBase.x, displayBase.y, displayBase.alpha, displayBase.scale)
		self.displayFrames[x]:Configure()

		-- add each frame to the ZOS scene manager to control visibility
		self.displayFramesScene[x] = ZO_HUDFadeSceneFragment:New(self.displayFrames[x], 0, 0)

		HUD_SCENE:AddFragment(self.displayFramesScene[x])
		HUD_UI_SCENE:AddFragment(self.displayFramesScene[x])
		SIEGE_BAR_SCENE:AddFragment(self.displayFramesScene[x])

		self.displayFrames[x]:SetHandler('OnEffectivelyShown', function(f)
			f:SetAlpha(f.displayAlpha) -- ensure alpha is reset after a scene fade
		end)
	end

	self:PopulateFilteredAuras()	-- AuraData.lua
	self:ConfigureAuraFadeTime()	-- Aura.lua
	self:InitializeAuraControl()	-- AuraControl.lua
	self:InitializeCastBar()		-- CastBar.lua
	self:InitializeProcs()			-- Procs.lua
	self:InitializeSettings()		-- Settings.lua

	-- setup events to handle actionbar slotted abilities (used for procs and the castbar)
	for slot = 3, 8 do
		self.slotData[slot] = {}
		self.OnActionSlotUpdated(nil, slot) -- populate initial data (before events registered so no triggers before setup is done)
	end

	EVENT_MANAGER:RegisterForEvent(self.name, EVENT_ACTION_SLOTS_FULL_UPDATE,	self.OnActionSlotsFullUpdate)
	EVENT_MANAGER:RegisterForEvent(self.name, EVENT_ACTION_SLOT_UPDATED,		self.OnActionSlotUpdated)
end

function Srendarr.SlashCommand(text)
	if (text == 'lock') then
		for x = 1, Srendarr.NUM_DISPLAY_FRAMES do
			Srendarr.displayFrames[x]:DisableDragOverlay()
		end

		Srendarr.Cast:DisableDragOverlay()

		Srendarr.uiLocked = true
	elseif (text == 'unlock') then
		for x = 1, Srendarr.NUM_DISPLAY_FRAMES do
			Srendarr.displayFrames[x]:EnableDragOverlay()
		end

		Srendarr.Cast:EnableDragOverlay()

		Srendarr.uiLocked = false
	else
		CHAT_SYSTEM:AddMessage(L.Usage)
	end
end


-- ------------------------
-- SLOTTED ABILITY DATA HANDLING
-- ------------------------
do
	local GetSlotBoundId		= GetSlotBoundId
	local GetAbilityName		= GetAbilityName
	local GetAbilityCastInfo	= GetAbilityCastInfo
	local GetAbilityIcon		= GetAbilityIcon
	local procAbilityNames		= Srendarr.procAbilityNames

	local abilityID, abilityName, slotData, isChannel, castTime, channelTime

	function Srendarr.OnActionSlotsFullUpdate()
		for slot = 3, 8 do
			Srendarr.OnActionSlotUpdated(nil, slot)
		end
	end

	function Srendarr.OnActionSlotUpdated(evt, slot)
		if (slot < 3 or slot > 8) then return end -- abort if not a main ability (or ultimate)

		abilityID	= GetSlotBoundId(slot)
		slotData	= Srendarr.slotData[slot]

		if (slotData.abilityID == abilityID) then return end -- nothing has changed, abort

		abilityName				= GetAbilityName(abilityID)

		slotData.abilityID		= abilityID
		slotData.abilityName	= abilityName
		slotData.abilityIcon	= GetAbilityIcon(abilityID)

		isChannel, castTime, channelTime = GetAbilityCastInfo(abilityID)

		if (castTime > 0 or channelTime > 0) then
			slotData.isDelayed		= true			-- check for needing a cast bar
			slotData.isChannel		= isChannel
			slotData.castTime		= castTime
			slotData.channelTime	= channelTime
		else
			slotData.isDelayed		= false
		end

		if (procAbilityNames[abilityName]) then -- this is currently a proc'd ability (or special case for crystal fragments)
			Srendarr:ProcAnimationStart(slot)
		elseif (slot ~= 8) then -- cannot have procs on ultimate slot
			Srendarr:ProcAnimationStop(slot)
		end
	end
end


-- ------------------------
-- BLACKLIST AND PROMINENT AURAS CONTROL
do ------------------------
	local str_gsub				= string.gsub

	local DoesAbilityExist		= DoesAbilityExist
	local GetAbilityName		= GetAbilityName
	local GetAbilityDuration	= GetAbilityDuration
	local GetAbilityDescription	= GetAbilityDescription
	local IsAbilityPassive		= IsAbilityPassive

	local fakeAuras				= Srendarr.fakeAuras

	function Srendarr:ProminentAuraAdd(auraName)
		auraName = str_gsub(auraName, '%^%a?', '') -- strip out any control characters player may have entered

		if (self.db.prominentWhitelist[auraName]) then return end -- already added this aura

		local matchedIDs = {}
		local compareName

		for id = 1, 100000 do -- scan all abilityIDs looking for this auraName
			if (DoesAbilityExist(id) and (GetAbilityDuration(id) > 0 or not IsAbilityPassive(id))) then
				compareName = str_gsub(GetAbilityName(id), '%^%a?', '') -- strip out any control characters from the ability name

				if (compareName == auraName) then -- matching ability with a duration (no toggles or passives in prominence)
					table.insert(matchedIDs, id)
				end
			end
		end

		if (fakeAuras[auraName]) then -- a fake aura exists for this ability, add its ID
			table.insert(matchedIDs, fakeAuras[auraName].abilityID)
		end

		if (#matchedIDs > 0) then -- matches were found
			self.db.prominentWhitelist[auraName] = {} -- add a new whitelist entry

			for _, id in ipairs(matchedIDs) do
				self.db.prominentWhitelist[auraName][id] = true
			end

			Srendarr:ConfigureAuraHandler() -- update handler ref

			CHAT_SYSTEM:AddMessage(string.format('%s: %s %s', L.Srendarr, auraName, L.Prominent_AuraAddSuccess)) -- inform user of successful addition
		else
			CHAT_SYSTEM:AddMessage(string.format('%s: %s %s', L.Srendarr, auraName, L.Prominent_AuraAddFail)) -- inform user of failed addition
		end
	end

	function Srendarr:ProminentAuraRemove(auraName)
		auraName = str_gsub(auraName, '%^%a?', '') -- strip out any control characters player may have entered

		if (not self.db.prominentWhitelist[auraName]) then return end -- not in whitelist, abort

		for id in pairs(self.db.prominentWhitelist[auraName]) do
			self.db.prominentWhitelist[auraName][id] = nil -- clean out whitelist entry
		end

		self.db.prominentWhitelist[auraName] = nil -- remove whitelist entrys

		Srendarr:ConfigureAuraHandler() -- update handler ref

		CHAT_SYSTEM:AddMessage(string.format('%s: %s %s', L.Srendarr, auraName, L.Prominent_AuraRemoved)) -- inform user of removal
	end

	function Srendarr:BlacklistAuraAdd(auraName)
		auraName = str_gsub(auraName, '%^%a?', '') -- strip out any control characters player may have entered

		if (self.db.blacklist[auraName]) then return end -- already added this aura

		local matchedIDs = {}
		local compareName

		for id = 1, 100000 do -- scan all abilityIDs looking for this auraName
			if (DoesAbilityExist(id)) then
				compareName = str_gsub(GetAbilityName(id), '%^%a?', '') -- strip out any control characters from the ability name

				if (compareName == auraName) then -- found a matching ability
					table.insert(matchedIDs, id)
				end
			end
		end

		if (fakeAuras[auraName]) then -- a fake aura exists for this ability, add its ID
			table.insert(matchedIDs, fakeAuras[auraName].abilityID)
		end

		if (#matchedIDs > 0) then -- matches were found
			self.db.blacklist[auraName] = {} -- add a new blacklist entry

			for _, id in ipairs(matchedIDs) do
				self.db.blacklist[auraName][id] = true
			end

			Srendarr:PopulateFilteredAuras() -- update filtered aura IDs

			CHAT_SYSTEM:AddMessage(string.format('%s: %s %s', L.Srendarr, auraName, L.Blacklist_AuraAddSuccess)) -- inform user of successful addition
		else
			CHAT_SYSTEM:AddMessage(string.format('%s: %s %s', L.Srendarr, auraName, L.Blacklist_AuraAddFail)) -- inform user of failed addition
		end
	end

	function Srendarr:BlacklistAuraRemove(auraName)
		auraName = str_gsub(auraName, '%^%a?', '') -- strip out any control characters player may have entered

		if (not self.db.blacklist[auraName]) then return end -- not in blacklist, abort

		for id in pairs(self.db.blacklist[auraName]) do
			self.db.blacklist[auraName][id] = nil -- clean out blacklist entry
		end

		self.db.blacklist[auraName] = nil -- remove blacklist entrys

		Srendarr:PopulateFilteredAuras() -- update filtered aura IDs

		CHAT_SYSTEM:AddMessage(string.format('%s: %s %s', L.Srendarr, auraName, L.Blacklist_AuraRemoved)) -- inform user of removal
	end
end


-- ------------------------
-- UI HELPER FUNCTIONS
-- ------------------------
do
	local math_abs				= math.abs
	local WM					= WINDOW_MANAGER

	function Srendarr:GetEdgeRelativePosition(object)
		local left, top     = object:GetLeft(),		object:GetTop()
		local right, bottom = object:GetRight(),	object:GetBottom()
		local rootW, rootH  = GuiRoot:GetWidth(),	GuiRoot:GetHeight()
		local point         = 0
		local x, y

		if (left < (rootW - right) and left < math_abs((left + right) / 2 - rootW / 2)) then
			x, point = left, 2 -- 'LEFT'
		elseif ((rootW - right) < math_abs((left + right) / 2 - rootW / 2)) then
			x, point = right - rootW, 8 -- 'RIGHT'
		else
			x, point = (left + right) / 2 - rootW / 2, 0
		end

		if (bottom < (rootH - top) and bottom < math_abs((bottom + top) / 2 - rootH / 2)) then
			y, point = top, point + 1 -- 'TOP|TOPLEFT|TOPRIGHT'
		elseif ((rootH - top) < math_abs((bottom + top) / 2 - rootH / 2)) then
			y, point = bottom - rootH, point + 4 -- 'BOTTOM|BOTTOMLEFT|BOTTOMRIGHT'
		else
			y = (bottom + top) / 2 - rootH / 2
		end

		point = (point == 0) and 128 or point -- 'CENTER'

		return point, x, y
	end

	function Srendarr.AddControl(parent, cType, level)
		local c = WM:CreateControl(nil, parent, cType)
		c:SetDrawLayer(DL_OVERLAY)
		c:SetDrawLevel(level)
		return c, c
	end
end


EVENT_MANAGER:RegisterForEvent(Srendarr.name, EVENT_ADD_ON_LOADED, Srendarr.OnInitialize)