local _, ns = ...
local B, C, L, DB = unpack(ns)
local UF = B:GetModule("UnitFrames")

local _G = getfenv(0)
local floor, strmatch, pairs, unpack, rad = floor, string.match, pairs, unpack, math.rad
local UnitThreatSituation, UnitIsTapDenied, UnitPlayerControlled, UnitIsUnit = UnitThreatSituation, UnitIsTapDenied, UnitPlayerControlled, UnitIsUnit
local UnitReaction, UnitIsConnected, UnitIsPlayer, UnitSelectionColor = UnitReaction, UnitIsConnected, UnitIsPlayer, UnitSelectionColor
local UnitClassification, UnitExists, InCombatLockdown, UnitCanAttack = UnitClassification, UnitExists, InCombatLockdown, UnitCanAttack
local SetCVar, UIFrameFadeIn, UIFrameFadeOut = SetCVar, UIFrameFadeIn, UIFrameFadeOut
local IsInRaid, IsInGroup, UnitName = IsInRaid, IsInGroup, UnitName
local GetNumGroupMembers, GetNumSubgroupMembers, UnitGroupRolesAssigned = GetNumGroupMembers, GetNumSubgroupMembers, UnitGroupRolesAssigned
local C_NamePlate_GetNamePlateForUnit = C_NamePlate.GetNamePlateForUnit
local GetTime = GetTime
local UnitNameplateShowsWidgetsOnly = UnitNameplateShowsWidgetsOnly
local GetSpellName = C_Spell.GetSpellName
local UnitEffectiveLevel, UnitClassBase, GetInstanceInfo = UnitEffectiveLevel, UnitClassBase, GetInstanceInfo
local UnitIsBossMob, UnitIsLieutenant = UnitIsBossMob, UnitIsLieutenant

-- Instance type tracker for mob type coloring
local isInInstance = false
local function updateZoneType()
	local _, zoneType = GetInstanceInfo()
	isInInstance = (zoneType == "party" or zoneType == "raid")
end

-- Init
function UF:UpdatePlateCVars()
	SetCVar("namePlateMinScale", C.db["Nameplate"]["MinScale"])
	SetCVar("namePlateMaxScale", C.db["Nameplate"]["MinScale"])
	SetCVar("nameplateMinAlpha", C.db["Nameplate"]["MinAlpha"])
	SetCVar("nameplateMaxAlpha", C.db["Nameplate"]["MinAlpha"])
	SetCVar("nameplateOverlapV", C.db["Nameplate"]["VerticalSpacing"])
	SetCVar("nameplateShowOnlyNameForFriendlyPlayerUnits", C.db["Nameplate"]["CVarOnlyNames"] and 1 or 0)
	SetCVar("nameplateUseClassColorForFriendlyPlayerUnitNames", C.db["Nameplate"]["CVarOnlyNames"] and 1 or 0)
	SetCVar("nameplateShowFriendlyNPCs", C.db["Nameplate"]["CVarShowNPCs"] and 1 or 0)
	SetCVar("nameplateMaxDistance", C.db["Nameplate"]["PlateRange"])
end

function UF:UpdatePlateSize()
	if InCombatLockdown() then return end
	if UF.NameplateDriver then
		UF.NameplateDriver:SetSize(C.db["Nameplate"]["HarmWidth"], C.db["Nameplate"]["HarmHeight"])
		UF.NameplateDriver.enemyNonInteractible = C.db["Nameplate"]["EnemyThru"]
		UF.NameplateDriver.friendlyNonInteractible = C.db["Nameplate"]["FriendlyThru"]
	end
end

local function PurgeKey(t, k) -- code from platynator
	t[k] = nil
	local c = 42
	repeat
		if t[c] == nil then
			t[c] = nil
		end
		c = c + 1
	until issecurevariable(t, k)
end

function UF:SetupCVars()
	UF:UpdatePlateCVars()
	SetCVar("nameplateOverlapH", .8)
	SetCVar("nameplateSelectedAlpha", 1)
	SetCVar("showQuestTrackingTooltips", 1)

	SetCVar("nameplateSelectedScale", 1)
	SetCVar("nameplateLargerScale", 1)
	SetCVar("nameplateGlobalScale", 1)
	SetCVar("NamePlateHorizontalScale", 1)
	SetCVar("NamePlateVerticalScale", 1)
	SetCVar("NamePlateClassificationScale", 1)

	--SetCVar("nameplateShowSelf", 0) -- default player plate
	SetCVar("nameplateResourceOnTarget", 0)
	UF:UpdatePlateSize()
	hooksecurefunc(NamePlateDriverFrame, "UpdateNamePlateSize", UF.UpdatePlateSize)
	-- fix blizz friendly plate visibility
	SetCVar("nameplatePlayerMaxDistance", 60)
	-- hide realm name in blizz nameonly
	if NamePlateFriendlyFrameOptions then
		PurgeKey(NamePlateFriendlyFrameOptions, "updateNameUsesGetUnitName")
	end
end

-- Elements
local function refreshNameplateUnits(VALUE)
	wipe(UF[VALUE])
	if not C.db["Nameplate"]["Show"..VALUE] then return end

	for npcID in pairs(C[VALUE]) do
		if C.db["Nameplate"][VALUE][npcID] == nil then
			UF[VALUE][npcID] = true
		end
	end
	for npcID, value in pairs(C.db["Nameplate"][VALUE]) do
		if value then
			UF[VALUE][npcID] = true
		end
	end
end

UF.CustomUnits = {}
function UF:CreateUnitTable()
	refreshNameplateUnits("CustomUnits")
end

-- Update unit color
local executedCurve = C_CurveUtil.CreateColorCurve()
executedCurve:SetType(Enum.LuaCurveType.Step)

function UF:UpdateExcutedCurve()
	local executeRatio = C.db["Nameplate"]["ExecuteRatio"]
	executedCurve:ClearPoints()
	executedCurve:AddPoint(executeRatio/100, CreateColor(1, 1, 1))
	executedCurve:AddPoint(0, CreateColor(1, 0, 0))
end

function UF:UpdateColor(_, unit)
	if not unit or self.unit ~= unit then return end

	local element = self.Health
	local name = self.unitName
	local npcID = self.npcID
	local isCustomUnit = UF.CustomUnits[name] or UF.CustomUnits[npcID]
	local isPlayer = self.isPlayer
	local isFriendly = self.isFriendly
	local status = UnitThreatSituation("player", unit)
	local customColor = C.db["Nameplate"]["CustomColor"]
	local secureColor = C.db["Nameplate"]["SecureColor"]
	local transColor = C.db["Nameplate"]["TransColor"]
	local insecureColor = C.db["Nameplate"]["InsecureColor"]
	local revertThreat = C.db["Nameplate"]["DPSRevertThreat"]
	--local offTankColor = C.db["Nameplate"]["OffTankColor"]
	local healthPerc = UnitHealthPercent(unit, true, executedCurve)
	local targetColor = C.db["Nameplate"]["TargetColor"]
	local focusColor = C.db["Nameplate"]["FocusColor"]
	local dotColor = C.db["Nameplate"]["DotColor"]
	local r, g, b

	if not UnitIsConnected(unit) then
		r, g, b = .7, .7, .7
	else
		if C.db["Nameplate"]["ColoredTarget"] and UnitIsUnit(unit, "target") then
			r, g, b = targetColor.r, targetColor.g, targetColor.b
		elseif C.db["Nameplate"]["ColoredFocus"] and UnitIsUnit(unit, "focus") then
			r, g, b = focusColor.r, focusColor.g, focusColor.b
		elseif isCustomUnit then
			r, g, b = customColor.r, customColor.g, customColor.b
		elseif self.Auras.hasTheDot then
			r, g, b = dotColor.r, dotColor.g, dotColor.b
		elseif isPlayer and isFriendly then
			if C.db["Nameplate"]["FriendlyCC"] then
				r, g, b = B.UnitColor(unit)
			else
				r, g, b = .3, .3, 1
			end
		elseif isPlayer and (not isFriendly) and C.db["Nameplate"]["HostileCC"] then
			r, g, b = B.UnitColor(unit)
		elseif UnitIsTapDenied(unit) and not UnitPlayerControlled(unit) or C.TrashUnits[npcID] then
			r, g, b = .6, .6, .6
		else
			if not isPlayer and isInInstance and C.db["Nameplate"]["MobTypeColoring"] then
				local pLevel = UnitEffectiveLevel("player")
				local uLevel = UnitEffectiveLevel(unit)
				local classification = UnitClassification(unit)
				local isElite = classification == "elite" or classification == "rareelite"
				local isBoss = (uLevel == pLevel + 2 or uLevel == -1) or UnitIsBossMob(unit)
				local isLieutenant = UnitIsLieutenant(unit) or (isElite and uLevel == pLevel + 1)
				local bossColor = C.db["Nameplate"]["BossColor"]
				local lieutenantColor = C.db["Nameplate"]["LieutenantColor"]
				local casterColor = C.db["Nameplate"]["CasterColor"]
				local meleeColor = C.db["Nameplate"]["MeleeColor"]
				local trivialColor = C.db["Nameplate"]["TrivialColor"]
				if isBoss then
					if C.db["Nameplate"]["ShowBossColor"] then
						r, g, b = bossColor.r, bossColor.g, bossColor.b
					end
				elseif isLieutenant then
					if C.db["Nameplate"]["ShowLieutColor"] then
						r, g, b = lieutenantColor.r, lieutenantColor.g, lieutenantColor.b
					end
				elseif UnitClassBase(unit) == "PALADIN" then
					if C.db["Nameplate"]["ShowCasterColor"] then
						r, g, b = casterColor.r, casterColor.g, casterColor.b
					end
				elseif isElite then
					if C.db["Nameplate"]["ShowMeleeColor"] then
						r, g, b = meleeColor.r, meleeColor.g, meleeColor.b
					end
				else
					if C.db["Nameplate"]["ShowTrivialColor"] then
						r, g, b = trivialColor.r, trivialColor.g, trivialColor.b
					end
				end
			end
			if not r then
				r, g, b = UnitSelectionColor(unit, true)
			end
			if status and (C.db["Nameplate"]["TankMode"] or DB.Role == "Tank") then
				if status == 3 then
					if DB.Role ~= "Tank" and revertThreat then
						r, g, b = insecureColor.r, insecureColor.g, insecureColor.b
					else
						--if isOffTank then
						--	r, g, b = offTankColor.r, offTankColor.g, offTankColor.b
						--else
							r, g, b = secureColor.r, secureColor.g, secureColor.b
						--end
					end
				elseif status == 2 or status == 1 then
					r, g, b = transColor.r, transColor.g, transColor.b
				elseif status == 0 then
					if DB.Role ~= "Tank" and revertThreat then
						r, g, b = secureColor.r, secureColor.g, secureColor.b
					else
						r, g, b = insecureColor.r, insecureColor.g, insecureColor.b
					end
				end
			end
		end
	end

	if r or g or b then
		element:SetStatusBarColor(r, g, b)
	end

	self.ThreatIndicator:Hide()
	if status and (isCustomUnit or (not C.db["Nameplate"]["TankMode"] and DB.Role ~= "Tank")) then
		if status == 3 then
			self.ThreatIndicator:SetBackdropBorderColor(1, 0, 0)
			self.ThreatIndicator:Show()
		elseif status == 2 or status == 1 then
			self.ThreatIndicator:SetBackdropBorderColor(1, 1, 0)
			self.ThreatIndicator:Show()
		end
	end

	self.nameText:SetTextColor(healthPerc:GetRGB())
end

function UF:UpdateThreatColor(_, unit)
	if unit ~= self.unit then return end

	UF.UpdateColor(self, _, unit)
end

function UF:CreateThreatColor(self)
	local threatIndicator = B.CreateSD(self.backdrop, nil, true)
	threatIndicator:SetFrameLevel(2)
	threatIndicator:Hide()
	self.backdrop.__shadow = nil

	self.ThreatIndicator = threatIndicator
	self.ThreatIndicator.Override = UF.UpdateThreatColor
end

function UF:UpdateFocusColor()
	if C.db["Nameplate"]["ColoredFocus"] then
		UF.UpdateThreatColor(self, _, self.unit)
	end
end

-- Target indicator
function UF:UpdateTargetChange()
	local element = self.TargetIndicator
	if not element then return end

	local unit = self.unit
	if C.db["Nameplate"]["TargetIndicator"] ~= 1 then
		if UnitIsUnit(unit, "target") and not UnitIsUnit(unit, "player") then
			element:Show()
		else
			element:Hide()
		end
	end
	if C.db["Nameplate"]["ColoredTarget"] then
		UF.UpdateThreatColor(self, _, unit)
	end
end

function UF:UpdateTargetIndicator()
	local element = self.TargetIndicator
	if not element then return end

	local style = C.db["Nameplate"]["TargetIndicator"]
	local isNameOnly = self.plateType == "NameOnly"
	if style == 1 then
		element:Hide()
	else
		if style == 2 then
			element.TopArrow:Show()
			element.LeftArrow:Hide()
			element.RightArrow:Hide()
			element.Glow:Hide()
			element.nameGlow:Hide()
		elseif style == 3 then
			element.TopArrow:Hide()
			element.LeftArrow:Show()
			element.RightArrow:Show()
			element.Glow:Hide()
			element.nameGlow:Hide()
		elseif style == 4 then
			element.TopArrow:Hide()
			element.LeftArrow:Hide()
			element.RightArrow:Hide()
			if isNameOnly then
				element.Glow:Hide()
				element.nameGlow:Show()
			else
				element.Glow:Show()
				element.nameGlow:Hide()
			end
		elseif style == 5 then
			element.TopArrow:Show()
			element.LeftArrow:Hide()
			element.RightArrow:Hide()
			if isNameOnly then
				element.Glow:Hide()
				element.nameGlow:Show()
			else
				element.Glow:Show()
				element.nameGlow:Hide()
			end
		elseif style == 6 then
			element.TopArrow:Hide()
			element.LeftArrow:Show()
			element.RightArrow:Show()
			if isNameOnly then
				element.Glow:Hide()
				element.nameGlow:Show()
			else
				element.Glow:Show()
				element.nameGlow:Hide()
			end
		end
		element:Show()
	end
end

function UF:AddTargetIndicator(self)
	local frame = CreateFrame("Frame", nil, self)
	frame:SetAllPoints()
	frame:SetFrameLevel(0)
	frame:Hide()

	frame.TopArrow = frame:CreateTexture(nil, "BACKGROUND", nil, -6)
	frame.TopArrow:SetSize(52, 52)
	frame.TopArrow:SetTexture(DB.arrowTex)
	frame.TopArrow:SetVertexColor(1, 0, 0, 1)
	frame.TopArrow:SetPoint("BOTTOM", frame, "TOP", 0, 21)
	
	frame.LeftArrow = frame:CreateTexture(nil, "BACKGROUND", nil, -6)
	frame.LeftArrow:SetSize(36, 36)
	frame.LeftArrow:SetTexture(DB.arrowTex)
	frame.LeftArrow:SetVertexColor(1, 0, 0, 1)
	frame.LeftArrow:SetPoint("RIGHT", frame, "LEFT", -1, 0)
	frame.LeftArrow:SetRotation(rad(90))
	
	frame.RightArrow = frame:CreateTexture(nil, "BACKGROUND", nil, -6)
	frame.RightArrow:SetSize(36, 36)
	frame.RightArrow:SetTexture(DB.arrowTex)
	frame.RightArrow:SetVertexColor(1, 0, 0, 1)
	frame.RightArrow:SetPoint("LEFT", frame, "RIGHT", 1, 0)
	frame.RightArrow:SetRotation(rad(-90))

	frame.Glow = B.CreateSD(frame, 6, true)
	frame.Glow:SetOutside(self.backdrop, 5, 5)
	frame.Glow:SetBackdropBorderColor(1, 1, 1)
	frame.Glow:SetFrameLevel(0)

	frame.nameGlow = frame:CreateTexture(nil, "BACKGROUND", nil, -5)
	frame.nameGlow:SetSize(150, 80)
	frame.nameGlow:SetTexture("Interface\\GLUES\\Models\\UI_Draenei\\GenericGlow64")
	frame.nameGlow:SetVertexColor(0, .6, 1)
	frame.nameGlow:SetBlendMode("ADD")
	frame.nameGlow:SetPoint("CENTER", self, "BOTTOM")

	self.TargetIndicator = frame
	self:RegisterEvent("PLAYER_TARGET_CHANGED", UF.UpdateTargetChange, true)
	UF.UpdateTargetIndicator(self)
end

-- Quest progress
local isInInstance
local function CheckInstanceStatus()
	isInInstance = IsInInstance()
end

function UF:QuestIconCheck()
	CheckInstanceStatus()
	B:RegisterEvent("PLAYER_ENTERING_WORLD", CheckInstanceStatus)
end

function UF:UpdateQuestUnit(_, unit)
	if not C.db["Nameplate"]["QuestIndicator"] then return end
	if isInInstance then
		self.questIcon:Hide()
		self.questCount:SetText("")
		return
	end

	unit = unit or self.unit
	local isLootQuest, questProgress
	local prevDiff = 0

	local data = C_TooltipInfo.GetUnit(unit)
	if data then
		for i = 1, #data.lines do
			local lineData = data.lines[i]
			if lineData.type == 8 then
				local text = lineData.leftText -- progress string
				if text and B:NotSecretValue(text) then
					local current, goal = strmatch(text, "(%d+)/(%d+)")
					local progress = strmatch(text, "(%d+)%%")
					if current and goal then
						local diff = floor(goal - current)
						if diff > prevDiff then
							questProgress = diff
							prevDiff = diff
						end
					elseif progress and prevDiff == 0 then
						if floor(100 - progress) > 0 then
							questProgress = progress.."%" -- lower priority on progress, keep looking
						end
					end
				end
			end
		end
	end

	if questProgress then
		self.questCount:SetText(questProgress)
		--self.questIcon:SetTexture("Interface/WorldMap/UI-WorldMap-QuestIcon")		--self.questIcon:SetAtlas(DB.objectTex)
		--self.questIcon:SetTexCoord(0, 0.56, 0.5, 1)
		--self.questIcon:Show()
	else
		self.questCount:SetText("")
		if isLootQuest then
			self.questIcon:SetAtlas('Banker')			--self.questIcon:SetAtlas(DB.questTex)
			self.questIcon:Show()
		else
			self.questIcon:Hide()
		end
	end
end

function UF:AddQuestIcon(self)
	if not C.db["Nameplate"]["QuestIndicator"] then return end

	local qicon = self:CreateTexture(nil, "OVERLAY", nil, 0)
	qicon:SetPoint("RIGHT", self, "LEFT", 0, 3)
	qicon:SetSize(21, 21)
	--qicon:SetAtlas(DB.questTex)
	qicon:SetTexture("Interface/WorldMap/UI-WorldMap-QuestIcon")
	qicon:SetTexCoord(0, 0.56, 0.5, 1)
	qicon:Hide()
	--local count = B.CreateFS(self, 18, "", nil, "LEFT", 0, 0)
	local count = self:CreateFontString(nil, 'OVERLAY')
	count:SetPoint("RIGHT", qicon, "LEFT", 0, 0)
	count:SetShadowOffset(1, -1)
	count:SetFont("Interface\\AddOns\\OrzUI\\Media\\Fonts\\Infinity.ttf", 23, "OUTLINE")
	count:SetTextColor(1,.82,0)

	self.questIcon = qicon
	self.questCount = count
	self:RegisterEvent("QUEST_LOG_UPDATE", UF.UpdateQuestUnit, true)
end

-- Unit classification
local NPClassifies = {
	rare = {"Interface\\MINIMAP\\ObjectIcons", .391, .487, .644, .74},  --rare = {1, 1, 1, true},
	elite = {"Interface\\MINIMAP\\Minimap_skull_elite", 0, 1, 0, 1},	--elite = {1, 1, 1},
	rareelite = {"Interface\\MINIMAP\\ObjectIcons", .754, .875, .624, .749},	--rareelite = {1, .1, .1},
	worldboss = {"Interface\\MINIMAP\\ObjectIcons", .879, 1, .754, .879},	  --worldboss = {0, 1, 0},
}

function UF:AddCreatureIcon(self)
	local icon = self.Health:CreateTexture(nil, "ARTWORK")
	--icon:SetAtlas("auctionhouse-icon-favorite")
	icon:SetPoint("RIGHT", self.nameText, "LEFT", 9, 1)
	icon:SetSize(16, 16)
	icon:Hide()

	self.ClassifyIndicator = icon
end

function UF:UpdateUnitClassify(unit)
	if not self.ClassifyIndicator then return end
	if not unit then unit = self.unit end

	self.ClassifyIndicator:Hide()

	if self.__tagIndex > 3 then
		local class = UnitClassification(unit)
		local classify = class and NPClassifies[class]
		if classify then
			local tex, r, g, b, desature = unpack(classify)
			--self.ClassifyIndicator:SetVertexColor(r, g, b)
			--self.ClassifyIndicator:SetDesaturated(desature)
			
			--if UnitLevel(unit) and UnitLevel(unit) < UnitLevel("player") then  -- or level == -1
			    --if class == elite then
			        --self.ClassifyIndicator:Hide()
			    --end
			--else    
			  self.ClassifyIndicator:SetTexture(tex)
		      self.ClassifyIndicator:SetTexCoord(r, g, b, desature)
		      self.ClassifyIndicator:Show()
			--end
		else
			self.ClassifyIndicator:Hide()
		end
	end
end

-- Mouseover indicator
function UF:IsMouseoverUnit()
	if not self or not self.unit then return end

	if self:IsVisible() and UnitExists("mouseover") then
		return UnitIsUnit("mouseover", self.unit)
	end
	return false
end

function UF:UpdateMouseoverShown()
	if not self or not self.unit then return end

	if self:IsShown() and UnitIsUnit("mouseover", self.unit) then
		self.HighlightIndicator:Show()
		self.HighlightUpdater:Show()
	else
		self.HighlightUpdater:Hide()
	end
end

function UF:HighlightOnUpdate(elapsed)
	self.elapsed = (self.elapsed or 0) + elapsed
	if self.elapsed > .1 then
		if not UF.IsMouseoverUnit(self.__owner) then
			self:Hide()
		end
		self.elapsed = 0
	end
end

function UF:HighlightOnHide()
	self.__owner.HighlightIndicator:Hide()
end

function UF:MouseoverIndicator(self)
	local highlight = CreateFrame("Frame", nil, self.Health)
	highlight:SetAllPoints(self)
	highlight:Hide()

	local texture = highlight:CreateTexture(nil, "ARTWORK")
	texture:SetAllPoints()
	texture:SetColorTexture(1, 1, 1, .35)
	local glow = B.CreateSD(highlight, 8, true)
	glow:SetOutside(self.backdrop, 8, 8)
	glow:SetBackdropBorderColor(0, .6, 1)
	glow:SetFrameLevel(1)

	self:RegisterEvent("UPDATE_MOUSEOVER_UNIT", UF.UpdateMouseoverShown, true)

	local updater = CreateFrame("Frame", nil, self)
	updater.__owner = self
	updater:SetScript("OnUpdate", UF.HighlightOnUpdate)
	updater:HookScript("OnHide", UF.HighlightOnHide)

	self.HighlightIndicator = highlight
	self.HighlightUpdater = updater
end

-- Create Nameplates
local platesList = {}
UF.nameplateUnits = {}

function UF:CreatePlates()
	self.mystyle = "nameplate"
	self:SetSize(C.db["Nameplate"]["PlateWidth"], C.db["Nameplate"]["PlateHeight"])
	self:ClearAllPoints()
	self:SetPoint("CENTER")
	--self:SetScale(OrzUISetDB["UIScale"])

	local health = CreateFrame("StatusBar", nil, self)
	health:SetAllPoints()
	health:SetStatusBarTexture(DB.normTex)
	UF:SmoothBar(health)
	self.backdrop = B.SetBD(health)
	self.backdrop.__shadow = nil
	self.Health = health
	self.Health.UpdateColor = UF.UpdateColor

	local tarName = B.CreateFS(self, C.db["Nameplate"]["NameTextSize"]+4)
	tarName:ClearAllPoints()
	tarName:SetPoint("TOP", self, "BOTTOM", 0, -10)
	tarName:Hide()
	self:Tag(tarName, "[tarname]")
	self.tarName = tarName

	UF:CreateHealthText(self)
	UF:CreateCastBar(self)
	UF:CreateRaidMark(self)
	UF:CreatePrediction(self)
	UF:CreateAuras(self)
	UF:CreatePlateDebuffs(self)
	UF:CreatePVPClassify(self)
	UF:CreateThreatColor(self)

	self.Auras.showStealableBuffs = true
	self.Auras.alwaysShowStealable = C.db["Nameplate"]["ShowDispel"]

	local title = B.CreateFS(self, C.db["Nameplate"]["NameOnlyTitleSize"])
	title:ClearAllPoints()
	title:SetPoint("TOP", self.nameText, "BOTTOM", 0, -2)
	title:Hide()
	self:Tag(title, "[npctitle]")
	self.npcTitle = title

	UF:MouseoverIndicator(self)
	UF:AddTargetIndicator(self)
	UF:AddCreatureIcon(self)
	UF:AddQuestIcon(self)

	self:RegisterEvent("PLAYER_FOCUS_CHANGED", UF.UpdateFocusColor, true)

	platesList[self] = self:GetName()
end

function UF:ToggleNameplateAuras(shouldEnable)
	if C.db["Nameplate"]["PlateAuras"] and shouldEnable then
		if not self:IsElementEnabled("Auras") then
			self:EnableElement("Auras")
		end
	else
		if self:IsElementEnabled("Auras") then
			self:DisableElement("Auras")
		end
	end
end

function UF:UpdateNameplateAuras()
	UF.ToggleNameplateAuras(self, true)

	local element = self.Auras
	if C.db["Nameplate"]["TargetPower"] then
		element:SetPoint("BOTTOMLEFT", self.nameText, "TOPLEFT", 0, 10 + C.db["Nameplate"]["PPBarHeight"])
	else
		element:SetPoint("BOTTOMLEFT", self.nameText, "TOPLEFT", 0, 5)
	end
	element.numTotal = C.db["Nameplate"]["maxAuras"]
	element.maxCols = C.db["Nameplate"]["AurasPerRow"]
	element.fontSize = C.db["Nameplate"]["FontSize"]
	element.showDebuffType = C.db["Nameplate"]["DebuffColor"]
	element.showStealableBuffs = true
	element.alwaysShowStealable = C.db["Nameplate"]["ShowDispel"]
	element.desaturateDebuff = C.db["Nameplate"]["Desaturate"]
	element.sizeRatio = C.db["Nameplate"]["SizeRatio"]
	UF:UpdateAuraContainer(self, element, element.numTotal)
	element:ForceUpdate()
end

function UF:UpdateNameplateDebuffs()
	local element = self.Debuffs
	element.numDebuffs = not C.db["Nameplate"]["PlateCC"] and 0 or C.db["Nameplate"]["NumCC"]
	element.maxCols = C.db["Nameplate"]["CCPerRow"]
	element.fontSize = C.db["Nameplate"]["CCFontSize"]
	element.showDebuffType = C.db["Nameplate"]["DebuffColor"]
	element.desaturateDebuff = false
	element.sizeRatio = C.db["Nameplate"]["CCSizeRatio"]
	UF:UpdateAuraContainer(self, element, element.numDebuffs)
	if element.ForceUpdate then
		element:ForceUpdate()
	end
end

function UF.Nameplate_FilterDebuff(element, _, data)
	return data.isHarmfulAura and data.isCrowdControlAura
end

function UF:CreatePlateDebuffs(self)
	local element = CreateFrame("Frame", nil, self)
	element:SetPoint("LEFT", self.Health, "RIGHT", 5, 0)
	element.initialAnchor = "LEFT"
	element.disableMouse = true
	element.spacing = 3
	self.Debuffs = element

	UF.UpdateNameplateDebuffs(self)
	element.FilterAura = UF.Nameplate_FilterDebuff
	element.PostCreateButton = UF.PostCreateButton
	element.PostUpdateButton = UF.PostUpdateButton
	element.PostProcessAuraData = UF.PostProcessAuraData
end

UF.PlateNameTags = {
	[1] = "",
	[2] = "[name]",
	[3] = "[nplevel][name]",
	[4] = "[nprare][name]",
	[5] = "[nprare][nplevel][name]",
}
function UF:UpdateNameplateSize()
	local plateWidth, plateHeight = C.db["Nameplate"]["PlateWidth"], C.db["Nameplate"]["PlateHeight"]
	local plateCBHeight, plateCBOffset = C.db["Nameplate"]["PlateCBHeight"], C.db["Nameplate"]["PlateCBOffset"]
	local nameTextSize, CBTextSize = C.db["Nameplate"]["NameTextSize"], C.db["Nameplate"]["CBTextSize"]
	local nameTextOffset = C.db["Nameplate"]["NameTextOffset"]
	local healthTextSize = C.db["Nameplate"]["HealthTextSize"]
	local healthTextOffset = C.db["Nameplate"]["HealthTextOffset"]
	if C.db["Nameplate"]["FriendPlate"] and self.isFriendly and not C.db["Nameplate"]["NameOnlyMode"] then -- cannot use plateType here
		plateWidth, plateHeight = C.db["Nameplate"]["FriendPlateWidth"], C.db["Nameplate"]["FriendPlateHeight"]
		plateCBHeight, plateCBOffset = C.db["Nameplate"]["FriendPlateCBHeight"], C.db["Nameplate"]["FriendPlateCBOffset"]
		nameTextSize, CBTextSize = C.db["Nameplate"]["FriendNameSize"], C.db["Nameplate"]["FriendCBTextSize"]
		nameTextOffset = C.db["Nameplate"]["FriendNameOffset"]
		healthTextSize = C.db["Nameplate"]["FriendHealthSize"]
		healthTextOffset = C.db["Nameplate"]["FriendHealthOffset"]
	end
	local iconSize = plateHeight + plateCBHeight + 5
	local nameType = C.db["Nameplate"]["NameType"]
	local nameOnlyTextSize, nameOnlyTitleSize = C.db["Nameplate"]["NameOnlyTextSize"], C.db["Nameplate"]["NameOnlyTitleSize"]

	if self.plateType == "NameOnly" then
		B.SetFontSize(self.nameText, nameOnlyTextSize)
		local prefix = (not self.isSoftTarget and "[nprare][nplevel]" or "")
		self:Tag(self.nameText, prefix.."[color][name]")
		self.__tagIndex = 6
		B.SetFontSize(self.npcTitle, nameOnlyTitleSize)
		self.npcTitle:UpdateTag()
	else
		B.SetFontSize(self.nameText, nameTextSize)
		self.nameText:SetPoint("BOTTOMLEFT", self, "TOPLEFT", 0, nameTextOffset)
		self.nameText:SetPoint("BOTTOMRIGHT", self, "TOPRIGHT", 0, nameTextOffset)
		self:Tag(self.nameText, UF.PlateNameTags[nameType])
		self.__tagIndex = nameType

		self:SetSize(plateWidth, plateHeight)
		B.SetFontSize(self.tarName, nameTextSize+4)
		self.Castbar.Icon:SetSize(iconSize, iconSize)
		self.Castbar:SetHeight(plateCBHeight)
		B.SetFontSize(self.Castbar.Time, CBTextSize)
		self.Castbar.Time:SetPoint("TOPRIGHT", self.Castbar, "RIGHT", 0, plateCBOffset)
		B.SetFontSize(self.Castbar.Text, CBTextSize)
		self.Castbar.Text:SetPoint("TOPLEFT", self.Castbar, "LEFT", 0, plateCBOffset)
		self.Castbar.Shield:SetPoint("TOP", self.Castbar, "CENTER", 0, plateCBOffset)
		self.Castbar.Shield:SetSize(CBTextSize + 4, CBTextSize + 4)
		B.SetFontSize(self.Castbar.spellTarget, CBTextSize+3)
		B.SetFontSize(self.healthValue, healthTextSize)
		self.healthValue:SetPoint("RIGHT", self, 0, healthTextOffset)
		self:Tag(self.healthValue, "[VariousHP("..UF.VariousTagIndex[C.db["Nameplate"]["HealthType"]]..")]")
		self.healthValue:UpdateTag()
		self.RaidTargetIndicator:SetPoint("BOTTOMRIGHT", self, "TOPLEFT", C.db["Nameplate"]["RaidTargetX"], C.db["Nameplate"]["RaidTargetY"])
	end
	self.nameText:UpdateTag()
end

function UF:RefreshNameplats()
	for nameplate in pairs(platesList) do
		UF.UpdateNameplateSize(nameplate)
		UF.UpdateUnitClassify(nameplate)
		UF.UpdateNameplateAuras(nameplate)
		UF.UpdateNameplateDebuffs(nameplate)
		UF.UpdateTargetIndicator(nameplate)
		UF.UpdateTargetChange(nameplate)
	end
	UF:UpdatePlateSize()
end

function UF:RefreshAllPlates()
	UF:ResizePlayerPlate()
	UF:RefreshNameplats()
	UF:ResizeTargetPower()
end

local DisabledElements = {
	"Health", "Castbar", "HealthPrediction", "PvPClassificationIndicator", "ThreatIndicator"
}

local SoftTargetBlockElements = {
	"RaidTargetIndicator",
}

function UF:UpdatePlateByType()
	local name = self.nameText
	local hpval = self.healthValue
	local title = self.npcTitle
	local raidtarget = self.RaidTargetIndicator
	local questIcon = self.questIcon

	if self.widgetsOnly then
		name:Hide()
	else
		name:Show()
		name:ClearAllPoints()
	end
	raidtarget:ClearAllPoints()

	local shouldEnableAura
	if self.isSoftTarget then
		for _, element in pairs(SoftTargetBlockElements) do
			if self:IsElementEnabled(element) then
				self:DisableElement(element)
			end
		end
		shouldEnableAura = false
	else
		for _, element in pairs(SoftTargetBlockElements) do
			if not self:IsElementEnabled(element) then
				self:EnableElement(element)
			end
		end
		shouldEnableAura = true
	end

	if self.plateType == "NameOnly" then
		for _, element in pairs(DisabledElements) do
			if self:IsElementEnabled(element) then
				self:DisableElement(element)
			end
		end
		shouldEnableAura = false

		name:SetJustifyH("CENTER")
		name:SetPoint("CENTER", self, "BOTTOM")
		hpval:Hide()
		title:Show()

		raidtarget:SetPoint("TOP", title, "BOTTOM", 0, -5)
		if questIcon then questIcon:SetPoint("RIGHT", name, "LEFT", 0, 0) end

		if self.widgetContainer then
			self.widgetContainer:ClearAllPoints()
			self.widgetContainer:SetPoint("TOP", title, "BOTTOM", 0, -5)
		end
	else
		for _, element in pairs(DisabledElements) do
			if not self:IsElementEnabled(element) then
				self:EnableElement(element)
			end
		end
		shouldEnableAura = true

		name:SetJustifyH("LEFT")
		hpval:Show()
		title:Hide()

		raidtarget:SetPoint("RIGHT", self, "LEFT", -3, 0)
		if questIcon then questIcon:SetPoint("RIGHT", self, "LEFT", 0, 0) end

		if self.widgetContainer then
			self.widgetContainer:ClearAllPoints()
			self.widgetContainer:SetPoint("TOP", self.Castbar, "BOTTOM", 0, -5)
		end
	end

	UF.UpdateNameplateSize(self)
	UF.UpdateTargetIndicator(self)
	UF.ToggleNameplateAuras(self, shouldEnableAura)
end

function UF:RefreshPlateType(unit)
	self.reaction = UnitReaction(unit, "player")
	self.isFriendly = self.reaction and self.reaction >= 4 and not UnitCanAttack("player", unit)
	self.isSoftTarget = UnitIsUnit(unit, "softinteract")
	if C.db["Nameplate"]["NameOnlyMode"] and self.isFriendly or self.widgetsOnly or self.isSoftTarget then
		self.plateType = "NameOnly"
	elseif C.db["Nameplate"]["FriendPlate"] and self.isFriendly then
		self.plateType = "FriendPlate"
	else
		self.plateType = "None"
	end

	if self.previousType == nil or self.previousType ~= self.plateType then
		UF.UpdatePlateByType(self)
		self.previousType = self.plateType
	end
end

function UF:OnUnitFactionChanged(unit)
	if not UF.nameplateUnits[unit] then return end

	local nameplate = C_NamePlate_GetNamePlateForUnit(unit)
	local unitFrame = nameplate and nameplate.unitFrame
	if unitFrame and unitFrame.unitName then
		UF.RefreshPlateType(unitFrame, unit)
	end
end

function UF:OnUnitSoftTargetChanged() -- needs review
	if not GetCVarBool("SoftTargetIconGameObject") then return end

	for _, nameplate in pairs(C_NamePlate.GetNamePlates()) do
		local unitFrame = nameplate and nameplate.unitFrame
		if unitFrame then
			unitFrame.nameText:UpdateTag()
		end
	end
end

function UF:RefreshPlateByEvents()
	updateZoneType()
	B:RegisterEvent("PLAYER_ENTERING_WORLD", updateZoneType)
	B:RegisterEvent("UNIT_FACTION", UF.OnUnitFactionChanged)
	B:RegisterEvent("PLAYER_SOFT_INTERACT_CHANGED", UF.OnUnitSoftTargetChanged)
end

local function onTargetChanged(self, event, unit)
	if not self then return end

	UF.UpdateTargetChange(self)
	UF.UpdateQuestUnit(self, event, unit)
	UF.UpdateUnitClassify(self, unit)
	UF:UpdateTargetClassPower()

	self.tarName:SetShown(C.ShowTargetNPCs[self.npcID])
end

function UF:OnNameplateAdded(event, unit)
	if not self then return end

	UF.nameplateUnits[unit] = true

	local name = UnitName(unit)
	self.unitName = B:NotSecretValue(name) and name or nil
	local guid = UnitGUID(unit)
	self.unitGUID = B:NotSecretValue(guid) and guid or nil
	self.isPlayer = UnitIsPlayer(unit)
	self.npcID = B.GetNPCID(self.unitGUID)
	self.widgetsOnly = UnitNameplateShowsWidgetsOnly(unit)

	local blizzPlate = self:GetParent().UnitFrame
	if blizzPlate then
		self.widgetContainer = blizzPlate.WidgetContainer
		if self.widgetContainer then
			--self.widgetContainer:SetParent(self)
			self.widgetContainer:SetScale(1/OrzUISetDB["UIScale"])
		end

		self.softTargetFrame = blizzPlate.SoftTargetFrame
		if self.softTargetFrame then
			--self.softTargetFrame:SetParent(self)
			self.softTargetFrame:SetScale(1/OrzUISetDB["UIScale"])
		end
	end

	UF.RefreshPlateType(self, unit)
	onTargetChanged(self, event, unit)
end

function UF:OnNameplateRemoved(_, unit)
	if not self then return end
	self.npcID = nil
	self.nameText:SetText("")
	self.npcTitle:SetText("")
	UF.nameplateUnits[unit] = nil
end

function UF:OnTargetChanged(event, unit)
	onTargetChanged(self, event, unit)
end

-- Player Nameplate
function UF:PlateVisibility(event)
	local alpha = C.db["Nameplate"]["PPFadeoutAlpha"]
	if (event == "PLAYER_REGEN_DISABLED" or InCombatLockdown()) and UnitIsUnit("player", self.unit) then
		if self:IsElementEnabled("Health") then
			UIFrameFadeIn(self.Health, .3, self.Health:GetAlpha(), 1)
			UIFrameFadeIn(self.Health.bg, .3, self.Health.bg:GetAlpha(), .7)
			UIFrameFadeIn(self.predicFrame, .3, self:GetAlpha(), 1)
		end
		UIFrameFadeIn(self.Power, .3, self.Power:GetAlpha(), 1)
		UIFrameFadeIn(self.Power.bg, .3, self.Power.bg:GetAlpha(), .7)
		UIFrameFadeIn(self.ClassPowerBar, .3, self.ClassPowerBar:GetAlpha(), 1)
		if self.Stagger and self.Stagger:IsShown() then
			UIFrameFadeIn(self.Stagger, .3, self.Stagger:GetAlpha(), 1)
		end
	else
		if self:IsElementEnabled("Health") then
			UIFrameFadeOut(self.Health, 2, self.Health:GetAlpha(), alpha)
			UIFrameFadeOut(self.Health.bg, 2, self.Health.bg:GetAlpha(), alpha)
			UIFrameFadeOut(self.predicFrame, 2, self:GetAlpha(), alpha)
		end
		UIFrameFadeOut(self.Power, 2, self.Power:GetAlpha(), alpha)
		UIFrameFadeOut(self.Power.bg, 2, self.Power.bg:GetAlpha(), alpha)
		UIFrameFadeOut(self.ClassPowerBar, 2, self.ClassPowerBar:GetAlpha(), alpha)
		if self.Stagger and self.Stagger:IsShown() then
			UIFrameFadeOut(self.Stagger, 2, self.Stagger:GetAlpha(), alpha)
		end
	end
end

function UF:ResizePlayerPlate()
	local plate = _G.oUF_PlayerPlate
	if plate then
		local barWidth = C.db["Nameplate"]["PPWidth"]
		local barHeight = C.db["Nameplate"]["PPBarHeight"]
		local healthHeight = C.db["Nameplate"]["PPHealthHeight"]
		local powerHeight = C.db["Nameplate"]["PPPowerHeight"]

		plate:SetSize(barWidth, healthHeight + powerHeight + C.mult)
		plate.mover:SetSize(barWidth, healthHeight + powerHeight + C.mult)
		plate.Health:SetHeight(healthHeight)
		plate.Power:SetHeight(powerHeight)

		local bars = plate.ClassPower or plate.Runes
		if bars then
			plate.ClassPowerBar:SetSize(barWidth, barHeight)
			plate.ClassPowerBar.width = barWidth
			local max = bars.__max
			for i = 1, max do
				bars[i]:SetHeight(barHeight)
				bars[i]:SetWidth((barWidth - (max-1)*C.margin) / max)
			end
		end
		if plate.Stagger then
			plate.Stagger:SetSize(barWidth, barHeight)
		end
		if plate.Avada then
			local iconSize = (barWidth+2*C.mult - C.margin*5)/6
			for i = 1, 6 do
				plate.Avada[i]:SetSize(iconSize, iconSize)
			end
		end
	end
end

function UF:CreatePlayerPlate()
	self.mystyle = "PlayerPlate"
	self:EnableMouse(false)
	local healthHeight, powerHeight = C.db["Nameplate"]["PPHealthHeight"], C.db["Nameplate"]["PPPowerHeight"]
	self:SetSize(C.db["Nameplate"]["PPWidth"], healthHeight + powerHeight + C.mult)

	UF:CreateHealthBar(self)
	self.Health.bg:SetVertexColor(0, 0, 0, .7)
	UF:CreatePowerBar(self)
	UF:CreatePrediction(self)
	UF:CreateClassPower(self)
	UF:StaggerBar(self)
	--UF:AvadaKedavra(self)

	local textFrame = CreateFrame("Frame", nil, self.Power)
	textFrame:SetAllPoints()
	textFrame:SetFrameLevel(self:GetFrameLevel() + 5)
	self.powerText = B.CreateFS(textFrame, 14)
	self:Tag(self.powerText, "[pppower]")
	UF:TogglePlatePower()

	UF:CreateGCDTicker(self)
	UF:TogglePlateVisibility()
end

function UF:TogglePlayerPlate()
	local plate = _G.oUF_PlayerPlate
	if not plate then return end

	if C.db["Nameplate"]["ShowPlayerPlate"] then
		plate:Enable()
	else
		plate:Disable()
	end
end

function UF:TogglePlatePower()
	local plate = _G.oUF_PlayerPlate
	if not plate then return end

	plate.powerText:SetShown(C.db["Nameplate"]["PPPowerText"])
end

function UF:TogglePlateHealth()
	local plate = _G.oUF_PlayerPlate
	if not plate or not plate.Health then return end

	if C.db["Nameplate"]["PPHealthBar"] then
		if not plate:IsElementEnabled("Health") then
			plate:EnableElement("Health")
			plate.Health:ForceUpdate()
			if plate.ClassPowerBar then
				plate.ClassPowerBar:SetPoint("BOTTOMLEFT", plate, "TOPLEFT", 0, C.margin)
			end
			if plate.Stagger then
				plate.Stagger:SetPoint("BOTTOMLEFT", plate, "TOPLEFT", 0, C.margin)
			end
		end
	else
		if plate:IsElementEnabled("Health") then
			plate:DisableElement("Health")
			if plate.ClassPowerBar then
				plate.ClassPowerBar:SetPoint("BOTTOMLEFT", plate.Power, "TOPLEFT", 0, C.margin)
			end
			if plate.Stagger then
				plate.Stagger:SetPoint("BOTTOMLEFT", plate.Power, "TOPLEFT", 0, C.margin)
			end
		end
	end
end

function UF:TogglePlateVisibility()
	local plate = _G.oUF_PlayerPlate
	if not plate then return end

	if C.db["Nameplate"]["PPFadeout"] then
		plate:RegisterEvent("UNIT_EXITED_VEHICLE", UF.PlateVisibility)
		plate:RegisterEvent("UNIT_ENTERED_VEHICLE", UF.PlateVisibility)
		plate:RegisterEvent("PLAYER_REGEN_ENABLED", UF.PlateVisibility, true)
		plate:RegisterEvent("PLAYER_REGEN_DISABLED", UF.PlateVisibility, true)
		plate:RegisterEvent("PLAYER_ENTERING_WORLD", UF.PlateVisibility, true)
		UF.PlateVisibility(plate)
	else
		plate:UnregisterEvent("UNIT_EXITED_VEHICLE", UF.PlateVisibility)
		plate:UnregisterEvent("UNIT_ENTERED_VEHICLE", UF.PlateVisibility)
		plate:UnregisterEvent("PLAYER_REGEN_ENABLED", UF.PlateVisibility)
		plate:UnregisterEvent("PLAYER_REGEN_DISABLED", UF.PlateVisibility)
		plate:UnregisterEvent("PLAYER_ENTERING_WORLD", UF.PlateVisibility)
		UF.PlateVisibility(plate, "PLAYER_REGEN_DISABLED")
	end
end

-- Target nameplate
function UF:CreateTargetPlate()
	self.mystyle = "targetplate"
	self:EnableMouse(false)
	self:SetSize(10, 10)

	UF:CreateClassPower(self)
end

function UF:UpdateTargetClassPower()
	local plate = _G.oUF_TargetPlate
	if not plate then return end

	local bar = plate.ClassPowerBar
	local nameplate = C_NamePlate_GetNamePlateForUnit("target")
	if nameplate then
		bar:SetParent(nameplate.unitFrame)
		bar:ClearAllPoints()
		bar:SetPoint("BOTTOM", nameplate.unitFrame.nameText, "TOP", 0, 5)
		bar:Show()
	else
		bar:Hide()
	end
end

function UF:ToggleTargetClassPower()
	local plate = _G.oUF_TargetPlate
	if not plate then return end

	local playerPlate = _G.oUF_PlayerPlate
	if C.db["Nameplate"]["TargetPower"] then
		plate:Enable()
		if plate.ClassPower then
			if not plate:IsElementEnabled("ClassPower") then
				plate:EnableElement("ClassPower")
				plate.ClassPower:ForceUpdate()
			end
			if playerPlate then
				if playerPlate:IsElementEnabled("ClassPower") then
					playerPlate:DisableElement("ClassPower")
				end
			end
		end
		if plate.Runes then
			if not plate:IsElementEnabled("Runes") then
				plate:EnableElement("Runes")
				plate.Runes:ForceUpdate()
			end
			if playerPlate then
				if playerPlate:IsElementEnabled("Runes") then
					playerPlate:DisableElement("Runes")
				end
			end
		end
	else
		plate:Disable()
		if plate.ClassPower then
			if plate:IsElementEnabled("ClassPower") then
				plate:DisableElement("ClassPower")
			end
			if playerPlate then
				if not playerPlate:IsElementEnabled("ClassPower") then
					playerPlate:EnableElement("ClassPower")
					playerPlate.ClassPower:ForceUpdate()
				end
			end
		end
		if plate.Runes then
			if plate:IsElementEnabled("Runes") then
				plate:DisableElement("Runes")
			end
			if playerPlate then
				if not playerPlate:IsElementEnabled("Runes") then
					playerPlate:EnableElement("Runes")
					playerPlate.Runes:ForceUpdate()
				end
			end
		end
	end
end

function UF:ResizeTargetPower()
	local plate = _G.oUF_TargetPlate
	if not plate then return end

	local barWidth = C.db["Nameplate"]["PlateWidth"]
	local barHeight = C.db["Nameplate"]["PPBarHeight"]
	local bars = plate.ClassPower or plate.Runes
	if bars then
		plate.ClassPowerBar:SetSize(barWidth, barHeight)
		plate.ClassPowerBar.width = barWidth
		local max = bars.__max
		for i = 1, max do
			bars[i]:SetHeight(barHeight)
			bars[i]:SetWidth((barWidth - (max-1)*C.margin) / max)
		end
	end
end

function UF:UpdateGCDTicker()
	local cooldownInfo = C_Spell.GetSpellCooldown(61304)
	local start = cooldownInfo and cooldownInfo.startTime
	local duration = cooldownInfo and cooldownInfo.duration
	local isActive = cooldownInfo and cooldownInfo.isActive
	local cdObject
	if isActive then
		cdObject = C_Spell.GetSpellCooldownDuration(61304)
	end

	if B:IsSecretValue(cdObject) then
		self:SetTimerDuration(gcdDuration)
		self.spark:Show()
	elseif start > 0 and duration > 0 then
		if self.duration ~= duration then
			self:SetMinMaxValues(0, duration)
			self.duration = duration
		end
		self:SetValue(GetTime() - start)
		self.spark:Show()
	else
		self.spark:Hide()
	end
end

function UF:CreateGCDTicker(self)
	local ticker = CreateFrame("StatusBar", nil, self.Power)
	ticker:SetFrameLevel(self:GetFrameLevel() + 3)
	ticker:SetStatusBarTexture(DB.normTex)
	ticker:GetStatusBarTexture():SetAlpha(0)
	ticker:SetAllPoints()

	local spark = ticker:CreateTexture(nil, "OVERLAY")
	spark:SetTexture(DB.sparkTex)
	spark:SetBlendMode("ADD")
	spark:SetPoint("TOPLEFT", ticker:GetStatusBarTexture(), "TOPRIGHT", -10, 10)
	spark:SetPoint("BOTTOMRIGHT", ticker:GetStatusBarTexture(), "BOTTOMRIGHT", 10, -10)
	ticker.spark = spark

	ticker:SetScript("OnUpdate", UF.UpdateGCDTicker)
	self.GCDTicker = ticker

	UF:ToggleGCDTicker()
end

function UF:ToggleGCDTicker()
	local plate = _G.oUF_PlayerPlate
	local ticker = plate and plate.GCDTicker
	if not ticker then return end

	ticker:SetShown(C.db["Nameplate"]["PPGCDTicker"])
end

UF.MajorSpells = {}
function UF:RefreshMajorSpells()
	wipe(UF.MajorSpells)

	for spellID in pairs(C.MajorSpells) do
		local name = GetSpellName(spellID)
		if name then
			local modValue = OrzUISetDB["MajorSpells"][spellID]
			if modValue == nil then
				UF.MajorSpells[spellID] = true
			end
		end
	end

	for spellID, value in pairs(OrzUISetDB["MajorSpells"]) do
		if value then
			UF.MajorSpells[spellID] = true
		end
	end
end

UF.NameplateWhite = {}
UF.NameplateBlack = {}

local function RefreshNameplateFilter(list, key)
	wipe(UF[key])

	for spellID in pairs(list) do
		local name = GetSpellName(spellID)
		if name then
			if OrzUISetDB[key][spellID] == nil then
				UF[key][spellID] = true
			end
		end
	end

	for spellID, value in pairs(OrzUISetDB[key]) do
		if value then
			UF[key][spellID] = true
		end
	end
end

function UF:RefreshNameplateFilters()
	RefreshNameplateFilter(C.WhiteList, "NameplateWhite")
	RefreshNameplateFilter(C.BlackList, "NameplateBlack")
end