--NOBODY, except Keseva touches this file.

-- globals
DBM.Nameplate = {}
-- locals
local nameplateFrame = DBM.Nameplate
local units = {}
local num_units = 0
local playerName, playerGUID = UnitName("player"), UnitGUID("player")--Cache these, they never change
local GetNamePlateForUnit, GetNamePlates = C_NamePlate.GetNamePlateForUnit, C_NamePlate.GetNamePlates
local twipe, floor = table.wipe, math.floor

--------------------
--  Create Frame  --
--------------------
local DBMNameplateFrame = CreateFrame("Frame", "DBMNameplate", UIParent)
DBMNameplateFrame:SetFrameStrata('BACKGROUND')
DBMNameplateFrame:Hide()

--------------------------
-- Aura frame functions --
--------------------------
local CreateAuraFrame
do
	local function AuraFrame_CreateIcon(frame)
		local icon = DBMNameplateFrame:CreateTexture(nil,'BACKGROUND',nil,0)
		icon:SetSize(DBM.Options.NPAuraSize, DBM.Options.NPAuraSize)
		icon:Hide()

		tinsert(frame.icons,icon)

		return icon
	end
	local function AuraFrame_GetIcon(frame,texture)
		-- find unused icon or create new icon
		if not frame.icons or #frame.icons == 0 then
			return frame:CreateIcon()
		else
			if frame.texture_index[texture] then
				-- return icon already used for this texture
				return frame.texture_index[texture]
			else
				-- find unused icon:
				for _, icon in ipairs(frame.icons) do
					if not icon:IsShown() then
						return icon
					end
				end

				-- create new icon
				return frame:CreateIcon()
			end
		end
	end
	local function AuraFrame_ArrangeIcons(frame)
		if not frame.icons or #frame.icons == 0 then return end

		local prev,total_width,first_icon
		for _, icon in ipairs(frame.icons) do
			if icon:IsShown() then
				icon:ClearAllPoints()

				if not prev then
					total_width = 0
					first_icon = icon
					icon:SetPoint('BOTTOM',frame.parent,'TOP')
				else
					total_width = total_width + icon:GetWidth()
					icon:SetPoint('LEFT',prev,'RIGHT')
				end

				prev = icon
			end
		end

		if first_icon and total_width and total_width > 0 then
			-- shift first icon back to centre visible row
			first_icon:SetPoint('BOTTOM',frame.parent,'TOP',
				-floor(total_width/2),0)
		end
	end
	local function AuraFrame_AddAura(frame,aura_tbl)
		if not frame.icons then
			frame.icons = {}
		end
		if not frame.texture_index then
			frame.texture_index = {}
		end

		local icon = frame:GetIcon(aura_tbl.texture)
		icon:SetTexture(aura_tbl.texture)
		icon:Show()

		frame.texture_index[aura_tbl.texture] = icon
		frame:ArrangeIcons()
	end
	local function AuraFrame_RemoveAura(frame,texture,batch)
		if not texture then return end
		if not frame.texture_index then return end

		local icon = frame.texture_index[texture]
		if not icon then return end

		icon:Hide()
		frame.texture_index[texture] = nil

		if not batch then
			frame:ArrangeIcons()
		end
	end
	local function AuraFrame_RemoveAll(frame)
		if not frame.icons or not frame.texture_index then return end

		for texture,_ in pairs(frame.texture_index) do
			frame:RemoveAura(texture,true)
		end
		twipe(frame.texture_index)
	end

	local auraframe_proto = {
		CreateIcon = AuraFrame_CreateIcon,
		GetIcon = AuraFrame_GetIcon,
		ArrangeIcons = AuraFrame_ArrangeIcons,
		AddAura = AuraFrame_AddAura,
		RemoveAura = AuraFrame_RemoveAura,
		RemoveAll = AuraFrame_RemoveAll,
	}
	auraframe_proto.__index = auraframe_proto

	function CreateAuraFrame(frame)
		if not frame then return end

		local new_frame = {}
		setmetatable(new_frame, auraframe_proto)
		new_frame.parent = frame

		return new_frame
	end
end
-------------------------
-- Nameplate functions --
-------------------------
local function Nameplate_OnHide(frame)
	if not frame then return end
	frame.DBMAuraFrame:RemoveAll()
end
local function HookNameplate(frame)
	if not frame then return end
	frame.DBMAuraFrame = CreateAuraFrame(frame)
	frame:HookScript('OnHide',Nameplate_OnHide)
end

local function Nameplate_AutoHide(self, isGUID, unit, spellId, texture)
	self:Hide(isGUID, unit, spellId, texture)
end

local function Nameplate_UnitAdded(frame,unit)
	if not frame or not unit then return end

	if not frame.DBMAuraFrame then
		-- hook as required;
		HookNameplate(frame)
	end

	local unitName = DBM:GetUnitFullName(unit)
	local guid = UnitGUID(unit)
	local unit_tbl

	if guid and units[guid] then
		unit_tbl = units[guid]
	elseif unitName and units[unitName] then
		unit_tbl = units[unitName]
	end

	if unit_tbl and #unit_tbl > 0 then
		for _,aura_tbl in ipairs(unit_tbl) do
			frame.DBMAuraFrame:AddAura(aura_tbl)
		end
	end
end
----------------
--  On Event  --
----------------
DBMNameplateFrame:SetScript("OnEvent", function(_, event, ...)
	if event == 'NAME_PLATE_UNIT_ADDED' then
		local unit = ...
		if not unit then return end
		local f = GetNamePlateForUnit(unit)
		if not f then return end

		Nameplate_UnitAdded(f,unit)
	end
end)

-----------------
--  Functions  --
-----------------
--/run DBM:FireEvent("BossMod_EnableHostileNameplates")
--/run DBM.Nameplate:Show(false, GetUnitName("target", true), 227723)--Mana tracking, easy to find in Dalaran
--/run DBM.Nameplate:Hide(true, nil, nil, nil, true)
--/run DBM.Nameplate:Hide(true, UnitGUID("target"), 227723)
--/run DBM.Nameplate:Hide(false, GetUnitName("target", true), 227723)

--Add more nameplate mods as they gain support
function nameplateFrame:SupportedNPMod()
	if not DBM.Options.UseNameplateHandoff then return false end
	if _G["KuiNameplates"] or _G["TidyPlatesThreatDBM"] or _G["Plater"] then return true end
	return false
end

--isGUID: guid or name (bool)
--ie, anchored to UIParent Center (ie player is in center) and to bottom of nameplate aura.
function nameplateFrame:Show(isGUID, unit, spellId, texture, duration, desaturate)
	-- nameplate icons are disabled;
	if DBM.Options.DontShowNameplateIcons then return end

	-- ignore player nameplate;
	if playerGUID == unit or playerName == unit then return end

	--Texture Id passed as string so as not to get confused with spellID for GetSpellTexture
	local currentTexture = tonumber(texture) or texture or GetSpellTexture(spellId)

	-- Supported by nameplate mod, passing to their handler;
	if self:SupportedNPMod() then
		DBM:FireEvent("BossMod_ShowNameplateAura", isGUID, unit, currentTexture, duration, desaturate)
		DBM:Debug("DBM.Nameplate Found supported NP mod, only sending Show callbacks", 3)
		return
	end

	--Not running supported NP Mod, internal handling
	if not self:IsShown() then
		DBMNameplateFrame:Show()
		DBMNameplateFrame:RegisterEvent("NAME_PLATE_UNIT_ADDED")
		DBM:Debug("DBM.Nameplate Enabling", 2)
	end

	if not units[unit] then
		units[unit] = {}
		num_units = num_units + 1
	end

	tinsert(units[unit], {
		texture = currentTexture
	})

	-- find frame for this unit;
	if not isGUID then
		local frame = GetNamePlateForUnit(unit)
		if frame then
			Nameplate_UnitAdded(frame, unit)
			if duration then
				DBM:Schedule(duration, Nameplate_AutoHide, self, isGUID, unit, spellId, texture)
			end
		end
	else
		--GUID, less efficient because it must scan all plates to find
		--but supports npcs/enemies
		for _, frame in pairs(GetNamePlates()) do
			local foundUnit = frame.namePlateUnitToken or (frame.UnitFrame and frame.UnitFrame.unit)
			if foundUnit and UnitGUID(foundUnit) == unit then
				Nameplate_UnitAdded(frame, foundUnit)
				if duration then
					DBM:Schedule(duration, Nameplate_AutoHide, self, isGUID, unit, spellId, texture)
				end
				break
			end
		end
	end
end

--Friendly is still being kept around for world bosses, for now anyways, but args being swapped.
function nameplateFrame:Hide(isGUID, unit, spellId, texture, force, isHostile, isFriendly)
	--Texture Id passed as string so as not to get confused with spellID for GetSpellTexture
	local currentTexture = tonumber(texture) or texture or GetSpellTexture(spellId)

	if self:SupportedNPMod() then
		DBM:Debug("DBM.Nameplate Found supported NP mod, only sending Hide callbacks", 3)

		if force then
			if isFriendly then
				DBM:FireEvent("BossMod_DisableFriendlyNameplates")
			end
			if isHostile then
				DBM:FireEvent("BossMod_DisableHostileNameplates")
			end
		elseif unit then
			DBM:FireEvent("BossMod_HideNameplateAura", isGUID, unit, currentTexture)
		end

		return
	end

	--Not running supported NP Mod, internal handling
	if unit and units[unit] then
		if currentTexture then
			for i,aura_tbl in ipairs(units[unit]) do
				if aura_tbl.texture == currentTexture then
					tremove(units[unit],i)
					break
				end
			end
		end

		if not currentTexture or #units[unit] == 0 then
			units[unit] = nil
			num_units = num_units - 1
		end
	end

	-- find frame for this unit
	-- (or hide all visible textures if force ~= nil)
	if not isGUID and not force then--Only need to find one unit
		local frame = GetNamePlateForUnit(unit)
		if frame and frame.DBMAuraFrame then
			if not currentTexture then
				frame.DBMAuraFrame:RemoveAll()
			else
				frame.DBMAuraFrame:RemoveAura(currentTexture)
			end
		end
	else
		--We either passed force, or GUID,
		--either way requires scanning all nameplates
		for _, frame in pairs(GetNamePlates()) do
			if frame.DBMAuraFrame then
				if force then
					frame.DBMAuraFrame:RemoveAll()
				elseif isGUID then
					local foundUnit = frame.namePlateUnitToken or (frame.UnitFrame and frame.UnitFrame.unit)
					if foundUnit and UnitGUID(foundUnit) == unit then
						if not currentTexture then
							frame.DBMAuraFrame:RemoveAll()
						else
							frame.DBMAuraFrame:RemoveAura(currentTexture)
						end
					end
				end
			end
		end
	end

	-- disable nameplate hooking;
	if force or num_units <= 0 then
		twipe(units)
		num_units = 0

		DBMNameplateFrame:UnregisterEvent("NAME_PLATE_UNIT_ADDED")
		DBMNameplateFrame:Hide()
		DBM:Debug("DBM.Nameplate Disabling", 2)
	end
end

function nameplateFrame:IsShown()
	return DBMNameplateFrame and DBMNameplateFrame:IsShown()
end
