--Plater main software file

--Calls with : are functions imported from the framework
--whenever a variable or function has a --private comment attached to it, means scripts cannot access it (read, write, override), anything else can be overriden with scripts
--with that, you can make your own version of Plater by modifying and overriding functions entirelly using a hooking script, them you can export the script and upload to wago.io (have fun :)
--check the list of available functions and members to override at 'Plater.CanOverride_Functions' and 'Plater.CanOverride_Members'

--Weakauras Scripters: if you need to attach something to Plater nameplates:
-- local namePlate = C_NamePlate.GetNamePlateForUnit (unitID)
-- local unitFrame = namePlate.unitFrame --unitFrame is the main frame where all things is attached, it has SetAllPoints() on the namePlate frame.
-- local healthBar = unitFrame.healthBar
-- local castBar = unitFrame.castBar

-- update localization

-- navigate within the code using search tags: ~color ~border, etc...

 if (true) then
	--return
	--but not today
end

--> details! framework
local DF = _G ["DetailsFramework"]
if (not DF) then
	print ("|cFFFFAA00Plater: framework not found, if you just installed or updated the addon, please restart your client.|r")
	return
end

--/run UIErrorsFrame:HookScript ("OnEnter", function() UIErrorsFrame:EnableMouse (false);Plater:Msg("UIErrorsFrame had MouseEnabled, its disabled now.") end)
--> some WA or addon are enabling the mouse on the error frame making nameplates unclickable
if (UIErrorsFrame) then
	UIErrorsFrame:HookScript ("OnEnter", function()
		--safe disable the mouse on error frame avoiding mouse interactions and warn the user
		UIErrorsFrame:EnableMouse (false)
		Plater:Msg ("something enabled the mouse on UIErrorsFrame, Plater disabled.")
	end)
	UIErrorsFrame:EnableMouse (false)
end

--> locals
local unpack = unpack
local ipairs = ipairs
local rawset = rawset
--local rawget = rawget --200 locals limit
--local setfenv = setfenv --200 locals limit
local xpcall = xpcall
local InCombatLockdown = InCombatLockdown
local UnitIsPlayer = UnitIsPlayer
local UnitClassification = UnitClassification
local UnitDetailedThreatSituation = UnitDetailedThreatSituation
local UnitCanAttack = UnitCanAttack
--local IsSpellInRange = IsSpellInRange --200 locals limit
local abs = math.abs
local format = string.format
local GetSpellInfo = GetSpellInfo or function(spellID) if not spellID then return nil end local si = C_Spell.GetSpellInfo(spellID) if si then return si.name, nil, si.iconID, si.castTime, si.minRange, si.maxRange, si.spellID, si.originalIconID end end
local UnitIsUnit = UnitIsUnit
local type = type
local select = select
local UnitGUID = UnitGUID
local strsplit = strsplit
local lower = string.lower
local floor = floor
local max = math.max
local min = math.min

local IS_WOW_PROJECT_MAINLINE = WOW_PROJECT_ID == WOW_PROJECT_MAINLINE
local IS_WOW_PROJECT_NOT_MAINLINE = WOW_PROJECT_ID ~= WOW_PROJECT_MAINLINE
local IS_WOW_PROJECT_CLASSIC_ERA = WOW_PROJECT_ID == WOW_PROJECT_CLASSIC
local IS_WOW_PROJECT_CLASSIC_WRATH = IS_WOW_PROJECT_NOT_MAINLINE and ClassicExpansionAtLeast and LE_EXPANSION_WRATH_OF_THE_LICH_KING and ClassicExpansionAtLeast(LE_EXPANSION_WRATH_OF_THE_LICH_KING)
--local IS_WOW_PROJECT_CLASSIC_CATACLYSM = IS_WOW_PROJECT_NOT_MAINLINE and ClassicExpansionAtLeast and LE_EXPANSION_CATACLYSM and ClassicExpansionAtLeast(LE_EXPANSION_CATACLYSM)

local PixelUtil = PixelUtil or DFPixelUtil

local parserFunctions --reference needed

local LibSharedMedia = LibStub:GetLibrary ("LibSharedMedia-3.0") -- https://www.curseforge.com/wow/addons/libsharedmedia-3-0
local LCG = LibStub:GetLibrary("LibCustomGlow-1.0") -- https://github.com/Stanzilla/LibCustomGlow
local LibRangeCheck = LibStub:GetLibrary ("LibRangeCheck-3.0") -- https://github.com/WeakAuras/LibRangeCheck-3.0
local LibTranslit = LibStub:GetLibrary ("LibTranslit-1.0") -- https://github.com/Vardex/LibTranslit
local LDB = LibStub ("LibDataBroker-1.1", true)
local LDBIcon = LDB and LibStub ("LibDBIcon-1.0", true)

local addonId, platerInternal = ...
local _ = nil

--localization
local LOC = DF.Language.GetLanguageTable(addonId)

---@type plater
local Plater = DF:CreateAddOn ("Plater", "PlaterDB", PLATER_DEFAULT_SETTINGS, InterfaceOptionsFrame and { --options table --TODO: DISABLED FOR DRAGONFLIGHT FOR NOW!
	name = "Plater Nameplates",
	type = "group",
	args = {
		openOptions = {
			name = "Open Plater Options",
			desc = "Opens the Plater Options Menu.",
			type = "execute",
			func = function()
				if InterfaceOptionsFrame then
					InterfaceOptionsFrame:Hide()
				elseif SettingsPanel then
					SettingsPanel:Hide()
				end
				HideUIPanel(GameMenuFrame)
				Plater.OpenOptionsPanel()
			end,
		},
	}
})
local GetAddOnMetadata = C_AddOns and C_AddOns.GetAddOnMetadata or GetAddOnMetadata
Plater.versionString = GetAddOnMetadata("Plater", "Version")
Plater.fullVersionInfo = Plater.versionString .. " - DF v" .. select(2,LibStub:GetLibrary("DetailsFramework-1.0")) .. " - " .. GetBuildInfo()
function Plater.GetVersionInfo(printOut)
	-- update, just in case...
	Plater.versionString = GetAddOnMetadata("Plater", "Version")
	Plater.fullVersionInfo = Plater.versionString .. " - DF v" .. select(2,LibStub:GetLibrary("DetailsFramework-1.0")) .. " - " .. GetBuildInfo()
	if printOut then print("Plater version info:\n" .. Plater.fullVersionInfo) end
	return Plater.fullVersionInfo
end

--> when a hook script is compiled, it increases the build version, so the handler for running scripts will notice in the change and update the script in real time
local PLATER_HOOK_BUILD = 1
function Plater.IncreaseHookBuildID() --private
	PLATER_HOOK_BUILD = PLATER_HOOK_BUILD + 1
end

--> if a widget has a RefreshID lower than the addon, it needs to be updated
local PLATER_REFRESH_ID = 1
function Plater.IncreaseRefreshID() --private
	PLATER_REFRESH_ID = PLATER_REFRESH_ID + 1
	Plater.IncreaseRefreshID_Auras()
end

platerInternal.CreateDataTables(Plater)

Plater.ForceBlizzardNameplateUnits = {
	--
}
Plater.AddForceBlizzardNameplateUnits = function(npcID)
	if type(npcID) == "number" then
		Plater.ForceBlizzardNameplateUnits[npcID] = true
	end
end
Plater.RemoveForceBlizzardNameplateUnits = function(npcID)
	if type(npcID) == "number" then
		Plater.ForceBlizzardNameplateUnits[npcID] = nil
	end
end

--store npc names and spell names from the current/latest combat
--used to sort data in the options panel: Spell List, Spell Colors and Npc Colors
Plater.LastCombat = {
	npcNames = {},
	spellNames = {},
}

Plater.MDTSettings = {
	button_width = 18, --button and icon width
	button_height = 18,
	enemyinfo_button_point = {"topright", "topright", 4.682, -21.361},
	spellinfo_button_point = {"bottomright", "bottomright", -12, 2},
	icon_texture = [[Interface\Buttons\UI-Panel-BiggerButton-Up]],
	icon_coords = {0.2, 0.8, 0.2, 0.8},
	alpha = 0.834, --button alpha
}

-- ~hook (hook scripts are cached in the indexed part of these tales, for performance the member ScriptAmount caches the amount of scripts inside the indexed table)
local HOOK_NAMEPLATE_ADDED = {ScriptAmount = 0}
local HOOK_NAMEPLATE_CREATED = {ScriptAmount = 0}
local HOOK_NAMEPLATE_REMOVED = {ScriptAmount = 0}
local HOOK_NAMEPLATE_UPDATED = {ScriptAmount = 0}
local HOOK_TARGET_CHANGED = {ScriptAmount = 0}
local HOOK_CAST_START = {ScriptAmount = 0}
local HOOK_CAST_UPDATE = {ScriptAmount = 0}
local HOOK_CAST_STOP = {ScriptAmount = 0}
local HOOK_RAID_TARGET = {ScriptAmount = 0}
local HOOK_COMBAT_ENTER = {ScriptAmount = 0}
local HOOK_COMBAT_LEAVE = {ScriptAmount = 0}
local HOOK_NAMEPLATE_CONSTRUCTOR = {ScriptAmount = 0}
local HOOK_PLAYER_POWER_UPDATE = {ScriptAmount = 0}
local HOOK_PLAYER_TALENT_UPDATE = {ScriptAmount = 0}
local HOOK_HEALTH_UPDATE = {ScriptAmount = 0}
local HOOK_ZONE_CHANGED = {ScriptAmount = 0}
local HOOK_UNITNAME_UPDATE = {ScriptAmount = 0}
local HOOK_LOAD_SCREEN = {ScriptAmount = 0}
local HOOK_PLAYER_LOGON = {ScriptAmount = 0}
local HOOK_MOD_INITIALIZATION = {ScriptAmount = 0}
local HOOK_MOD_DEINITIALIZATION = {ScriptAmount = 0}
local HOOK_COMM_RECEIVED_MESSAGE = {ScriptAmount = 0}
local HOOK_COMM_SEND_MESSAGE = {ScriptAmount = 0}
local HOOK_OPTION_CHANGED = {ScriptAmount = 0}
local HOOK_MOD_OPTION_CHANGED = {ScriptAmount = 0}
local HOOK_NAMEPLATE_DESTRUCTOR = {ScriptAmount = 0}

platerInternal.HOOK_MOD_OPTION_CHANGED = HOOK_MOD_OPTION_CHANGED --triggered from Plater.ScriptingOptions.lua

local PLATER_GLOBAL_MOD_ENV = {}  -- contains modEnv for each mod, identified by "<mod name>"
local PLATER_GLOBAL_SCRIPT_ENV = {} -- contains modEnv for each script, identified by "<script name>"

--> cvars just to make them easier to read
local CVAR_ENABLED = "1"
local CVAR_DISABLED = "0"

--> cache some common used member strings for better reading
local MEMBER_UNITID = "namePlateUnitToken"
local MEMBER_GUID = "namePlateUnitGUID"
local MEMBER_NPCID = "namePlateNpcId"
local MEMBER_QUEST = "namePlateIsQuestObjective"
local MEMBER_REACTION = "namePlateUnitReaction"
local MEMBER_RANGE = "namePlateInRange"
local MEMBER_NOCOMBAT = "namePlateNoCombat"
local MEMBER_NAME = "namePlateUnitName"
local MEMBER_NAMELOWER = "namePlateUnitNameLower"
local MEMBER_TARGET = "namePlateIsTarget"

--> cache nameplate types for better reading the code
local ACTORTYPE_FRIENDLY_PLAYER = "friendlyplayer"
local ACTORTYPE_FRIENDLY_NPC = "friendlynpc"
local ACTORTYPE_ENEMY_PLAYER = "enemyplayer"
local ACTORTYPE_ENEMY_NPC = "enemynpc"
local ACTORTYPE_PLAYER = "player"

local class_specs_coords = {
	[577] = {128/512, 192/512, 256/512, 320/512}, --> havoc demon hunter
	[581] = {192/512, 256/512, 256/512, 320/512}, --> vengeance demon hunter

	[250] = {0, 64/512, 0, 64/512}, --> blood dk
	[251] = {64/512, 128/512, 0, 64/512}, --> frost dk
	[252] = {128/512, 192/512, 0, 64/512}, --> unholy dk
	
	[102] = {192/512, 256/512, 0, 64/512}, -->  druid balance
	[103] = {256/512, 320/512, 0, 64/512}, -->  druid feral
	[104] = {320/512, 384/512, 0, 64/512}, -->  druid guardian
	[105] = {384/512, 448/512, 0, 64/512}, -->  druid resto

	[253] = {448/512, 512/512, 0, 64/512}, -->  hunter bm
	[254] = {0, 64/512, 64/512, 128/512}, --> hunter marks
	[255] = {64/512, 128/512, 64/512, 128/512}, --> hunter survivor
	
	[62] = {(128/512) + 0.001953125, 192/512, 64/512, 128/512}, --> mage arcane
	[63] = {192/512, 256/512, 64/512, 128/512}, --> mage fire
	[64] = {256/512, 320/512, 64/512, 128/512}, --> mage frost
	
	[268] = {320/512, 384/512, 64/512, 128/512}, --> monk bm
	[269] = {448/512, 512/512, 64/512, 128/512}, --> monk ww
	[270] = {384/512, 448/512, 64/512, 128/512}, --> monk mw
	
	[65] = {0, 64/512, 128/512, 192/512}, --> paladin holy
	[66] = {64/512, 128/512, 128/512, 192/512}, --> paladin protect
	[70] = {(128/512) + 0.001953125, 192/512, 128/512, 192/512}, --> paladin ret
	
	[256] = {192/512, 256/512, 128/512, 192/512}, --> priest disc
	[257] = {256/512, 320/512, 128/512, 192/512}, --> priest holy
	[258] = {(320/512) + (0.001953125 * 4), 384/512, 128/512, 192/512}, --> priest shadow
	
	[259] = {64/512, 128/512, 384/512, 448/512}, --> rogue assassination
	[260] = {0, 64/512, 384/512, 448/512}, --> rogue outlaw
	[261] = {0, 64/512, 192/512, 256/512}, --> rogue sub
	
	[262] = {64/512, 128/512, 192/512, 256/512}, --> shaman elemental
	[263] = {128/512, 192/512, 192/512, 256/512}, --> shamel enhancement
	[264] = {192/512, 256/512, 192/512, 256/512}, --> shaman resto
	
	[265] = {256/512, 320/512, 192/512, 256/512}, --> warlock aff
	[266] = {320/512, 384/512, 192/512, 256/512}, --> warlock demo
	[267] = {384/512, 448/512, 192/512, 256/512}, --> warlock destro
	
	[71] = {448/512, 512/512, 192/512, 256/512}, --> warrior arms
	[72] = {0, 64/512, 256/512, 320/512}, --> warrior fury
	[73] = {64/512, 128/512, 256/512, 320/512}, --> warrior protect
	
	[1467] = {256/512, 320/512, 256/512, 320/512}, --> evoker devastation
	[1468] = {320/512, 384/512, 256/512, 320/512}, --> evoker preservation
	[1473] = {384/512, 448/512, 256/512, 320/512}, --> evoker augmentation
}

--localization
Plater.AnchorNames = {
	LOC["OPTIONS_ANCHOR_TOPLEFT"],
	LOC["OPTIONS_ANCHOR_LEFT"],
	LOC["OPTIONS_ANCHOR_BOTTOMLEFT"],
	LOC["OPTIONS_ANCHOR_BOTTOM"],
	LOC["OPTIONS_ANCHOR_BOTTOMRIGHT"],
	LOC["OPTIONS_ANCHOR_RIGHT"],
	LOC["OPTIONS_ANCHOR_TOPRIGHT"],
	LOC["OPTIONS_ANCHOR_TOP"],
	LOC["OPTIONS_ANCHOR_CENTER"],
	LOC["OPTIONS_ANCHOR_INNERLEFT"],
	LOC["OPTIONS_ANCHOR_INNERRIGHT"],
	LOC["OPTIONS_ANCHOR_INNERTOP"],
	LOC["OPTIONS_ANCHOR_INNERBOTTOM"],
}

Plater.AnchorNamesByPhraseId = {
	"OPTIONS_ANCHOR_TOPLEFT",
	"OPTIONS_ANCHOR_LEFT",
	"OPTIONS_ANCHOR_BOTTOMLEFT",
	"OPTIONS_ANCHOR_BOTTOM",
	"OPTIONS_ANCHOR_BOTTOMRIGHT",
	"OPTIONS_ANCHOR_RIGHT",
	"OPTIONS_ANCHOR_TOPRIGHT",
	"OPTIONS_ANCHOR_TOP",
	"OPTIONS_ANCHOR_CENTER",
	"OPTIONS_ANCHOR_INNERLEFT",
	"OPTIONS_ANCHOR_INNERRIGHT",
	"OPTIONS_ANCHOR_INNERTOP",
	"OPTIONS_ANCHOR_INNERBOTTOM",
}

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> cached value ~cache
--Plater allocate several values in memory to save performance (cpu), this may increase memory usage
--example: intead of querying Plater.db.profile.tank it just hold a pointer to that table in the variable DB_AGGRO_TANK_COLORS, and this pointer is updated when the user changes something in the options panel

	local DB_NUMBER_REGION_EAST_ASIA
	local DB_TICK_THROTTLE
	local DB_LERP_COLOR
	local DB_LERP_COLOR_SPEED
	local DB_PLATE_CONFIG
	local DB_HOVER_HIGHLIGHT
	local DB_CLASS_COLORS

	--auras
	local DB_AURA_ENABLED
	local DB_AURA_ALPHA
	local DB_AURA_SEPARATE_BUFFS

	local DB_USE_UIPARENT
	
	local DB_UNITCOLOR_CACHE = {}
	local DB_UNITCOLOR_SCRIPT_CACHE = {}

	local IS_USING_DETAILS_INTEGRATION

	local DB_TRACK_METHOD
	local DB_BORDER_COLOR_R
	local DB_BORDER_COLOR_G
	local DB_BORDER_COLOR_B
	local DB_BORDER_COLOR_A
	local DB_BORDER_THICKNESS
	local DB_AGGRO_CHANGE_HEALTHBAR_COLOR
	local DB_AGGRO_CHANGE_NAME_COLOR
	local DB_AGGRO_CHANGE_BORDER_COLOR
	local DB_AGGRO_CAN_CHECK_NOTANKAGGRO
	local DB_TARGET_SHADY_ENABLED
	local DB_TARGET_SHADY_ALPHA
	local DB_TARGET_SHADY_COMBATONLY

	local DB_NAME_NPCENEMY_ANCHOR
	local DB_NAME_NPCFRIENDLY_ANCHOR
	local DB_NAME_PLAYERENEMY_ANCHOR
	local DB_NAME_PLAYERFRIENDLY_ANCHOR

	local DB_DO_ANIMATIONS
	local DB_ANIMATION_TIME_DILATATION

	local DB_USE_RANGE_CHECK
	local DB_USE_NON_TARGETS_ALPHA
	local DB_USE_FOCUS_TARGET_ALPHA
	local DB_USE_ALPHA_FRIENDLIES
	local DB_USE_ALPHA_ENEMIES
	local DB_USE_QUICK_HIDE
	local DB_SHOW_HEALTHBARS_FOR_NOT_ATTACKABLE

	local DB_TEXTURE_CASTBAR
	local DB_TEXTURE_CASTBAR_BG
	local DB_TEXTURE_HEALTHBAR
	local DB_TEXTURE_HEALTHBAR_BG

	local DB_CASTBAR_HIDE_ENEMIES
	local DB_CASTBAR_HIDE_FRIENDLY

	---@type plater_spelldata[]
	local DB_CAPTURED_SPELLS = {}

	---@type plater_spelldata[]
	local DB_CAPTURED_CASTS = {}

	--store the aggro color table for tanks and dps
	local DB_AGGRO_TANK_COLORS
	local DB_AGGRO_DPS_COLORS

	--store if the no combat alpha is enabled
	local DB_NOT_COMBAT_ALPHA_ENABLED
	
	local DB_USE_HEALTHCUTOFF = false
	local DB_HEALTHCUTOFF_AT = 0.2
	local DB_HEALTHCUTOFF_AT_UPPER = 0.8
	
	--store translit option
	local DB_USE_NAME_TRANSLIT = false
	local TRANSLIT_MARK = "*"
	
	--store the npc id cache
	local DB_NPCIDS_CACHE = {}

	Plater.ScriptAura = {}
	local SCRIPT_AURA_TRIGGER_CACHE = Plater.ScriptAura

	Plater.ScriptCastBar = {}
	local SCRIPT_CASTBAR_TRIGGER_CACHE = Plater.ScriptCastBar

	Plater.ScriptUnit = {}
	local SCRIPT_UNIT_TRIGGER_CACHE = Plater.ScriptUnit
	
	--spell animations - store a table with information about animation for spells
	local SPELL_WITH_ANIMATIONS = {}
	--cache this inside plater object to access it from the animation editor
	Plater.SPELL_WITH_ANIMATIONS = SPELL_WITH_ANIMATIONS

	--store players which have the tank role in the group
	local TANK_CACHE = {}

	--store pet GUIDs
	---@type plater_petinfo[]
	local PET_CACHE = {}
	--store pets summoned by the player it self
	Plater.PlayerPetCache = {}

	--store if the player is in combat (not reliable, toggled at regen switch)
	local PLAYER_IN_COMBAT

	--store if the player is not inside an instance
	local IS_IN_OPEN_WORLD = true
	--store if the player is inside a instance (raid or dungeon)
	local IS_IN_INSTANCE = false

	--if true, the animation will update its settings before play
	local IS_EDITING_SPELL_ANIMATIONS = false
	
	local HOOKED_BLIZZARD_PLATEFRAMES = {}
	local ENABLED_BLIZZARD_PLATEFRAMES = {}
	local SUPPORT_BLIZZARD_PLATEFRAMES = false
	local NUM_NAMEPLATES_ON_SCREEN = 0
	local NAMEPLATES_ON_SCREEN_CACHE = {}
	
	local CLASS_INFO_CACHE = {}

	--store a list of friendly players in the player friends list
	Plater.FriendsCache = {}
	
	--store quests the player is in
	Plater.QuestCache = {}
	--store only campaign quests
	Plater.QuestCacheCampaign = {}
	
	--cache the profile settings for each actor type on this table, so scripts can have access to profile
	Plater.ActorTypeSettingsCache = { --private
		RefreshID = -1,
		--plate holder tables, they will be overriden when updating the cache
		[ACTORTYPE_FRIENDLY_PLAYER] = {},
		[ACTORTYPE_FRIENDLY_NPC] = {},
		[ACTORTYPE_ENEMY_PLAYER] = {},
		[ACTORTYPE_ENEMY_NPC] = {},
		[ACTORTYPE_PLAYER] = {},
	}

	--update the settings cache for scritps
	--this is a table with a copy of the settings from the profile so can be safelly accessed by scripts
	function Plater.UpdateSettingsCache() --private
		if (Plater.ActorTypeSettingsCache.RefreshID >= PLATER_REFRESH_ID) then
			return
		end
		
		local namePlateConfig = Plater.db.profile.plate_config
		Plater.ActorTypeSettingsCache [ACTORTYPE_FRIENDLY_PLAYER] = DF.table.copy ({}, namePlateConfig [ACTORTYPE_FRIENDLY_PLAYER])
		Plater.ActorTypeSettingsCache [ACTORTYPE_FRIENDLY_NPC] = DF.table.copy ({}, namePlateConfig [ACTORTYPE_FRIENDLY_NPC])
		Plater.ActorTypeSettingsCache [ACTORTYPE_ENEMY_PLAYER] = DF.table.copy ({}, namePlateConfig [ACTORTYPE_ENEMY_PLAYER])
		Plater.ActorTypeSettingsCache [ACTORTYPE_ENEMY_NPC] = DF.table.copy ({}, namePlateConfig [ACTORTYPE_ENEMY_NPC])
		Plater.ActorTypeSettingsCache [ACTORTYPE_PLAYER] = DF.table.copy ({}, namePlateConfig [ACTORTYPE_PLAYER])
		
		Plater.ActorTypeSettingsCache.RefreshID = PLATER_REFRESH_ID
	end
	
	function Plater.InitLDB()
		if LDB then
			local databroker = LDB:NewDataObject ("Plater", {
				type = "data source",
				icon = [[Interface\AddOns\Plater\images\cast_bar]],
				text = "Plater",
				showInCompartment = true,
				
				HotCornerIgnore = true,
				
				OnClick = function (self, button)
				
					if (button == "LeftButton") then
						if (PlaterOptionsPanelFrame and PlaterOptionsPanelFrame:IsShown()) then
							PlaterOptionsPanelFrame:Hide()
							return true
						end
						Plater.OpenOptionsPanel()
					
					elseif (button == "RightButton") then
					
						GameTooltip:Hide()
						local GameCooltip = GameCooltip2
						
						GameCooltip:Reset()
						GameCooltip:SetType ("menu")
						GameCooltip:SetOption ("ButtonsYMod", -5)
						GameCooltip:SetOption ("HeighMod", 5)
						GameCooltip:SetOption ("TextSize", 10)
						
						--> disable minimap icon
						local toggle_minimap = function()
							PlaterDBChr.minimap.hide = not PlaterDBChr.minimap.hide
							
							if (PlaterDBChr.minimap.hide) then
								LDBIcon:Hide ("Plater")
							else
								LDBIcon:Show ("Plater")
							end
							LDBIcon:Refresh ("Plater", PlaterDBChr.minimap)
						end
						
						local toggle_compartment = function()
							if LDBIcon:IsButtonInCompartment("Plater") then
								LDBIcon:RemoveButtonFromCompartment("Plater")
							else
								LDBIcon:AddButtonToCompartment("Plater")
							end
						end
						
						GameCooltip:AddMenu (1, function() Plater.EnableProfiling(true) end, true, nil, nil, "Start profiling", nil, true)
						GameCooltip:AddIcon ([[Interface\Addons\Plater\media\sphere_full_64]], 1, 1, 14, 14, 0, 1, 0, 1, "red")
						GameCooltip:AddMenu (1, function() Plater.DisableProfiling() end, true, nil, nil, "Stop profiling", nil, true)
						GameCooltip:AddIcon ([[Interface\Addons\Plater\media\square_64]], 1, 1, 14, 14, 0, 1, 0, 1, "blue")
						GameCooltip:AddMenu (1, function() Plater.ShowPerfData() end, true, nil, nil, "Show profiling data", nil, true)
						GameCooltip:AddIcon ([[Interface\Addons\Plater\media\eye_64]], 1, 1, 14, 14, 0, 1, 0, 1, "green")
						GameCooltip:AddLine ("$div")
						GameCooltip:AddMenu (1, toggle_minimap, true, nil, nil, "Hide/Show Minimap Icon", nil, true)
						GameCooltip:AddIcon ([[Interface\Buttons\UI-Panel-HideButton-Disabled]], 1, 1, 14, 14, 7/32, 24/32, 8/32, 24/32, "gray")
						GameCooltip:AddMenu (1, toggle_compartment, true, nil, nil, "Hide/Show Compartment Entry", nil, true)
						GameCooltip:AddIcon ([[Interface\Buttons\UI-Panel-HideButton-Disabled]], 1, 1, 14, 14, 7/32, 24/32, 8/32, 24/32, "gray")
						
						--GameCooltip:SetBackdrop (1, _detalhes.tooltip_backdrop, nil, _detalhes.tooltip_border_color)
						GameCooltip:SetWallpaper (1, [[Interface\SPELLBOOK\Spellbook-Page-1]], {.6, 0.1, 0.64453125, 0}, {.8, .8, .8, 0.2}, true)
						
						GameCooltip:SetOwner (self, "topright", "bottomleft")
						GameCooltip:ShowCooltip()
					
					end
					
				end,
				OnTooltipShow = function (tooltip)
					tooltip:AddLine ("Plater Nameplates", 1, 1, 1)
					tooltip:AddLine ("|cFFCFCFCFLeft click|r: Show/Hide Options Window")
					tooltip:AddLine ("|cFFCFCFCFRight click|r: Quick Menu")
				end,
			})
			
			if (databroker and not LDBIcon:IsRegistered ("Plater")) then
				PlaterDBChr.minimap = PlaterDBChr.minimap or {}
				LDBIcon:Register ("Plater", databroker, PlaterDBChr.minimap)
				if not PlaterDBChr.minimap.showInCompartment == true then
					--LDBIcon:AddButtonToCompartment("Plater") -- this is opt-in in LDBIcon (for now)
				end
			end
			
			Plater.databroker = databroker
		end

	end
	
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> character specific abilities and spells ~spells

	-- ~execute
	---update if can use execute indicators - this function needs to be updated when a new execute spell is added, removed, modified
	---in scripts you can use Plater.SetExecuteRange or override this function completelly
	function Plater.GetHealthCutoffValue(getOnly)
		Plater.SetExecuteRange (false)
		
		local lowerEnabled, upperEnabled = Plater.db.profile.health_cutoff, Plater.db.profile.health_cutoff_upper
			
		if (not (lowerEnabled or upperEnabled)) then
			if not getOnly then
				return
			end
		end
		
		local lowExecute, highExecute = nil, nil
		
		if IS_WOW_PROJECT_MAINLINE then
			--retail
			
			--small helper
			local isTalentLearned = function(nodeID)
				local talentConfig = C_ClassTalents.GetActiveConfigID()
				local nodeInfo = talentConfig and nodeID and C_Traits.GetNodeInfo(talentConfig, nodeID)
				return nodeInfo and nodeInfo.entryIDsWithCommittedRanks and nodeInfo.entryIDsWithCommittedRanks[1] and true or false
			end
			
			local classLoc, class = UnitClass ("player")
			local spec = GetSpecialization()
			if (spec and class) then
			
				if (class == "PRIEST") then
					-- SW:D is available to all priest specs
					if IsPlayerSpell(32379) then
						lowExecute = 0.20
					end
					
				elseif (class == "MAGE") then
					if IsPlayerSpell(2948) then -- Scorch
						lowExecute = 0.3
						if isTalentLearned(449349) then --Sunfury Execution (for Scorch)
							lowExecute = 0.35
						end
					end
					if IsPlayerSpell(205026) then --Firestarter
						highExecute = 0.9
					end
					if IsPlayerSpell(384581) then -- Arcane Bombardment
						lowExecute = 0.35
					end
					
				elseif (class == "WARRIOR") then
					-- Execute is baseline
					if IsPlayerSpell(163201) then
						local using_Massacre = IsPlayerSpell(281001) or IsPlayerSpell(206315)
						lowExecute = using_Massacre and 0.35 or 0.2
						--local using_Condemn = IsPlayerSpell(317320) -- that's not really used anymore...
						--highExecute = using_Condemn and 0.8 or nil
					end
					
				elseif (class == "HUNTER") then
					if IsPlayerSpell(53351) or IsPlayerSpell(320976) then -- Kill Shot
						lowExecute = 0.2
					end
					if IsPlayerSpell(273887) then --> is using killer instinct?
						lowExecute = 0.35
					end
					if IsPlayerSpell(260228) then --> Careful Aim
						highExecute = 0.7
					end
					
				elseif (class == "PALADIN") then
					-- hammer of wrath
					if IsPlayerSpell(24275) then
						lowExecute = 0.2
					end
					
				elseif (class == "MONK") then
					--Touch of Death
					if IsPlayerSpell(322113) then
						lowExecute = 0.15
					end
				
				elseif (class == "WARLOCK") then				
					if IsPlayerSpell(17877) then --Shadowburn
						lowExecute = 0.20
					elseif IsSpellKnownOrOverridesKnown(198590) then --Drain Soul
						lowExecute = 0.20
					end
				
				elseif (class == "ROGUE") then				
					if IsPlayerSpell(328085) then --Blindside
						lowExecute = 0.35
					end
				
				elseif (class == "DEATHKNIGHT") then
					if IsPlayerSpell(343294) then --Soul Reaper
						lowExecute = 0.35
					end
				
				end
			end
		
		else
			-- WotLK and classic
			local classLoc, class = UnitClass ("player")
			if (class) then
				if (class == "WARRIOR") then
					-- Execute
					if GetSpellInfo(GetSpellInfo(5308)) then
						lowExecute = 0.2
					end
				elseif (class == "PALADIN") then
					-- Hammer of Wrath
					if GetSpellInfo(GetSpellInfo(24275)) then
						lowExecute = 0.2
					end
				elseif (class == "WARLOCK") then
					-- Decimation
					if IsPlayerSpell(63156) or IsPlayerSpell(63158) then
						lowExecute = 0.25
					else
						lowExecute = 0.25
					end
				elseif (class == "HUNTER") then
					-- Kill Shot
					if GetSpellInfo(GetSpellInfo(53351)) then
						lowExecute = 0.2
					end
				elseif (class == "PRIEST") then
					if IS_WOW_PROJECT_CLASSIC_WRATH then -- why wrath again?... can't remember
						for i = 1, 6 do
							local enabled, _, glyphSpellID = GetGlyphSocketInfo(i)
							if enabled and glyphSpellID then
								if glyphSpellID == 55682 then --Glyph of Shadow Word: Death
									lowExecute = 0.35
									break
								end
							end
						end
					end
					
					-- SW:D is available to all priest specs
					if IsPlayerSpell(32379) then
						lowExecute = 0.25
					end
				end
			end
		
		end
		
		if not getOnly then
			Plater.SetExecuteRange (true, lowerEnabled and lowExecute or nil, upperEnabled and highExecute or nil)
		end
		return lowerEnabled and lowExecute or nil, upperEnabled and highExecute or nil
	end	

	---range check ~range
	---@param plateFrame plateframe
	---@param onAdded boolean
	function Plater.CheckRange (plateFrame, onAdded)
		Plater.StartLogPerformanceCore("Plater-Core", "Update", "CheckRange")
		
		local profile = Plater.db.profile
		local unitFrame = plateFrame.unitFrame
		local castBarFade = unitFrame.castBar.fadeOutAnimation:IsPlaying() --and profile.cast_statusbar_use_fade_effects
		local nameplateAlpha = 1
		local occlusionAlpha = tonumber(GetCVar ("nameplateOccludedAlphaMult")) or 1
		if DB_USE_UIPARENT and profile.honor_blizzard_plate_alpha then
		--if DB_USE_UIPARENT end
			nameplateAlpha = plateFrame:GetAlpha()
		end
		unitFrame.IsInRange = nil
		
		--if is using the no combat alpha and the unit isn't in combat, ignore the range check, no combat alpha is disabled by default
		if (unitFrame.IsSelf) then
			unitFrame.IsInRange = true --player plate is always in range
			
			unitFrame:SetAlpha (nameplateAlpha)

			unitFrame.healthBar:SetAlpha (1)
			if not castBarFade then
				unitFrame.castBar:SetAlpha (1)
			end
			unitFrame.powerBar:SetAlpha (1)
			unitFrame.BuffFrame:SetAlpha (DB_AURA_ALPHA)
			unitFrame.BuffFrame2:SetAlpha (DB_AURA_ALPHA)
			
			Plater.EndLogPerformanceCore("Plater-Core", "Update", "CheckRange")
			return
			
		elseif (plateFrame [MEMBER_NOCOMBAT] or unitFrame.isWidgetOnlyMode) then
			if unitFrame.isWidgetOnlyMode then
				unitFrame:SetAlpha (1)
			elseif nameplateAlpha < profile.not_affecting_combat_alpha and nameplateAlpha >= occlusionAlpha then
				unitFrame:SetAlpha (nameplateAlpha)
			else
				unitFrame:SetAlpha (profile.not_affecting_combat_alpha)
			end
			--unitFrame:SetAlpha (profile.not_affecting_combat_alpha) -- already set if necessary
			unitFrame.healthBar:SetAlpha (1)
			if not castBarFade then
				unitFrame.castBar:SetAlpha (1)
			end
			unitFrame.powerBar:SetAlpha (1)
			unitFrame.BuffFrame:SetAlpha (DB_AURA_ALPHA)
			unitFrame.BuffFrame2:SetAlpha (DB_AURA_ALPHA)
			
			Plater.EndLogPerformanceCore("Plater-Core", "Update", "CheckRange")
			return
		
		--the unit is friendly or not using range check and non targets alpha
		elseif ((not DB_USE_ALPHA_FRIENDLIES and plateFrame [MEMBER_REACTION] >= 5) or (not DB_USE_ALPHA_ENEMIES and plateFrame [MEMBER_REACTION] < 5) or (not DB_USE_RANGE_CHECK and not DB_USE_NON_TARGETS_ALPHA)) then
			unitFrame:SetAlpha (nameplateAlpha)
			unitFrame.healthBar:SetAlpha (1)
			if not castBarFade then
				unitFrame.castBar:SetAlpha (1)
			end
			unitFrame.powerBar:SetAlpha (1)
			unitFrame.BuffFrame:SetAlpha (DB_AURA_ALPHA)
			unitFrame.BuffFrame2:SetAlpha (DB_AURA_ALPHA)
			
			plateFrame [MEMBER_RANGE] = true
			unitFrame [MEMBER_RANGE] = true
			
			Plater.EndLogPerformanceCore("Plater-Core", "Update", "CheckRange")
			return
		end
		
		
		--alpha values
		local inRangeAlpha
		local overallRangeCheckAlpha
		local healthBar_rangeCheckAlpha
		local castBar_rangeCheckAlpha
		local buffFrames_rangeCheckAlpha
		local powerBar_rangeCheckAlpha
		local rangeChecker
		local rangeCheckRange
		
		if plateFrame [MEMBER_REACTION] < 5 then
			-- enemy
			inRangeAlpha = profile.range_check_in_range_or_target_alpha
			overallRangeCheckAlpha = profile.range_check_alpha
			healthBar_rangeCheckAlpha = profile.range_check_health_bar_alpha
			castBar_rangeCheckAlpha = profile.range_check_cast_bar_alpha
			buffFrames_rangeCheckAlpha = profile.range_check_buffs_alpha
			powerBar_rangeCheckAlpha = profile.range_check_power_bar_alpha
			rangeChecker = Plater.RangeCheckFunctionEnemy or LibRangeCheck:GetHarmMaxChecker(Plater.RangeCheckRangeEnemy or 40, true)
			rangeCheckRange = Plater.RangeCheckRangeEnemy
			
		else
			-- friendly
			inRangeAlpha = profile.range_check_in_range_or_target_alpha_friendlies
			overallRangeCheckAlpha = profile.range_check_alpha_friendlies
			healthBar_rangeCheckAlpha = profile.range_check_health_bar_alpha_friendlies
			castBar_rangeCheckAlpha = profile.range_check_cast_bar_alpha_friendlies
			buffFrames_rangeCheckAlpha = profile.range_check_buffs_alpha_friendlies
			powerBar_rangeCheckAlpha = profile.range_check_power_bar_alpha_friendlies
			rangeChecker = Plater.RangeCheckFunctionFriendly or LibRangeCheck:GetFriendMaxChecker(Plater.RangeCheckRangeFriendly or 40, true)
			rangeCheckRange = Plater.RangeCheckRangeFriendly
		end
		
		if not rangeChecker then
			rangeChecker = function (unit)
				local minRange, maxRange = (LibRangeCheck:GetRange(unit, nil, true) or 0) <= (rangeCheckRange or 40)
				Plater.EndLogPerformanceCore("Plater-Core", "Update", "CheckRange")
				return maxRange or minRange
			end
			Plater.GetSpellForRangeCheck()
		end

		--this unit is target
		local unitIsTarget = unitFrame.isSoftInteract -- default to softinteract
		local notTheTarget = false
		--when the unit is out of range and isnt target, alpha is multiplied by this amount
		local alphaMultiplier = 0.70

		local healthBar = unitFrame.healthBar
		local castBar = unitFrame.castBar
		local powerBar = unitFrame.powerBar
		local buffFrame1 = unitFrame.BuffFrame
		local buffFrame2 = unitFrame.BuffFrame2		

		--if "units which is not target" is enabled and the player is targetting something else than the player it self
		if ((DB_USE_NON_TARGETS_ALPHA and Plater.PlayerHasTargetNonSelf) or (DB_USE_FOCUS_TARGET_ALPHA and Plater.PlayerHasFocusTargetNonSelf)) then
			if (plateFrame [MEMBER_TARGET]) then
				unitIsTarget = true
			elseif (DB_USE_FOCUS_TARGET_ALPHA and unitFrame.IsFocus) then
				unitIsTarget = true
			else
				notTheTarget = true
				if (profile.transparency_behavior_use_division) then
					alphaMultiplier = 0.5
				end
			end
		end
 
		--is using the range check by ability
		if (DB_USE_RANGE_CHECK and rangeChecker) then
			--check when the unit just has been added to the screen
			local isInRange = rangeChecker (plateFrame [MEMBER_UNITID])

			if (isInRange) then
				--unit is in rage
				unitFrame.IsInRange = true
				
				if (onAdded) then
					--plateFrame.FadedIn = true

					unitFrame:SetAlpha (nameplateAlpha * inRangeAlpha * (notTheTarget and overallRangeCheckAlpha or 1))
					healthBar:SetAlpha (inRangeAlpha * (notTheTarget and healthBar_rangeCheckAlpha or 1))
					if not castBarFade then
						castBar:SetAlpha (inRangeAlpha * (notTheTarget and castBar_rangeCheckAlpha or 1))
					end
					powerBar:SetAlpha (inRangeAlpha * (notTheTarget and powerBar_rangeCheckAlpha or 1))
					buffFrame1:SetAlpha (inRangeAlpha * (notTheTarget and buffFrames_rangeCheckAlpha or 1))
					buffFrame2:SetAlpha (inRangeAlpha * (notTheTarget and buffFrames_rangeCheckAlpha or 1))

					plateFrame [MEMBER_RANGE] = true
					plateFrame.unitFrame [MEMBER_RANGE] = true

				else
					local newAlpha = nameplateAlpha * inRangeAlpha * (notTheTarget and overallRangeCheckAlpha or 1)
					if (not DF:IsNearlyEqual (unitFrame:GetAlpha(), newAlpha, 0.01)) then
						--play animations (animation aren't while in development)
						unitFrame:SetAlpha (nameplateAlpha * inRangeAlpha * (notTheTarget and overallRangeCheckAlpha or 1))
						healthBar:SetAlpha (inRangeAlpha * (notTheTarget and healthBar_rangeCheckAlpha or 1))
						if not castBarFade then
							castBar:SetAlpha (inRangeAlpha * (notTheTarget and castBar_rangeCheckAlpha or 1))
						end
						powerBar:SetAlpha (inRangeAlpha * (notTheTarget and powerBar_rangeCheckAlpha or 1))
						buffFrame1:SetAlpha (inRangeAlpha * (notTheTarget and buffFrames_rangeCheckAlpha or 1))
						buffFrame2:SetAlpha (inRangeAlpha * (notTheTarget and buffFrames_rangeCheckAlpha or 1))
					end

					plateFrame [MEMBER_RANGE] = true
					plateFrame.unitFrame [MEMBER_RANGE] = true
				end
			else
				--unit is out of range
				unitFrame.IsInRange = false
				
				if (onAdded) then
					plateFrame.FadedIn = nil

--					unitFrame:SetAlpha (overallRangeCheckAlpha * (notTheTarget and overallRangeCheckAlpha or 1))
--					healthBar:SetAlpha (healthBar_rangeCheckAlpha * (notTheTarget and healthBar_rangeCheckAlpha or 1))
--					castBar:SetAlpha (castBar_rangeCheckAlpha * (notTheTarget and castBar_rangeCheckAlpha or 1))
--					powerBar:SetAlpha (powerBar_rangeCheckAlpha * (notTheTarget and powerBar_rangeCheckAlpha or 1))
--					buffFrame1:SetAlpha (buffFrames_rangeCheckAlpha * (notTheTarget and buffFrames_rangeCheckAlpha or 1))
--					buffFrame2:SetAlpha (buffFrames_rangeCheckAlpha * (notTheTarget and buffFrames_rangeCheckAlpha or 1))

					unitFrame:SetAlpha ((unitIsTarget and inRangeAlpha or overallRangeCheckAlpha) * nameplateAlpha * (notTheTarget and alphaMultiplier or 1))
					healthBar:SetAlpha ((unitIsTarget and inRangeAlpha or healthBar_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
					if not castBarFade then
						castBar:SetAlpha ((unitIsTarget and inRangeAlpha or castBar_rangeCheckAlpha) * (notTheTarget and alphaMultiplier  or 1))
					end
					powerBar:SetAlpha ((unitIsTarget and inRangeAlpha or powerBar_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
					buffFrame1:SetAlpha ((unitIsTarget and inRangeAlpha or buffFrames_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
					buffFrame2:SetAlpha ((unitIsTarget and inRangeAlpha or buffFrames_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))

					plateFrame [MEMBER_RANGE] = false
					plateFrame.unitFrame [MEMBER_RANGE] = false

				else
					local newAlpha = nameplateAlpha * overallRangeCheckAlpha * (notTheTarget and alphaMultiplier or 1)
					if (not DF:IsNearlyEqual (unitFrame:GetAlpha(), newAlpha, 0.01)) then
						
						--play animations (animation aren't while in development)
--						unitFrame:SetAlpha (overallRangeCheckAlpha * (notTheTarget and overallRangeCheckAlpha or 1))
--						healthBar:SetAlpha (healthBar_rangeCheckAlpha * (notTheTarget and healthBar_rangeCheckAlpha or 1))
--						castBar:SetAlpha (castBar_rangeCheckAlpha * (notTheTarget and castBar_rangeCheckAlpha or 1))
--						powerBar:SetAlpha (powerBar_rangeCheckAlpha * (notTheTarget and powerBar_rangeCheckAlpha or 1))
--						buffFrame1:SetAlpha (buffFrames_rangeCheckAlpha * (notTheTarget and buffFrames_rangeCheckAlpha or 1))
--						buffFrame2:SetAlpha (buffFrames_rangeCheckAlpha * (notTheTarget and buffFrames_rangeCheckAlpha or 1))
						
						unitFrame:SetAlpha ((unitIsTarget and inRangeAlpha or overallRangeCheckAlpha) * nameplateAlpha * (notTheTarget and alphaMultiplier or 1))
						healthBar:SetAlpha ((unitIsTarget and inRangeAlpha or healthBar_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
						if not castBarFade then
							castBar:SetAlpha ((unitIsTarget and inRangeAlpha or castBar_rangeCheckAlpha) * (notTheTarget and alphaMultiplier  or 1))
						end
						powerBar:SetAlpha ((unitIsTarget and inRangeAlpha or powerBar_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
						buffFrame1:SetAlpha ((unitIsTarget and inRangeAlpha or buffFrames_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
						buffFrame2:SetAlpha ((unitIsTarget and inRangeAlpha or buffFrames_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
					end
					plateFrame [MEMBER_RANGE] = false
					plateFrame.unitFrame [MEMBER_RANGE] = false
				end
			end

		--range check isnt enabled, check is no target alpha is
		elseif (DB_USE_NON_TARGETS_ALPHA) then
			
			--player has a target other than him self?
			if (Plater.PlayerHasTargetNonSelf) then
				--is this unit is the current player target?
				if (unitIsTarget) then
					if (not DF:IsNearlyEqual (unitFrame:GetAlpha(), nameplateAlpha * inRangeAlpha, 0.01)) then
						unitFrame:SetAlpha (nameplateAlpha * inRangeAlpha)
						healthBar:SetAlpha (inRangeAlpha)
						if not castBarFade then
							castBar:SetAlpha (inRangeAlpha)
						end
						powerBar:SetAlpha (inRangeAlpha)
						buffFrame1:SetAlpha (DB_AURA_ALPHA)
						buffFrame2:SetAlpha (DB_AURA_ALPHA)
					end
					plateFrame.FadedIn = true

				else
					--this unit isnt the current player target
					if (not DF:IsNearlyEqual (unitFrame:GetAlpha(), nameplateAlpha * inRangeAlpha * overallRangeCheckAlpha, 0.01)) then
						unitFrame:SetAlpha (nameplateAlpha * inRangeAlpha * overallRangeCheckAlpha)
						healthBar:SetAlpha (inRangeAlpha * healthBar_rangeCheckAlpha)
						if not castBarFade then
							castBar:SetAlpha (inRangeAlpha * castBar_rangeCheckAlpha)
						end
						powerBar:SetAlpha (inRangeAlpha * powerBar_rangeCheckAlpha)
						buffFrame1:SetAlpha (inRangeAlpha * buffFrames_rangeCheckAlpha)
						buffFrame2:SetAlpha (inRangeAlpha * buffFrames_rangeCheckAlpha)
					end
					plateFrame.FadedIn = nil
				end
			else
				--player does not have a target, so just set to regular alpha
				plateFrame.FadedIn = true
				unitFrame:SetAlpha (nameplateAlpha * inRangeAlpha)
				healthBar:SetAlpha (1)
				if not castBarFade then
					castBar:SetAlpha (1)
				end
				powerBar:SetAlpha (1)
				buffFrame1:SetAlpha (DB_AURA_ALPHA)
				buffFrame2:SetAlpha (DB_AURA_ALPHA)
			end
		else
			-- no alpha settings, so just go to default
			plateFrame.FadedIn = true
			unitFrame:SetAlpha (nameplateAlpha * inRangeAlpha)
			healthBar:SetAlpha (1)
			if not castBarFade then
				castBar:SetAlpha (1)
			end
			powerBar:SetAlpha (1)
			buffFrame1:SetAlpha (DB_AURA_ALPHA)
			buffFrame2:SetAlpha (DB_AURA_ALPHA)
		end
		
		Plater.EndLogPerformanceCore("Plater-Core", "Update", "CheckRange")
	end	
	
	local re_GetSpellForRangeCheck = function()
		Plater.GetSpellForRangeCheck()
	end

	--> execute after player logon or when the player changes its spec
	local tryingToUpdateRangeChecker = false
	function Plater.GetSpellForRangeCheck()
		if tryingToUpdateRangeChecker then return end
		Plater.RangeCheckRangeEnemy = nil
		Plater.RangeCheckRangeFriendly = nil
		Plater.RangeCheckFunctionEnemy = nil
		Plater.RangeCheckFunctionFriendly = nil

		local specIndex = (IS_WOW_PROJECT_MAINLINE) and GetSpecialization() or 0
		if (specIndex) then
			local specID = (IS_WOW_PROJECT_MAINLINE) and GetSpecializationInfo (specIndex) or select (3, UnitClass ("player"))
			if (specID and specID ~= 0) then
			
			--[[ -- don't do that here, really. it will reset ranges with talent changes, etc. maybe only for current spec?
				--range check spells fallback update
				local harmCheckers = {}
				local maxHarm = 0
				for range, func in LibRangeCheck:GetHarmCheckers(true) do
					harmCheckers[range] = func
					if maxHarm < range then maxHarm = range end
				end
				local friendCheckers = {}
				local maxFriend = 0
				for range, func in LibRangeCheck:GetFriendCheckers(true) do
					friendCheckers[range] = func
					if maxFriend < range then maxFriend = range end
				end
				if WOW_PROJECT_ID == WOW_PROJECT_MAINLINE then
					for specID, _ in pairs (Plater.SpecList [select (2, UnitClass ("player"))]) do
						if harmCheckers then
							if (PlaterDBChr.spellRangeCheckRangeEnemy [specID] == nil or not harmCheckers[PlaterDBChr.spellRangeCheckRangeEnemy [specID] ]) then
								PlaterDBChr.spellRangeCheckRangeEnemy [specID] = maxHarm --Plater.DefaultSpellRangeList [specID]
							end
						end
						if friendCheckers then
							if (PlaterDBChr.spellRangeCheckRangeFriendly [specID] == nil or not friendCheckers[PlaterDBChr.spellRangeCheckRangeFriendly [specID] ]) then
								PlaterDBChr.spellRangeCheckRangeFriendly [specID] = maxFriend --Plater.DefaultSpellRangeListF [specID]
							end
						end
					end
				else
					local playerClass = select (3, UnitClass ("player"))
					if harmCheckers then
						if (PlaterDBChr.spellRangeCheckRangeEnemy [playerClass] == nil or not harmCheckers[PlaterDBChr.spellRangeCheckRangeEnemy [playerClass] ]) then
							PlaterDBChr.spellRangeCheckRangeEnemy [playerClass] = maxHarm --Plater.DefaultSpellRangeList [playerClass]
						end
					end
					if friendCheckers then
						if (PlaterDBChr.spellRangeCheckRangeFriendly [playerClass] == nil or not friendCheckers[PlaterDBChr.spellRangeCheckRangeFriendly [playerClass] ]) then
							PlaterDBChr.spellRangeCheckRangeFriendly [playerClass] = maxFriend --Plater.DefaultSpellRangeListF [playerClass]
						end
					end
				end
				]]--
			
				--the local character saved variable hold the spell name used for the range check
				Plater.RangeCheckRangeFriendly = PlaterDBChr.spellRangeCheckRangeFriendly [specID] or Plater.DefaultSpellRangeListF [specID] or 40
				Plater.RangeCheckRangeEnemy = PlaterDBChr.spellRangeCheckRangeEnemy [specID] or Plater.DefaultSpellRangeList [specID] or 40
				Plater.RangeCheckFunctionFriendly = LibRangeCheck:GetFriendMaxChecker(Plater.RangeCheckRangeFriendly, true)
				Plater.RangeCheckFunctionEnemy = LibRangeCheck:GetHarmMaxChecker(Plater.RangeCheckRangeEnemy, true)
				
				tryingToUpdateRangeChecker = false
			else
				tryingToUpdateRangeChecker = true
				C_Timer.After (1, re_GetSpellForRangeCheck)
			end
		else
			tryingToUpdateRangeChecker = true
			C_Timer.After (1, re_GetSpellForRangeCheck)
		end
	end

	-- ~tank --todo: make these functions be inside the Plater object
	--true if the 'player' unit is a tank
	--parameter "hasTankAura" is used to force aura scan skip for paladins -> UpdatePlayerTankState -> SPELL_AURA_APPLIED/REMOVED (CLASSIC)
	local function IsPlayerEffectivelyTank(hasTankAura)
		if IS_WOW_PROJECT_MAINLINE then
			local assignedRole = UnitGroupRolesAssigned ("player")
			if (assignedRole == "NONE") then
				local spec = GetSpecialization()
				return spec and GetSpecializationRole (spec) == "TANK"
			end
			return assignedRole == "TANK"
		elseif IS_WOW_PROJECT_CLASSIC_WRATH then
			local assignedRole = UnitGroupRolesAssigned ("player")
			if assignedRole == "NONE" and UnitLevel ("player") >= 10 then
				assignedRole = GetTalentGroupRole(GetActiveTalentGroup())
			end
			local playerIsTank = assignedRole == "TANK"
			
			if not playerIsTank then
				playerIsTank = GetPartyAssignment("MAINTANK", "player") or false
			end
			
			return playerIsTank
		else
		
			local playerIsTank = hasTankAura or false
		
			if not hasTankAura then
				local playerClass = Plater.PlayerClass
				if playerClass == "WARRIOR" then
					local stance = GetShapeshiftFormID() --18 is def, 24 is glad
					playerIsTank = stance == 18 or ((not stance == 24) and IsEquippedItemType("Shields")) -- Defensive Stance or shield (and not glad)
				elseif playerClass == "DRUID" then
					local formId = GetShapeshiftFormID()
					playerIsTank = (formId == 5) or (formId == 8) -- Bear Form or Dire Bear Form...
				elseif playerClass == "PALADIN" then
					for i=1,40 do
					  local spellId = select(10, UnitBuff("player",i))
					  if spellId == 25780 or spellId == 407627 then
						playerIsTank = true
					  end
					end
				elseif playerClass == "ROGUE" then
					for i=1,40 do
					  local spellId = select(10, UnitBuff("player",i))
					  if spellId == 400015 or spellId == 400016 then
						playerIsTank = true
					  end
					end
				elseif playerClass == "WARLOCK" then
					for i=1,40 do
					  local spellId = select(10, UnitBuff("player",i))
					  if spellId == 403789 then
						playerIsTank = true
					  end
					end
				elseif playerClass == "SHAMAN" then
					for i=1,40 do
					  local spellId = select(10, UnitBuff("player",i))
					  if spellId == 408680 then
						playerIsTank = true
					  end
					end
				end
				
			end
			
			-- if the player is assigned as MAINTANK, then treat him as one:
			if not playerIsTank then
				playerIsTank = GetPartyAssignment("MAINTANK", "player") or false
			end
			
			return playerIsTank
		end
	end

	--return true if the unit is in tank role
	local function IsUnitEffectivelyTank (unit)
		if IS_WOW_PROJECT_MAINLINE then
			return UnitGroupRolesAssigned (unit) == "TANK"
		elseif IS_WOW_PROJECT_CLASSIC_WRATH then
			if IsInRaid() then
				return GetPartyAssignment("MAINTANK", unit)
			else
				return UnitGroupRolesAssigned (unit) == "TANK"
			end
		else
			return GetPartyAssignment("MAINTANK", unit)
		end
	end
	
	
	-- toggle Threat Color Mode between tank / dps (CLASSIC)
	function Plater.ToggleThreatColorMode()
		if IS_WOW_PROJECT_NOT_MAINLINE and not IS_WOW_PROJECT_CLASSIC_WRATH then
			Plater.db.profile.tank_threat_colors = not Plater.db.profile.tank_threat_colors
			Plater.RefreshTankCache()
			if Plater.PlayerIsTank then
				print("Plater: Using Tank Threat Colors")
			else
				print("Plater: Using DPS Threat Colors")
			end
		end
	end
	
	local function UpdatePlayerTankState(hasAura)
		if (IsPlayerEffectivelyTank(hasAura)) then
			TANK_CACHE [UnitName ("player")] = true
			Plater.PlayerIsTank = true
		else
			TANK_CACHE [UnitName ("player")] = false
			if IS_WOW_PROJECT_MAINLINE or IS_WOW_PROJECT_CLASSIC_WRATH then
				Plater.PlayerIsTank = false
			else
				Plater.PlayerIsTank = false or Plater.db.profile.tank_threat_colors
			end
		end
	end
	
	--iterate among group members and store the names of all tanks in the group
	--this is called when the player enter, leave or when the group roster is changed
	--tank cache is used mostly in the aggro check to know if the player is a tank
	function Plater.RefreshTankCache() --private
		Plater.PlayerIsTank = false
	
		wipe (TANK_CACHE)
		
		--add the player to the tank pool if the player is a tank
		UpdatePlayerTankState()
		
		--search for tanks in the raid
		if (IsInRaid()) then
			for i = 1, GetNumGroupMembers() do
				if (IsUnitEffectivelyTank ("raid" .. i)) then
					if (not UnitIsUnit ("raid" .. i, "player")) then
						local unitName = UnitName ("raid" .. i)
						if unitName ~= UNKNOWN then
							TANK_CACHE [unitName] = true
						end
					end
				end
			end
		
		--is in group and is inside a dungeon
		--there's only one tank on dungeon but dps may see if a unit is not in the tank aggro
		elseif (IsInGroup() and Plater.ZoneInstanceType == "party") then
			for i = 1, GetNumGroupMembers() -1 do
				if (IsUnitEffectivelyTank ("party" .. i)) then
					if (not UnitIsUnit ("party" .. i, "player")) then
						local unitName = UnitName ("party" .. i)
						if unitName ~= UNKNOWN then
							TANK_CACHE [unitName] = true
						end
					end
				end
			end
		end
	end

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> general unit functions

	--> return a table with points on where the unitFrame is attached
	--these points are hardcoded in the UpdatePlateSize() function
	function Plater.GetPoints (unitFrame)
		local points = {
			{"topleft", unitFrame.PlateFrame, "topleft", 0, 0},
			{"bottomright", unitFrame.PlateFrame, "bottomright", 0, 0},
		}
		return points
	end

	--> return an iterator with all namepaltes on the screen
	function Plater.GetAllShownPlates() --private
		return C_NamePlate.GetNamePlates()
	end

	--> returns if the unit is tapped (gray health color when another player hit the unit first) 
	function Plater.IsUnitTapDenied (unitID)
		return unitID and not UnitPlayerControlled (unitID) and UnitIsTapDenied (unitID)
	end

	--> returns what member from the profile need to be used, since there's entries for in combat and out of combat
	function Plater.GetHashKey (inCombat) --private
		if (inCombat or (inCombat == nil and PLAYER_IN_COMBAT)) then
			return "cast_incombat", "health_incombat", "mana_incombat"
		else
			return "cast", "health", "mana"
		end
	end

	--> return true if the resource bar should shown above the nameplate in the current target nameplate
	function Plater.IsShowingResourcesOnTarget() --private
		return PlaterDBChr.resources_on_target
	end
	
	--> when the player enter in the world, wait a few seconds to get the guild name data
	local delayed_guildname_check = function()
		Plater.PlayerGuildName = GetGuildInfo ("player")
		if (not Plater.PlayerGuildName or Plater.PlayerGuildName == "") then
			Plater.PlayerGuildName = "ThePlayerHasNoGuildName/30Char"
		end
	end
	
	--> run a scheduled update for a nameplate, functions can create schedules when some events are triggered when the client doesn't have the data yet
	function Plater.RunScheduledUpdate (timerObject) --private
		Plater.StartLogPerformanceCore("Plater-Core", "Update", "RunScheduledUpdate")

		local unitId = timerObject.unitId
		---@type plateframe
		local plateFrame = C_NamePlate.GetNamePlateForUnit (unitId)
		
		if (plateFrame) then
			--save user input data (usualy set from scripts) before call the unit added event
				local unitFrame = plateFrame.unitFrame
				local customHealthBarWidth = unitFrame.customHealthBarWidth
				local customHealthBarHeight = unitFrame.customHealthBarHeight
				
				local customCastBarWidth = unitFrame.customCastBarWidth
				local customCastBarHeight = unitFrame.customCastBarHeight
				
				local customPowerBarWidth = unitFrame.customPowerBarWidth
				local customPowerBarHeight = unitFrame.customPowerBarHeight
				
				local customBorderColor = unitFrame.customBorderColor
			
			--full refresh the nameplate, this will override user data from scripts
			Plater.RunFunctionForEvent ("NAME_PLATE_UNIT_REMOVED", unitId)
			Plater.RunFunctionForEvent ("NAME_PLATE_UNIT_ADDED", unitId)
			
			--restore user input data
				unitFrame.customHealthBarWidth = customHealthBarWidth
				unitFrame.customHealthBarHeight = customHealthBarHeight
				
				unitFrame.customCastBarWidth = customCastBarWidth
				unitFrame.customCastBarHeight = customCastBarHeight
				
				unitFrame.customPowerBarWidth = customPowerBarWidth
				unitFrame.customPowerBarHeight = customPowerBarHeight

				if (	customHealthBarWidth or
					customHealthBarHeight or
					customCastBarWidth or
					customCastBarHeight or
					customPowerBarWidth or
					customPowerBarHeight
				) then
					Plater.UpdatePlateSize (plateFrame)
				end
				
				unitFrame.customBorderColor = customBorderColor
				if (unitFrame.customBorderColor) then
					Plater.UpdateBorderColor (plateFrame.unitFrame)
				end
		end
		
		Plater.EndLogPerformanceCore("Plater-Core", "Update", "RunScheduledUpdate")
	end

	---run a delayed update on the namepalte, this is used when the client receives an information from the server but does not update the state immediately
	---this usualy happens with faction and flag changes
	---@param plateFrame plateframe
	---@param passedUnitId string|nil
	---@param scheduleTime number|nil
	function Plater.ScheduleUpdateForNameplate (plateFrame, passedUnitId, scheduleTime) --private
		local unitId = passedUnitId or plateFrame [MEMBER_UNITID]
		if not unitId and plateFrame.HasUpdateScheduled then -- well... fuck.
			plateFrame.HasUpdateScheduled:Cancel()
			return
		end
		
		--check if there's already an update scheduled for this unit
		if (plateFrame.HasUpdateScheduled and not plateFrame.HasUpdateScheduled:IsCancelled()) then
			if unitId and (not plateFrame.HasUpdateScheduled.unitId or plateFrame.HasUpdateScheduled.unitId ~= unitId) then
				plateFrame.HasUpdateScheduled:Cancel()
			else
				return
			end
		end
		
		plateFrame.HasUpdateScheduled = C_Timer.NewTimer (scheduleTime or 0, Plater.RunScheduledUpdate) --scheduleTime or next frame
		plateFrame.HasUpdateScheduled.unitId = unitId
	end

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> settings functions

	-- ~profile
	--refreshes the values for the profile when the profile is loaded or changed
	function Plater:RefreshConfig() --private
		platerInternal.ScriptTriggers.WipeDeprecatedScriptTriggersFromProfile(Plater.db.profile)

		Plater.IncreaseRefreshID()

		Plater.RefreshDBUpvalues()
		
		Plater.UpdateAllPlates()
		
		if (PlaterOptionsPanelFrame) then
			PlaterOptionsPanelFrame.RefreshOptionsFrame()
		end
		
		Plater.UpdateSettingsCache()
	end
	
	function Plater:RefreshConfigProfileChanged() --private
		Plater:RefreshConfig()
		
		--call the user to /reload his UI
		DF:ShowPromptPanel ("Plater profile changed, do you want /reload now (recommended)?", function() ReloadUI() end, function() end, true, 500)
	end
	
	--~save ~cvar
	local cvars_to_store = {
		["NamePlateClassificationScale"] = true,
		["NamePlateHorizontalScale"] = true,
		["NamePlateVerticalScale"] = true,
		["ShowClassColorInNameplate"] = true,
		["ShowNamePlateLoseAggroFlash"] = true,
		["nameplateGlobalScale"] = true,
		["nameplateLargerScale"] = true,
		["nameplateLargeTopInset"] = true,
		["nameplateLargeBottomInset"] = true,
		["nameplateMaxDistance"] = true,
		["nameplatePlayerMaxDistance"] = true,
		["nameplateMinScale"] = true,
		["nameplateMotion"] = true,
		["nameplateMotionSpeed"] = true,
		["nameplateOccludedAlphaMult"] = true,
		["nameplateOtherAtBase"] = true,
		["nameplateOtherTopInset"] = true,
		["nameplateOtherBottomInset"] = true,
		["nameplateOverlapV"] = true,
		["nameplateOverlapH"] = true,
		["nameplatePersonalHideDelaySeconds"] = true,
		["nameplatePersonalShowAlways"] = true,
		["nameplatePersonalShowInCombat"] = true,
		["nameplatePersonalShowWithTarget"] = true,
		["nameplateResourceOnTarget"] = (IS_WOW_PROJECT_MAINLINE),
		["nameplateSelectedScale"] = true,
		["nameplateSelfAlpha"] = (IS_WOW_PROJECT_MAINLINE),
		["nameplateSelfBottomInset"] = (IS_WOW_PROJECT_MAINLINE),
		["nameplateSelfScale"] = (IS_WOW_PROJECT_MAINLINE),
		["nameplateSelfTopInset"] = (IS_WOW_PROJECT_MAINLINE),
		["nameplateShowAll"] = true,
		["nameplateShowEnemies"] = true,
		["nameplateShowEnemyGuardians"] = true,
		["nameplateShowEnemyMinions"] = true,
		["nameplateShowEnemyMinus"] = true,
		["nameplateShowEnemyPets"] = true,
		["nameplateShowEnemyTotems"] = true,
		["nameplateShowFriends"] = true,
		["nameplateShowFriendlyNPCs"] = true,
		["nameplateShowFriendlyMinions"] = true,
		["nameplateShowFriendlyPets"] = true,
		["nameplateShowFriendlyGuardians"] = true,
		["nameplateShowFriendlyTotems"] = true,
		["nameplateShowOnlyNames"] = true,
		["nameplateShowSelf"] = (IS_WOW_PROJECT_MAINLINE),
		["nameplateTargetBehindMaxDistance"] = true,
		["clampTargetNameplateToScreen"] = true,
		["nameplateTargetRadialPosition"] = true,
		["nameplateSelectedAlpha"] = true,
		["nameplateNotSelectedAlpha"] = (IS_WOW_PROJECT_NOT_MAINLINE),
		["nameplateRemovalAnimation"] = (IS_WOW_PROJECT_NOT_MAINLINE),
		["nameplateMinAlpha"] = true,
		["nameplateMinAlphaDistance"] = true,
		["nameplateShowDebuffsOnFriendly"] = true,
		["SoftTargetIconGameObject"] = (IS_WOW_PROJECT_MAINLINE),
		["SoftTargetInteract"] = (IS_WOW_PROJECT_MAINLINE),
		["SoftTargetNameplateInteract"] = (IS_WOW_PROJECT_MAINLINE),
	}
	
	local cvars_to_store_lower = {}
	for CVarName in pairs (cvars_to_store) do
		cvars_to_store_lower[lower(CVarName)] = CVarName
	end
	
	--keep this separate for now, with only stuff that NEEDS restoring in order
	local function cvar_restore_order(v1, v2)
		local restoreOrder = {
			["nameplateShowFriends"] = 1,
			["nameplateShowFriendlyNPCs"] = 2,
			["nameplateShowFriendlyMinions"] = 3,
			["nameplateShowFriendlyPets"] = 4,
			["nameplateShowFriendlyGuardians"] = 5,
			["nameplateShowFriendlyTotems"] = 6,
			["nameplateShowEnemies"] = 7,
			["nameplateShowEnemyNPCs"] = 8,
			["nameplateShowEnemyMinions"] = 9,
			["nameplateShowEnemyPets"] = 10,
			["nameplateShowEnemyGuardians"] = 11,
			["nameplateShowEnemyTotems"] = 12,
		}
		
		local order1, order2 = restoreOrder[v1], restoreOrder[v2]
		
		if order1 and not order2 then
			return false
		elseif not order1 and order2 then
			return true
		elseif order1 and order2 then
			return order1 < order2
		elseif not order1 and not order2 then
			return v1 < v2
		end
		
	end
	
	function Plater.ParseCVarValue(value)
		if value == nil then return nil end
		--bool checks
		if type(value) == "boolean" then
			value = value and 1 or 0 --store as 1/0
		elseif value == "true" then
			value = 1
		elseif value == "false" then
			value = 0
		end
		return tostring(value) --to store string representation
	end
	
	local canSaveCVars = false --only allow storing after plater has restored
	--on logout or on profile change, or when they are actually set, save some important cvars inside the profile
	function Plater.SaveConsoleVariables(cvar, value) --private
		if not canSaveCVars then return end
		
		--print("save cvars", cvar, value, debugstack())
		local cvarTable = Plater.db.profile.saved_cvars
		local cvarLastChangedTable = Plater.db.profile.saved_cvars_last_change
		
		if (not cvarTable) then
			--return
			Plater.db.profile.saved_cvars = {}
			cvarTable = Plater.db.profile.saved_cvars
		end
		
		if not cvar then -- store all
			for CVarName, enabled in pairs (cvars_to_store) do
				if enabled then
					cvarTable [CVarName] = Plater.ParseCVarValue(GetCVar (CVarName))
				end
			end
		else
			-- make this case insensitive, but ensure original case is stored
			cvar = cvars_to_store_lower[lower(cvar) or "N/A"] -- get right case for storage
			if cvars_to_store[cvar] then
				cvarTable [cvar] = Plater.ParseCVarValue(value)
				local callstack = debugstack(2) -- starts at "SetCVar" or caller
				if callstack then
					local caller, line = callstack:match("\"@([^\"]+)\"%]:(%d+)")
					if not caller then
						caller, line = callstack:match("in function <([^:%[>]+):(%d+)>")
					end
					
					--print((caller and caller .. ":" .. line) or callstack)
					local isCVarUtil = (caller and caller:lower():find("[\\/]sharedxml[\\/]cvarutil%.lua"))
					cvarLastChangedTable [cvar] = not isCVarUtil and (caller and (caller .. ":" .. line)) or callstack or "N/A"
				end
			end
		end
		
	end
	--restore profile cvars
	function Plater.RestoreProfileCVars()
		if (InCombatLockdown()) then
			C_Timer.After (1, function() Plater.RestoreProfileCVars() end)
			return
		end
		
		--> try to restore cvars from the profile
		local savedCVars = Plater.db and Plater.db.profile and Plater.db.profile.saved_cvars
		if (savedCVars) then
			--pre-sort restore order:
			local orderKeys = {}
			for k in pairs (cvars_to_store) do
				tinsert(orderKeys, k)
			end
			table.sort(orderKeys, cvar_restore_order)
			
			for _, CVarName in pairs (orderKeys) do
				local CVarValue = savedCVars [CVarName]
				if CVarValue then --only restore what we want to store/restore!
					SetCVar (CVarName, Plater.ParseCVarValue(CVarValue))
				end
			end
		end
		canSaveCVars = true --allow storing after restoring the first time
	end
	
	function Plater.DebugCVars(cvar)
		cvar = cvar and cvar:gsub(" ", "") or nil
		if cvar and cvar ~= "" then
			if cvars_to_store[cvar] then
				print("CVar info:\nName: '" .. cvar .. "'\nCurrent Value: " .. (Plater.ParseCVarValue(GetCVar (cvar)) or "<not set>") .. "\nStored Value: " .. (Plater.db.profile.saved_cvars[cvar] or "<not stored>") ..  "\nLast changed by: " .. (Plater.db.profile.saved_cvars_last_change[cvar] and ("\n" .. Plater.db.profile.saved_cvars_last_change[cvar]) or "<no info>"))
			else
				print("CVar '" .. cvar .. "' is not stored in Plater.")
			end
		else
			print("No CVar name provided. Printing all stored CVar names. Use '/plater cvar <cvar name> for more details.'")
			local savedCVars = Plater.db and Plater.db.profile and Plater.db.profile.saved_cvars or {}
			for CVarName, CVarValue in pairs (savedCVars) do
				print("'" .. CVarName .. "' = " .. (Plater.ParseCVarValue(CVarValue) or "<not set>"))
			end
		end
	end

	--refresh call back will run all functions in its table when Plater refreshes the dynamic upvales for the file
	Plater.DBRefreshCallback = {}
	function Plater.RegisterRefreshDBCallback (func) --private
		DF.table.addunique (Plater.DBRefreshCallback, func)
	end
	function Plater.FireRefreshDBCallback() --private
		for _, func in ipairs (Plater.DBRefreshCallback) do
			DF:Dispatch (func)
		end
	end

	function Plater.DisableAuraTrackingForAuraTest()
		DB_AURA_ENABLED = false
	end

	--> place most used data into local upvalues to save process time
	--> scripts need to call this function if they change something in the profile ~refresh ~db ~upvalues
	function Plater.RefreshDBUpvalues()
		local profile = Plater.db.profile

		DB_AURA_ENABLED = profile.aura_enabled
		DB_AURA_ALPHA = profile.aura_alpha
		DB_AURA_SEPARATE_BUFFS = profile.buffs_on_aura2

		DB_NUMBER_REGION_EAST_ASIA = Plater.db.profile.number_region == "eastasia"
		
		DB_TICK_THROTTLE = profile.update_throttle
		DB_LERP_COLOR = profile.use_color_lerp

		--class colors
		DB_CLASS_COLORS = profile.class_colors
		--update colorStr
		for className, colorTable in pairs(profile.class_colors) do
			colorTable.colorStr = DetailsFramework:FormatColor("hex", colorTable.r, colorTable.g, colorTable.b, 1)
		end

		DB_LERP_COLOR_SPEED = profile.color_lerp_speed
		DB_PLATE_CONFIG = profile.plate_config
		DB_TRACK_METHOD = profile.aura_tracker.track_method
		
		DB_DO_ANIMATIONS = profile.use_health_animation
		DB_ANIMATION_TIME_DILATATION = profile.health_animation_time_dilatation
		
		DB_HOVER_HIGHLIGHT = profile.hover_highlight
		DB_USE_RANGE_CHECK = profile.range_check_enabled
		DB_USE_NON_TARGETS_ALPHA = profile.non_targeted_alpha_enabled
		DB_USE_FOCUS_TARGET_ALPHA = profile.focus_as_target_alpha
		DB_USE_ALPHA_FRIENDLIES = profile.transparency_behavior_on_friendlies
		DB_USE_ALPHA_ENEMIES = profile.transparency_behavior_on_enemies
		DB_USE_QUICK_HIDE = profile.quick_hide
		DB_SHOW_HEALTHBARS_FOR_NOT_ATTACKABLE = profile.show_healthbars_on_not_attackable
		
		DB_NPCIDS_CACHE = Plater.db.profile.npc_cache
		
		DB_USE_UIPARENT = profile.use_ui_parent
		
		DB_BORDER_COLOR_R = profile.border_color [1]
		DB_BORDER_COLOR_G = profile.border_color [2]
		DB_BORDER_COLOR_B = profile.border_color [3]
		DB_BORDER_COLOR_A = profile.border_color [4]
		DB_BORDER_THICKNESS = profile.border_thickness
		DB_AGGRO_CHANGE_HEALTHBAR_COLOR = profile.aggro_modifies.health_bar_color
		DB_AGGRO_CHANGE_BORDER_COLOR = profile.aggro_modifies.border_color
		DB_AGGRO_CHANGE_NAME_COLOR = profile.aggro_modifies.actor_name_color
		DB_AGGRO_CAN_CHECK_NOTANKAGGRO = profile.aggro_can_check_notank
		
		DB_AGGRO_TANK_COLORS = profile.tank.colors
		DB_AGGRO_DPS_COLORS = profile.dps.colors
		
		DB_NOT_COMBAT_ALPHA_ENABLED = profile.not_affecting_combat_enabled
		
		DB_TARGET_SHADY_ENABLED = profile.target_shady_enabled
		DB_TARGET_SHADY_ALPHA = profile.target_shady_alpha
		DB_TARGET_SHADY_COMBATONLY = profile.target_shady_combat_only
		
		DB_NAME_NPCENEMY_ANCHOR = profile.plate_config.enemynpc.actorname_text_anchor.side
		DB_NAME_NPCFRIENDLY_ANCHOR = profile.plate_config.friendlynpc.actorname_text_anchor.side
		DB_NAME_PLAYERENEMY_ANCHOR = profile.plate_config.enemyplayer.actorname_text_anchor.side
		DB_NAME_PLAYERFRIENDLY_ANCHOR = profile.plate_config.friendlyplayer.actorname_text_anchor.side
		
		DB_TEXTURE_CASTBAR = LibSharedMedia:Fetch ("statusbar", profile.cast_statusbar_texture)
		DB_TEXTURE_CASTBAR_BG = LibSharedMedia:Fetch ("statusbar", profile.cast_statusbar_bgtexture)
		DB_TEXTURE_HEALTHBAR = LibSharedMedia:Fetch ("statusbar", profile.health_statusbar_texture)
		DB_TEXTURE_HEALTHBAR_BG = LibSharedMedia:Fetch ("statusbar", profile.health_statusbar_bgtexture)	
		
		DB_CASTBAR_HIDE_ENEMIES = profile.hide_enemy_castbars
		DB_CASTBAR_HIDE_FRIENDLY = profile.hide_friendly_castbars
		
		DB_CAPTURED_CASTS = PlaterDB.captured_casts
		DB_CAPTURED_SPELLS = PlaterDB.captured_spells
		
		DB_USE_NAME_TRANSLIT = profile.use_name_translit

		--refresh lists
		Plater.RefreshDBLists()

		--refresh auras
		Plater.RefreshAuraCache() --on Plater_Auras.lua
		Plater.RefreshAuraDBUpvalues() --on Plater_Auras.lua
		Plater.UpdateAuraCache() --on Plater_Auras.lua

		--refresh resources
		Plater.Resources.RefreshResourcesDBUpvalues() --Plater_Resources.lua
	end
	
	function Plater.RefreshDBLists()
		local profile = Plater.db.profile

		wipe (SPELL_WITH_ANIMATIONS)
		
		if (profile.spell_animations) then
			for spellId, animations in pairs (profile.spell_animation_list) do
				if type(spellId) == "string" and tonumber(spellId) then
					profile.spell_animation_list[tonumber(spellId)] = animations
					profile.spell_animation_list[spellId] = nil
				end
				local frameAnimations = {}
				local spellName = GetSpellInfo (spellId)
				if (spellName) then
					for animationIndex, animationOptions in ipairs (animations) do
						if (animationOptions.enabled) then
							local data = DF.table.deploy ({}, animationOptions)
							data.animationCooldown = {} --store nameplate references with [nameplateRef] = GetTime() + cooldown
							tinsert (frameAnimations, data)
						end
					end
					
					SPELL_WITH_ANIMATIONS [spellName] = frameAnimations
				end
				
			end
		end

		IS_USING_DETAILS_INTEGRATION = false
		
		--details integration
		if (Details and Details.plater) then
			local detailsPlaterConfig = Details.plater
			if (detailsPlaterConfig.realtime_dps_enabled) then
				IS_USING_DETAILS_INTEGRATION = true
			elseif (detailsPlaterConfig.realtime_dps_player_enabled) then
				IS_USING_DETAILS_INTEGRATION = true
			elseif (detailsPlaterConfig.damage_taken_enabled) then
				IS_USING_DETAILS_INTEGRATION = true
			end
		end	

		--> build the list of npcs with special colors
		wipe (DB_UNITCOLOR_CACHE) --regular color overrides the threat color
		wipe (DB_UNITCOLOR_SCRIPT_CACHE) --color only used for scripts, plater does not use them 
		
		for npcID, infoTable in pairs (Plater.db.profile.npc_colors) do
			local enabled1 = infoTable [1] --this is the overall enabled
			local enabled2 = infoTable [2] --if this is true, this color is only used for scripts
			local colorID = infoTable [3] --the color
			
			if (enabled1 and not enabled2) then
				local r, g, b, a = DF:ParseColors (colorID)
				DB_UNITCOLOR_CACHE [npcID] = {r, g, b, a}
				
			elseif (enabled1 and enabled2) then
				local r, g, b, a = DF:ParseColors (colorID)
				DB_UNITCOLOR_SCRIPT_CACHE [npcID] = {r, g, b, a}
				
			end
		end
		
		Plater.IncreaseRefreshID()
		Plater.FireRefreshDBCallback()
	end

	--a patch is a function stored in the Plater_ScriptLibrary file and are executed only once to change a profile setting, remove or add an aura into the tracker or modify a script
	--patch versions are stored within the profile, so importing or creating a new profile will apply all patches that wasn't applyed into it yet
	function Plater.ApplyPatches() --private ~updates ~scriptupdates ~patch ~patches
		if (PlaterPatchLibrary) then
			local currentPatch = Plater.db.profile.patch_version
			local bSkipNonEssentialPatches = PlaterDB.SkipNonEssentialPatches
			for patchId = currentPatch+1, #PlaterPatchLibrary do
				local bCanInstallPatch = true

				if (bSkipNonEssentialPatches) then
					if (PlaterPatchLibrary[patchId].NotEssential) then
						print(LOC["OPTIONS_NOESSENTIAL_SKIP_ALERT"], PlaterPatchLibrary[patchId].Notes[1]) --"Skipped non-essential patch:"
						bCanInstallPatch = false
					end
				end

				if (bCanInstallPatch) then
					local patch = PlaterPatchLibrary [patchId]
					Plater:Msg ("Applied Patch #" .. patchId .. ":")
					
					for o = 1, #patch.Notes do
						print (patch.Notes [o])
					end
					
					DF:Dispatch (patch.Func)
				end
				
				Plater.db.profile.patch_version = patchId
			end
			
			--do not clear patch library, when creating a new profile it'll need to re-apply patches
			--PlaterPatchLibrary = nil
		end
	end
	
	function Plater.SetNameplateScale(unitFrame, scale)
		scale = tonumber(scale)
		unitFrame.nameplateScaleAdjust = scale and (scale > 0) and scale or 1
		if (DB_USE_UIPARENT) then
			Plater.UpdateUIParentScale (unitFrame.PlateFrame)
		else
			unitFrame:SetScale (unitFrame.nameplateScaleAdjust)
			Plater.UpdatePlateSize(unitFrame.PlateFrame)
		end
	end

	---when using UIParent as the parent for the unitFrame, this function is hooked in the plateFrame OnSizeChanged script
	---the goal is to adjust the the unitFrame scale when the plateFrame scale changes
	---this approach also solves the issue to the unitFrame not playing correctly the animation when the nameplate is removed from the screen
	---self is plateFrame, w, h aren't reliable
	---@param self plateframe
	---@param w any
	---@param h any
	function Plater.UpdateUIParentScale (self, w, h) --private
		local unitFrame = self.unitFrame
		if (unitFrame) then
			local defaultScale = self:GetEffectiveScale()
			--local defaultScale = UIParent:GetEffectiveScale()
			
			if (defaultScale < 0.4) then
				--assuming the nameplate is in process of being removed from the screen if the scale if lower than .4
				unitFrame:SetScale (defaultScale)
			else
				--scale (adding a fine tune knob)
				local scaleFineTune = max (Plater.db.profile.ui_parent_scale_tune, 0.3)
				
				--@Ariani - March, 9
				unitFrame:SetScale (defaultScale * scaleFineTune * (tonumber(unitFrame.nameplateScaleAdjust) or 1))

			end
		end
	end
	
	--this reset the UIParent levels to user default set on the UIParent tab
	--there's an api that calls this function called Plater.RefreshNameplateStrata()
	function Plater.UpdateUIParentLevels (unitFrame) --private
		--setup frame strata and levels
		local profile = Plater.db.profile
		local healthBar = unitFrame.healthBar
		local castBar = unitFrame.castBar
		local buffFrame1 = unitFrame.BuffFrame
		local buffFrame2 = unitFrame.BuffFrame2
		local buffSpecial = unitFrame.ExtraIconFrame

		if (DB_USE_UIPARENT) then
			--strata
			unitFrame:SetFrameStrata (profile.ui_parent_base_strata)
			healthBar:SetFrameStrata (profile.ui_parent_base_strata)
			castBar:SetFrameStrata (profile.ui_parent_cast_strata)
			buffFrame1:SetFrameStrata (profile.ui_parent_buff_strata)
			buffFrame2:SetFrameStrata (profile.ui_parent_buff2_strata)
			buffSpecial:SetFrameStrata (profile.ui_parent_buff_special_strata)
		end

		--level
		local baseLevel = unitFrame.baseFrameLevel or unitFrame:GetFrameLevel()
		healthBar:SetFrameLevel ((baseLevel > 0) and baseLevel or 0)
			
		local tmplevel = baseLevel + profile.ui_parent_cast_level + 3
		castBar:SetFrameLevel ((tmplevel > 0) and tmplevel or 0)
		
		tmplevel = baseLevel + profile.ui_parent_buff_level + 3
		buffFrame1:SetFrameLevel ((tmplevel > 0) and tmplevel or 0)
		
		tmplevel = baseLevel + profile.ui_parent_buff2_level + 10
		buffFrame2:SetFrameLevel ((tmplevel > 0) and tmplevel or 0)
		
		tmplevel = baseLevel + profile.ui_parent_buff_special_level + 10
		buffSpecial:SetFrameLevel ((tmplevel > 0) and tmplevel or 0)
		
		--raid-target marker adjust:
		unitFrame.PlaterRaidTargetFrame:SetFrameStrata(unitFrame.healthBar:GetFrameStrata())
		unitFrame.PlaterRaidTargetFrame:SetFrameLevel(unitFrame.healthBar:GetFrameLevel() + 25)
	end	
	
	--move the target nameplate to its strata
	--also need to move other frame components of this nameplate as well so the entire nameplate is up front
	function Plater.UpdateUIParentTargetLevels (unitFrame) --private
	--setup frame strata and levels
		local profile = Plater.db.profile
		local healthBar = unitFrame.healthBar
		local castBar = unitFrame.castBar
		local buffFrame1 = unitFrame.BuffFrame
		local buffFrame2 = unitFrame.BuffFrame2
		local buffSpecial = unitFrame.ExtraIconFrame
		if (DB_USE_UIPARENT) then
			--move all frames to target strata
			local targetStrata = Plater.db.profile.ui_parent_target_strata
			unitFrame:SetFrameStrata (targetStrata)
			healthBar:SetFrameStrata (targetStrata)
			castBar:SetFrameStrata (targetStrata)
			buffFrame1:SetFrameStrata (targetStrata)
			buffFrame2:SetFrameStrata (targetStrata)
			buffSpecial:SetFrameStrata (targetStrata)
		end

		--level
		local baseLevel = unitFrame.baseFrameLevel or unitFrame:GetFrameLevel()
		baseLevel = max(baseLevel + 5000, 5000)
		healthBar:SetFrameLevel ((baseLevel > 0) and baseLevel or 0)
			
		local tmplevel = min(baseLevel + profile.ui_parent_cast_level + 3, 10000)
		castBar:SetFrameLevel ((tmplevel > 0) and tmplevel or 0)
		
		tmplevel = min(baseLevel + profile.ui_parent_buff_level + 3, 10000)
		buffFrame1:SetFrameLevel ((tmplevel > 0) and tmplevel or 0)
		
		tmplevel = min(baseLevel + profile.ui_parent_buff2_level + 10, 10000)
		buffFrame2:SetFrameLevel ((tmplevel > 0) and tmplevel or 0)
		
		tmplevel = min(baseLevel + profile.ui_parent_buff_special_level + 10, 10000)
		buffSpecial:SetFrameLevel ((tmplevel > 0) and tmplevel or 0)
		
		--raid-target marker adjust:
		unitFrame.PlaterRaidTargetFrame:SetFrameStrata(unitFrame.healthBar:GetFrameStrata())
		unitFrame.PlaterRaidTargetFrame:SetFrameLevel(unitFrame.healthBar:GetFrameLevel() + 25)
	end
	
	--> regional format numbers
	do
		local eastAsiaMyriads_1k, eastAsiaMyriads_10k, eastAsiaMyriads_1B
		if (GetLocale() == "koKR") then
			eastAsiaMyriads_1k, eastAsiaMyriads_10k, eastAsiaMyriads_1B = "천", "만", "억"
			
		elseif (GetLocale() == "zhCN") then
			eastAsiaMyriads_1k, eastAsiaMyriads_10k, eastAsiaMyriads_1B = "千", "万", "亿"
			
		elseif (GetLocale() == "zhTW") then
			eastAsiaMyriads_1k, eastAsiaMyriads_10k, eastAsiaMyriads_1B = "千", "萬", "億"
			
		else
			eastAsiaMyriads_1k, eastAsiaMyriads_10k, eastAsiaMyriads_1B = "천", "만", "억"
		end

		function Plater.FormatNumber (number)
			if (DB_NUMBER_REGION_EAST_ASIA) then
				if (number > 99999999) then
					return format ("%.2f", number/100000000) .. eastAsiaMyriads_1B
					
				elseif (number > 999999) then
					return format ("%.2f", number/10000) .. eastAsiaMyriads_10k
					
				elseif (number > 99999) then
					return floor (number/10000) .. eastAsiaMyriads_10k
					
				elseif (number > 9999) then
					return format ("%.1f", (number/10000)) .. eastAsiaMyriads_10k
					
				elseif (number > 999) then
					return format ("%.1f", (number/1000)) .. eastAsiaMyriads_1k
					
				end
				
				return format ("%.1f", number)
			else
				if (number > 999999999) then
					return format ("%.2fB", number/1000000000)
					
				elseif (number > 999999) then
					return format ("%.2fM", number/1000000)
					
				elseif (number > 99999) then
					return floor (number/1000) .. "K"
					
				elseif (number > 999) then
					return format ("%.1fK", (number/1000))
					
				end
				
				return floor (number)			
			end
		end

	end
	

	
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> event handler

	--frame which will receive events
	Plater.EventHandlerFrame = CreateFrame ("frame") --private

	--schedule zone change
	local run_zonechanged_hook = function()
		if (HOOK_ZONE_CHANGED.ScriptAmount > 0) then
			--for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
			local globalScope = _G
			for i = 1, 40 do
				--run on all nameplates already created
				---@type plateframe
				local plateFrame = globalScope ["NamePlate" .. i]
				if (plateFrame) then
					for i = 1, HOOK_ZONE_CHANGED.ScriptAmount do
						local globalScriptObject = HOOK_ZONE_CHANGED [i]
						local unitFrame = plateFrame.unitFrame
						local scriptContainer = unitFrame:ScriptGetContainer()
						local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Zone Changed")
						--run
						unitFrame:ScriptRunHook (scriptInfo, "Zone Changed")
					end
				else
					break
				end
			end
		end	
	end

	function Plater.ScheduleZoneChangeHook()
		if (Plater.ScheduledZoneChangeTriggerHook) then
			Plater.ScheduledZoneChangeTriggerHook:Cancel()
		end
		Plater.ScheduledZoneChangeTriggerHook = C_Timer.NewTimer (2, run_zonechanged_hook)
	end
	
	function Plater.RunLoadScreenHook()
		for i = 1, HOOK_LOAD_SCREEN.ScriptAmount do
			local hookInfo = HOOK_LOAD_SCREEN [i]
			Plater.ScriptMetaFunctions.ScriptRunNoAttach (hookInfo, "Load Screen")
		end
	end

	function platerInternal.OnOptionChanged()
		for i = 1, HOOK_OPTION_CHANGED.ScriptAmount do
			local hookInfo = HOOK_OPTION_CHANGED[i]
			Plater.ScriptMetaFunctions.ScriptRunNoAttach(hookInfo, "Option Changed")
		end
	end
	
	--store all functions for all events that will be registered inside OnInit
	local last_GetShapeshiftFormID = GetShapeshiftFormID()
	local eventFunctions = {

		--when a unit from unatackable change its state, this event triggers several times, a schedule is used to only update once
		UNIT_FLAGS = function (_, unit)
			--if (unit == "player") then
			--	return
			--end
			
			if not string.match(unit, "nameplate%d%d?$") then return end
			
			---@type plateframe
			local plateFrame = C_NamePlate.GetNamePlateForUnit (unit, issecure())
			if (plateFrame) then
				--rules if can schedule an update for unit flag event:
				
				--has the hostility changed?
				local reactionChanged = false
				local curReaction = plateFrame [MEMBER_REACTION]
				local newReaction = UnitReaction(unit, "player")
				if not curReaction then -- in case the plater nameplate is not on screen, ensure that it can change
					reactionChanged = true
				elseif curReaction ~= newReaction then
					if curReaction == Plater.UnitReaction.UNITREACTION_NEUTRAL and newReaction ~= curReaction then
						reactionChanged = true
					elseif curReaction < Plater.UnitReaction.UNITREACTION_NEUTRAL and newReaction >= Plater.UnitReaction.UNITREACTION_NEUTRAL then
						reactionChanged = true
					elseif curReaction > Plater.UnitReaction.UNITREACTION_NEUTRAL and newReaction <= Plater.UnitReaction.UNITREACTION_NEUTRAL then
						reactionChanged = true
					end
				end

				--can the user attack or no longer attack?
				local attackableChanged = plateFrame.PlayerCannotAttack ~= not UnitCanAttack ("player", unit)
				if (reactionChanged or attackableChanged or not plateFrame.unitFrame.PlaterOnScreen) then
					--print ("UNIT_FLAG", plateFrame, issecure(), unit, unit and UnitName (unit))
					--Plater.ScheduleUpdateForNameplate (plateFrame, unit)
					
					Plater.RunScheduledUpdate({unitId = unit}) -- do this now
				end
			end
		end,
		
		UNIT_FACTION = function (_, unit)
			--if (unit == "player") then
			--	return
			--end
			
			--fires when somebody changes faction near the player
			---@type plateframe
			local plateFrame = C_NamePlate.GetNamePlateForUnit (unit, issecure())
			if (plateFrame) then
				Plater.ScheduleUpdateForNameplate (plateFrame)
			end
		end,

		ACTIVE_TALENT_GROUP_CHANGED = function()
			C_Timer.After (0.5, UpdatePlayerTankState)
			C_Timer.After (0.5, Plater.Resources.OnSpecChanged) --~resource
			C_Timer.After (2, Plater.GetSpellForRangeCheck)
			C_Timer.After (2, Plater.GetHealthCutoffValue)
			C_Timer.After (1, Plater.DispatchTalentUpdateHookEvent)
		end,
		
		PLAYER_SPECIALIZATION_CHANGED = function()
			C_Timer.After (0.5, Plater.Resources.OnSpecChanged) --~resource
			C_Timer.After (2, Plater.GetSpellForRangeCheck)
			C_Timer.After (2, Plater.GetHealthCutoffValue)
			C_Timer.After (1, Plater.DispatchTalentUpdateHookEvent)
		end,
		
		TRAIT_CONFIG_UPDATED = function()
			C_Timer.After (2, Plater.GetSpellForRangeCheck)
			C_Timer.After (2, Plater.GetHealthCutoffValue)
			C_Timer.After (1, Plater.DispatchTalentUpdateHookEvent)
		end,

		PLAYER_TALENT_UPDATE = function()
			C_Timer.After (2, Plater.GetSpellForRangeCheck)
			C_Timer.After (2, Plater.GetHealthCutoffValue)
			C_Timer.After (1, Plater.DispatchTalentUpdateHookEvent)
		end,
		
		GROUP_ROSTER_UPDATE = function()
			Plater.RefreshTankCache()
		end,
		
		UNIT_PET = function(_, unit)
			for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
				---@cast plateFrame plateframe
				if plateFrame.unitFrame and plateFrame.unitFrame.PlaterOnScreen then
					if not plateFrame.unitFrame.isPerformanceUnit then
						Plater.AddToAuraUpdate(plateFrame.unitFrame.unit) -- force aura update
					end
					Plater.ScheduleUpdateForNameplate (plateFrame)
				end
			end
		end,

		PLAYER_REGEN_DISABLED = function()
			PLAYER_IN_COMBAT = true

			Plater.RefreshAutoToggle(PLAYER_IN_COMBAT)

			Plater.RefreshTankCache()
			
			--Plater.UpdateAuraCache()
			Plater.UpdateAllPlates(false, false, true)
			
			--check if can run combat enter hook and schedule it true
			if (HOOK_COMBAT_ENTER.ScriptAmount > 0) then
				local hookTimer = C_Timer.NewTimer (0.1, Plater.ScheduleHookForCombat)
				hookTimer.Event = "Enter Combat"
			end
			
			Plater.CombatTime = GetTime()

			--store names and casts from 'last' combat, this is used when showing Npcs Colors and Cast Colors to bump up stuff from the last combat
			table.wipe(Plater.LastCombat.npcNames)
			table.wipe(Plater.LastCombat.spellNames)

			--store player and pet guids for friendly affiliation
			local unitCachePlayers
			local unitCachePets

			if (IsInRaid()) then
				unitCachePlayers = platerInternal.UnitIdCache.Raid --raid1, raid2, raid3
				unitCachePets = platerInternal.UnitIdCache.RaidPet --raidpet1, raidpet2, raidpet3
			else
				unitCachePlayers = platerInternal.UnitIdCache.Party --player, party1, party2
				unitCachePets = platerInternal.UnitIdCache.PartyPet --partypet1, partypet2
			end

			table.wipe(platerInternal.HasFriendlyAffiliation)

			for i = 1, #unitCachePlayers do
				local unitGuid = UnitGUID(unitCachePlayers[i])
				if (unitGuid) then
					platerInternal.HasFriendlyAffiliation[unitGuid] = true
				else
					break
				end
			end

			for i = 1, #unitCachePets do
				local unitGuid = UnitGUID(unitCachePets[i])
				if (unitGuid) then
					platerInternal.HasFriendlyAffiliation[unitGuid] = true
				end
			end
		end,

		PLAYER_REGEN_ENABLED = function()

			PLAYER_IN_COMBAT = false
			
			Plater.RefreshAutoToggle(PLAYER_IN_COMBAT, true)
			
			for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
				---@cast plateFrame plateframe
				plateFrame [MEMBER_NOCOMBAT] = nil
			end
			
			--check if can run combat enter hook and schedule it true
			if (HOOK_COMBAT_LEAVE.ScriptAmount > 0) then
				local hookTimer = C_Timer.NewTimer (0.1, Plater.ScheduleHookForCombat)
				hookTimer.Event = "Leave Combat"
			end
			
			Plater.RefreshTankCache()
			
			Plater.UpdateAllNameplateColors()
			Plater.UpdateAllPlates()
			
			if (platerInternal.OpenOptionspanelAfterCombat) then
				local OpenOptionspanelAfterCombat = platerInternal.OpenOptionspanelAfterCombat
				platerInternal.OpenOptionspanelAfterCombat = nil
				C_Timer.NewTimer (1.5, function() Plater.OpenOptionsPanel(unpack(OpenOptionspanelAfterCombat)) end )
			end
		end,

		FRIENDLIST_UPDATE = function()
			wipe (Plater.FriendsCache)
			for i = 1, C_FriendList.GetNumFriends() do
				local info = C_FriendList.GetFriendInfoByIndex (i)
				if (info and info.connected and info.name) then
					Plater.FriendsCache [info.name] = true
					Plater.FriendsCache [DF:RemoveRealmName (info.name)] = true
				end
			end
			
			if IS_WOW_PROJECT_MAINLINE then
				local _, numBNetOnline = BNGetNumFriends();
				for i = 1, numBNetOnline do
					local accountInfo = C_BattleNet.GetFriendAccountInfo(i);
					if (accountInfo and accountInfo.gameAccountInfo and accountInfo.gameAccountInfo.isOnline and accountInfo.gameAccountInfo.characterName) then
						Plater.FriendsCache [accountInfo.gameAccountInfo.characterName] = true
					end
				end
			else
				for i = 1, BNGetNumFriends() do 
					local presenceID, presenceName, battleTag, isBattleTagPresence, toonName, toonID, client, isOnline, lastOnline, isAFK, isDND, messageText, noteText, isRIDFriend, broadcastTime, canSoR = BNGetFriendInfo (i)
					if (isOnline and toonName) then
						Plater.FriendsCache [toonName] = true
					end
				end
			end
			
			--let's not trigger a full update on all plates because a friend is now online
			--Plater.UpdateAllPlates()
		end,

		RAID_TARGET_UPDATE = function()
			Plater.UpdateRaidMarkersOnAllNameplates()
		end,

		QUEST_REMOVED = function()
			Plater.QuestLogUpdated()
		end,
		QUEST_ACCEPTED = function()
			Plater.QuestLogUpdated()
		end,
		QUEST_ACCEPT_CONFIRM = function()
			Plater.QuestLogUpdated()
		end,
		QUEST_COMPLETE = function()
			Plater.QuestLogUpdated()
		end,
		QUEST_POI_UPDATE = function()
			Plater.QuestLogUpdated()
		end,
		QUEST_QUERY_COMPLETE = function()
			Plater.QuestLogUpdated()
		end,
		QUEST_DETAIL = function()
			Plater.QuestLogUpdated()
		end,
		QUEST_FINISHED = function()
			Plater.QuestLogUpdated()
		end,
		QUEST_GREETING = function()
			Plater.QuestLogUpdated()
		end,
		QUEST_LOG_UPDATE = function()
			Plater.QuestLogUpdated()
		end,
		UNIT_QUEST_LOG_CHANGED = function()
			Plater.QuestLogUpdated()
		end,

		PLAYER_FOCUS_CHANGED = function()
			Plater.OnPlayerTargetChanged()
		end,
		PLAYER_TARGET_CHANGED = function()
			Plater.OnPlayerTargetChanged()
		end,

		PLAYER_UPDATE_RESTING = function()
			Plater.RefreshAutoToggle()
		end,

		--update the unit name, triggered when the client receives the rest of the information about an unit
		UNIT_NAME_UPDATE = function (_, unitID)
			if (unitID) then
				---@type plateframe
				local plateFrame = C_NamePlate.GetNamePlateForUnit (unitID)
				if (plateFrame and plateFrame.unitFrame.PlaterOnScreen) then
					local unitFrame = plateFrame.unitFrame
					local unitName = UnitName (unitID)
					local unitNameTranslit = unitName
					if DB_USE_NAME_TRANSLIT then
						unitNameTranslit = LibTranslit:Transliterate(unitName, TRANSLIT_MARK)
					end
					plateFrame [MEMBER_NAME] = unitNameTranslit
					plateFrame [MEMBER_NAMELOWER] = lower (plateFrame [MEMBER_NAME])
					plateFrame.unitNameInternal = unitName
					unitFrame [MEMBER_NAME] = plateFrame [MEMBER_NAME]
					unitFrame [MEMBER_NAMELOWER] = plateFrame [MEMBER_NAMELOWER]
					unitFrame.unitNameInternal = unitName
					
					if (plateFrame.IsSelf) then
						--name isn't shown in the personal bar
						unitFrame.healthBar.unitName:SetText ("")
						return
					end
					
					--schedule an name update on this nameplate
					unitFrame.ScheduleNameUpdate = true
				end
			end
		end,

		ENCOUNTER_END = function()
			Plater.CurrentEncounterID = nil
			Plater.CurrentEncounterName = nil
			Plater.CurrentEncounterDifficultyId = nil
			Plater.LatestEncounter = time()
		end,

		ENCOUNTER_START = function (_, encounterID, encounterName, difficultyID)
			Plater.CurrentEncounterID = encounterID
			Plater.CurrentEncounterName = encounterName
			Plater.CurrentEncounterDifficultyId = difficultyID
			
			local _, zoneType = GetInstanceInfo()
			if (zoneType == "raid") then
				table.wipe (DB_CAPTURED_SPELLS)
			end
		end,

		CHALLENGE_MODE_START = function()
			table.wipe (DB_CAPTURED_SPELLS)
		end,

		ZONE_CHANGED_NEW_AREA = function()
			if (InCombatLockdown()) then
				C_Timer.After (1, function() Plater.RunFunctionForEvent ("ZONE_CHANGED_NEW_AREA") end)
				return
			end
			
			Plater.CurrentEncounterID = nil
			
			local pvpType, isFFA, faction = (GetZonePVPInfo or C_PvP.GetZonePVPInfo)()
			Plater.ZonePvpType = pvpType
			Plater.UpdateBgPlayerRoleCache()
			
			local name, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo()
			
			--reset when entering in a battleground
			if (instanceType == "pvp") then
				table.wipe (DB_CAPTURED_SPELLS)
			end
			Plater.ZoneInstanceType = instanceType
			Plater.ZoneName = name
			
			IS_IN_OPEN_WORLD = Plater.ZoneInstanceType == "none"
			IS_IN_INSTANCE = Plater.ZoneInstanceType == "raid" or Plater.ZoneInstanceType == "party"
			
			Plater.UpdateAllPlates()
			Plater.RefreshAutoToggle()
			
			--hooks
			Plater.ScheduleZoneChangeHook()
			
			if (Plater.PlayerEnteringWorld) then
				Plater.PlayerEnteringWorld = false
				C_Timer.After (1, Plater.RunLoadScreenHook)
			end
		end,

		ZONE_CHANGED_INDOORS = function()
			Plater.RunFunctionForEvent ("ZONE_CHANGED_NEW_AREA")
		end,

		ZONE_CHANGED = function()
			Plater.RunFunctionForEvent ("ZONE_CHANGED_NEW_AREA")
		end,
		
		PLAYER_ENTERING_WORLD = function(_, isInitialLogin, isReloadingUi)

			Plater.db.profile.login_counter = Plater.db.profile.login_counter + 1

			Plater.ScheduleRunFunctionForEvent (1, "ZONE_CHANGED_NEW_AREA")
			Plater.ScheduleRunFunctionForEvent (1, "FRIENDLIST_UPDATE")

			Plater.PlayerGuildName = GetGuildInfo ("player")
			if (not Plater.PlayerGuildName or Plater.PlayerGuildName == "") then
				Plater.PlayerGuildName = "ThePlayerHasNoGuildName/30Char"
				
				--somethimes guild information isn't available at the login
				C_Timer.After (10, delayed_guildname_check)
			end
			
			local pvpType, isFFA, faction = (GetZonePVPInfo or C_PvP.GetZonePVPInfo)()
			Plater.ZonePvpType = pvpType
			
			local name, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo()
			Plater.ZoneInstanceType = instanceType
			
			--> ensure resource on target consistency after login:
			local resourcesOnTarget = GetCVar ("nameplateResourceOnTarget") == CVAR_ENABLED
			if resourcesOnTarget then
				PlaterDBChr.resources_on_target = true
				if (not InCombatLockdown()) then
					SetCVar ("nameplateResourceOnTarget", CVAR_DISABLED) -- reset this to false always, as it conflicts
				end
			end
			
			-- this seems to be gone as of 18.12.2020
			--if Plater.db.profile.plate_config.friendlynpc.quest_enabled and not InCombatLockdown() then
				--SetCVar("showQuestTrackingTooltips", 1) -- ensure it is turned on...
			--end

			--create the frame to hold the plater resoruce bar
			Plater.Resources.CreateMainResourceFrame() --~resource
			
			--run hooks on load screen
			if (HOOK_LOAD_SCREEN.ScriptAmount > 0) then
				Plater.PlayerEnteringWorld = true
			end
		end,

		PLAYER_LOGOUT = function()
			--Plater.SaveConsoleVariables()
		end,
		
		VARIABLES_LOADED = function()
			
			C_Timer.After (0.1, Plater.ForceCVars)
			
			C_Timer.After (0.2, Plater.RestoreProfileCVars)

			C_Timer.After (0.3, Plater.UpdatePlateClickSpace)
			
			C_Timer.After (0.4, function() 
				Plater.RefreshAutoToggle(InCombatLockdown()) -- refresh this
				Plater.UpdateBaseNameplateOptions()
			end)
			
			-- hook CVar saving
			hooksecurefunc('SetCVar', Plater.SaveConsoleVariables)
			if C_CVar and C_CVar.SetCVar then
				hooksecurefunc(C_CVar, 'SetCVar', Plater.SaveConsoleVariables)
			end
			hooksecurefunc('ConsoleExec', function(console)
				local par1, par2, par3 = console:match('^(%S+)%s+(%S+)%s*(%S*)')
				if par1 then
					if par1:lower() == 'set' then -- /console SET cvar value
						Plater.SaveConsoleVariables(par2, par3)
					else -- /console cvar value
						Plater.SaveConsoleVariables(par1, par2)
					end
				end
			end)
			
		end,
		
		--many times at saved variables load the spell database isn't loaded yet
		PLAYER_LOGIN = function()			
			
			--C_Timer.After (0.1, Plater.GetSpellForRangeCheck)
			
			-- ensure OmniCC settings are up to date
			C_Timer.After (1, Plater.RefreshOmniCCGroup)
			
			--wait more time for the talents information be received from the server
			C_Timer.After (4, Plater.GetHealthCutoffValue)
			
			C_Timer.After (2, Plater.ScheduleZoneChangeHook)
			
			C_Timer.After (5, function()
				local petGUID = UnitGUID ("playerpet")
				if (petGUID) then
					local entry = {ownerGUID = Plater.PlayerGUID, ownerName = UnitName("player"), petName = UnitName("playerpet"), time = time()}
					Plater.PlayerPetCache [petGUID] = entry
				end
			end)
			
			--if the user just used a /reload to enable ui parenting, auto adjust the fine tune scale
			--the uiparent fine tune scale initially: after testing and playing around with it, I think it should be 1 / UIParent:GetEffectiveScale() and scaling should be done by multiplying defaultScale * scaleFineTune
			if (Plater.db.profile.use_ui_parent_just_enabled) then
				Plater.db.profile.use_ui_parent_just_enabled = false
				if (Plater.db.profile.ui_parent_scale_tune == 0) then
					--@Ariani - march 9
					Plater.db.profile.ui_parent_scale_tune = 1 / UIParent:GetEffectiveScale()
					
					--@Tercio:
					--if (UIParent:GetEffectiveScale() < 1) then
					--	Plater.db.profile.ui_parent_scale_tune = 1 - UIParent:GetEffectiveScale()
					--end
				end
			end
			
			if (not Plater.db.profile.number_region_first_run) then
				if (GetLocale() == "koKR") then
					Plater.db.profile.number_region = "eastasia"
				elseif (GetLocale() == "zhCN") then
					Plater.db.profile.number_region = "eastasia"
				elseif (GetLocale() == "zhTW") then
					Plater.db.profile.number_region = "eastasia"
				else
					Plater.db.profile.number_region = "western"
				end
				
				Plater.db.profile.number_region_first_run = true
			end
			
			if (Plater.db.profile.reopoen_options_panel_on_tab) then
				C_Timer.After (2, function()
					Plater.OpenOptionsPanel()
					PlaterOptionsPanelContainer:SelectTabByIndex (Plater.db.profile.reopoen_options_panel_on_tab)
					Plater.db.profile.reopoen_options_panel_on_tab = false
				end)
			end
			
			--run hooks on player logon
			if (HOOK_PLAYER_LOGON.ScriptAmount > 0) then
				C_Timer.After (1, function()
					for i = 1, HOOK_PLAYER_LOGON.ScriptAmount do
						local hookInfo = HOOK_PLAYER_LOGON [i]
						Plater.ScriptMetaFunctions.ScriptRunNoAttach (hookInfo, "Player Logon")
					end
				end)
			end
			
			--check addons incompatibility
			--> Plater has issues with ElvUI due to be using the same namespace for unitFrame and healthBar
			C_Timer.After (15, function()
				if ((IsAddOnLoaded or C_AddOns.IsAddOnLoaded) ("ElvUI")) then
					if (ElvUI[1] and ElvUI[1].private and ElvUI[1].private.nameplates and ElvUI[1].private.nameplates.enable) then
						Plater:Msg ("'ElvUI Nameplates' and 'Plater Nameplates' are enabled and both nameplates won't work together.")
						Plater:Msg ("You may disable ElvUI Nameplates at /elvui > Nameplates section or you may disable Plater at the addon control panel.")
					end
				end 
			end)

			-- ensure resources are up to date
			C_Timer.After (3, Plater.Resources.OnSpecChanged)
			
			-- translate NPC_CACHE entries if needed
			C_Timer.After (10, Plater.TranslateNPCCache)

		end,
		
		DISPLAY_SIZE_CHANGED = function()
			for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
				---@cast plateFrame plateframe
				if plateFrame.unitFrame.PlaterOnScreen then
					Plater.OnRetailNamePlateShow(plateFrame.UnitFrame)
				end
			end
			Plater.UpdateAllPlates (true)
		end,
		
		UI_SCALE_CHANGED = function()
			for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
				---@cast plateFrame plateframe
				Plater.OnRetailNamePlateShow(plateFrame.UnitFrame)
			end
			Plater.UpdateAllPlates (true)
		end,
		
		PLAYER_SOFT_INTERACT_CHANGED = function(_, arg1, arg2)
			for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
				---@cast plateFrame plateframe
				if plateFrame.unitFrame.PlaterOnScreen then
					if plateFrame [MEMBER_GUID] == arg1 or plateFrame [MEMBER_GUID] == arg2 then
						Plater.UpdateSoftInteractTarget(plateFrame, true)
					end
				end
			end
		end,
		
		PLAYER_SOFT_FRIEND_CHANGED = function(_, arg1, arg2)
			for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
				---@cast plateFrame plateframe
				if plateFrame.unitFrame.PlaterOnScreen then
					if plateFrame [MEMBER_GUID] == arg1 or plateFrame [MEMBER_GUID] == arg2 then
						Plater.UpdateSoftInteractTarget(plateFrame, true)
					end
				end
			end
		end,
		
		PLAYER_SOFT_ENEMY_CHANGED = function(_, arg1, arg2)
			for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
				---@cast plateFrame plateframe
				if plateFrame.unitFrame.PlaterOnScreen then
					if plateFrame [MEMBER_GUID] == arg1 or plateFrame [MEMBER_GUID] == arg2 then
						Plater.UpdateSoftInteractTarget(plateFrame, true)
					end
				end
			end
		end,
		
		--~created ~events ~oncreated 
		---@param event string
		---@param plateFrame plateframe
		NAME_PLATE_CREATED = function (event, plateFrame)
			--ViragDevTool_AddData({ctime = GetTime(), unit = plateFrame [MEMBER_UNITID] or "nil", stack = debugstack()}, "NAME_PLATE_CREATED - " .. (plateFrame [MEMBER_UNITID] or "nil"))
			--> create the unitframe
				local unitFrameOptions = {
					ShowPowerBar = false, 
					ShowBorder = false, 
					CanModifyHealhBarColor = false,
					ShowTargetOverlay = false,
					ShowUnitName = false, --let Plater control the unit name
					ClearUnitOnHide = false, --let Plater control when the cleanup is execute on the unit frame
				}
				
				local healthBarOptions = {
					ShowHealingPrediction = false,
					ShowShields = false,
				}
				
				local castBarOptions = {
					FadeInTime = 0.02,
					FadeOutTime = 0.66,
					SparkHeight = 20,
					LazyUpdateCooldown = 0.1,
					FillOnInterrupt = false,
					HideSparkOnInterrupt = false,
				}
				
				local powerBarOptions = {
					ShowAlternatePower = false,
				}
				
				--community patch by Ariani#0960 (discord)
				--make the unitFrame be parented to UIParent allowing frames to be moved between strata levels
				--March 3rd, 2019
				local newUnitFrame
				if (DB_USE_UIPARENT) then
					--TODO: why is it not showing properly when V / V hide / show but when leaving / entering scren??? -> plateFrame for now...
					--when using UIParent as the unit frame parent, adjust the unitFrame scale to be equal to blizzard plateFrame
					newUnitFrame = DF:CreateUnitFrame (UIParent, plateFrame:GetName() .. "PlaterUnitFrame", unitFrameOptions, healthBarOptions, castBarOptions, powerBarOptions)
					newUnitFrame:SetAllPoints()
					newUnitFrame:SetFrameStrata ("BACKGROUND")

					--plateFrame:HookScript("OnSizeChanged", Plater.UpdateUIParentScale)
					
					--create a 33ms show animation played when the nameplate is added in the screen
					--nevermind, unitFrame childs are kepping the last alpha value of the animation instead of reseting to their defaults
					--i'm considering this a bug in the animation API from the game client
					--at the moment this cannot be used
					--newUnitFrame.ShowUIParentAnimation = DF:CreateAnimationHub (newUnitFrame, nil, function(self) Plater.UpdateUIParentScale (self:GetParent().PlateFrame) end)
					--DF:CreateAnimation (newUnitFrame.ShowUIParentAnimation, "scale", 1, 0.033, .5, .5, 1, 1)
					--DF:CreateAnimation (newUnitFrame.ShowUIParentAnimation, "alpha", 1, 0.033, .5, 1)
					
					--end of patch
					
					newUnitFrame.IsUIParent = true --expose to scripts the unitFrame is a UIParent child
				else
					newUnitFrame = DF:CreateUnitFrame (plateFrame, plateFrame:GetName() .. "PlaterUnitFrame", unitFrameOptions, healthBarOptions, castBarOptions, powerBarOptions)
				end
				
				newUnitFrame.baseFrameLevel = newUnitFrame:GetFrameLevel()

				plateFrame.unitFrame = newUnitFrame
				--plateFrame.unitFrame:SetPoint("center", plateFrame)
				plateFrame.unitFrame:EnableMouse(false)
				
				--mix plater functions (most are for scripting support) into the unit frame
				DF:Mixin(newUnitFrame, Plater.ScriptMetaFunctions)
				
				--OnHide handler
				newUnitFrame:HookScript("OnHide", newUnitFrame.OnHideWidget)

				--OnHealthUpdate
				newUnitFrame.healthBar:SetHook("OnHealthChange", Plater.OnHealthChange)
				newUnitFrame.healthBar:SetHook("OnHealthMaxChange", Plater.OnHealthMaxChange)
				
				--register details framework hooks
				newUnitFrame.castBar:SetHook("OnShow", Plater.CastBarOnShow_Hook)
				hooksecurefunc(newUnitFrame.castBar, "OnEvent", Plater.CastBarOnEvent_Hook)
				hooksecurefunc(newUnitFrame.castBar, "OnTick", Plater.CastBarOnTick_Hook)
				
				newUnitFrame.HasHooksRegistered = true
				
				--to ensure all applies
				newUnitFrame:UpdateTargetOverlay()
				
				--backup the unit frame address so we can restore it in case a script messes up and override the unit frame
				plateFrame.unitFramePlater = newUnitFrame
				
				--set proprieties
				plateFrame.Plater = true
				plateFrame.unitFrame.Plater = true
				
				plateFrame.unitFrame.PlateFrame = plateFrame
				plateFrame.isNamePlate = true
				plateFrame.unitFrame.isNamePlate = true
				plateFrame.unitFrame.IsUnitNameplate = true
				
				plateFrame.NameAnchor = 0
				plateFrame.unitFrame.healthBar.isNamePlate = true
			
			plateFrame.unitFrame.RefreshID = 0
			
			-- "PlaterMainAuraIcon"
			-- "PlaterSecondaryAuraIcon"

			--> buff frames
				--main buff frame
				plateFrame.unitFrame.BuffFrame = CreateFrame ("frame", plateFrame.unitFrame:GetName() .. "BuffFrame1", plateFrame.unitFrame, BackdropTemplateMixin and "BackdropTemplate")
				plateFrame.unitFrame.BuffFrame.amountAurasShown = 0
				plateFrame.unitFrame.BuffFrame.PlaterBuffList = {}
				plateFrame.unitFrame.BuffFrame.isNameplate = true
				plateFrame.unitFrame.BuffFrame.unitFrame = plateFrame.unitFrame --used on resource frame anchor update
				plateFrame.unitFrame.BuffFrame.healthBar = plateFrame.unitFrame.healthBar
				plateFrame.unitFrame.BuffFrame.AuraCache = {}
				
				--secondary buff frame
				plateFrame.unitFrame.BuffFrame2 = CreateFrame ("frame", plateFrame.unitFrame:GetName() .. "BuffFrame2", plateFrame.unitFrame, BackdropTemplateMixin and "BackdropTemplate")
				plateFrame.unitFrame.BuffFrame2.amountAurasShown = 0
				plateFrame.unitFrame.BuffFrame2.PlaterBuffList = {}
				plateFrame.unitFrame.BuffFrame2.isNameplate = true
				plateFrame.unitFrame.BuffFrame2.unitFrame = plateFrame.unitFrame
				plateFrame.unitFrame.BuffFrame2.healthBar = plateFrame.unitFrame.healthBar
				plateFrame.unitFrame.BuffFrame2.AuraCache = {}
			
			--> identify aura containers
				plateFrame.unitFrame.BuffFrame.Name = "Main" --aura frame 1
				plateFrame.unitFrame.BuffFrame2.Name = "Secondary" --aura frame 2
			
			--> store the secondary anchor inside the regular buff container for speed
			plateFrame.unitFrame.BuffFrame.BuffFrame2 = plateFrame.unitFrame.BuffFrame2
			plateFrame.unitFrame.BuffFrame2.BuffFrame1 = plateFrame.unitFrame.BuffFrame
			
			--> unit aura cache
			plateFrame.unitFrame.AuraCache = {}
			plateFrame.unitFrame.GhostAuraCache = {}
			plateFrame.unitFrame.ExtraAuraCache = {}
			
			local healthBar = plateFrame.unitFrame.healthBar
			
			--cache the unit frame within the health and cast bars, this avoid GetParent() calls
			healthBar.unitFrame = plateFrame.unitFrame
			plateFrame.unitFrame.castBar.unitFrame = plateFrame.unitFrame
			
			--> pre create the scale animation used on animations for spell hits
			Plater.CreateScaleAnimation (plateFrame)
			--> create the animations when the unit goes out of range
			Plater.CreateAlphaAnimation (plateFrame)
			
			--store custom indicators
			plateFrame.unitFrame.CustomIndicators = {}
			
			--> cliclable area debug
				plateFrame.debugAreaTexture = plateFrame:CreateTexture (nil, "background")
				plateFrame.debugAreaTexture:SetColorTexture (.1, .1, .1, .834)
				plateFrame.debugAreaTexture:SetAllPoints()
				plateFrame.debugAreaTexture:Hide()
				plateFrame.debugAreaText = plateFrame:CreateFontString (nil, "artwork", "GameFontNormal")
				plateFrame.debugAreaText:SetPoint ("bottom", plateFrame.debugAreaTexture, "top", 0, 1)
				plateFrame.debugAreaText:SetText ("valid area for clicks")
				plateFrame.debugAreaText:SetTextColor (.7, .7, .7)
				plateFrame.debugAreaText:Hide()
			
			--> Indicators
				--container to store created indicators, they are created at run time
				plateFrame.IconIndicators = {}
			
			--> flash aggro
				Plater.CreateAggroFlashFrame (plateFrame)
				plateFrame.playerHasAggro = false
			
			--> target indicators
				--left and right target indicators
				plateFrame.unitFrame.TargetTextures2Sides = {}
				plateFrame.unitFrame.TargetTextures4Sides = {}
				for i = 1, 2 do
					local targetTexture = healthBar:CreateTexture (nil, "overlay")
					targetTexture:SetDrawLayer ("overlay", 7)
					tinsert (plateFrame.unitFrame.TargetTextures2Sides, targetTexture)
				end
				for i = 1, 4 do
					local targetTexture = healthBar:CreateTexture (nil, "overlay")
					targetTexture:SetDrawLayer ("overlay", 7)
					tinsert (plateFrame.unitFrame.TargetTextures4Sides, targetTexture)
				end
				
				--two extra target glow placed outside the healthbar, one above and another below the health bar
				local TargetNeonUp = plateFrame.unitFrame:CreateTexture (nil, "overlay")
				TargetNeonUp:SetDrawLayer ("overlay", 7)
				TargetNeonUp:SetBlendMode ("ADD")
				TargetNeonUp:Hide()
				plateFrame.TargetNeonUp = TargetNeonUp
				plateFrame.unitFrame.TargetNeonUp = TargetNeonUp
				
				local TargetNeonDown = plateFrame.unitFrame:CreateTexture (nil, "overlay")
				TargetNeonDown:SetDrawLayer ("overlay", 7)
				TargetNeonDown:SetBlendMode ("ADD")
				TargetNeonDown:SetTexCoord (0, 1, 1, 0)
				TargetNeonDown:Hide()
				plateFrame.TargetNeonDown = TargetNeonDown
				plateFrame.unitFrame.TargetNeonDown = TargetNeonDown
				
			--> target overlay (the texture added above the nameplate when the unit is selected)
				plateFrame.unitFrame.targetOverlayTexture = healthBar:CreateTexture (nil, "artwork")
				plateFrame.unitFrame.targetOverlayTexture:SetDrawLayer ("artwork", 2)
				plateFrame.unitFrame.targetOverlayTexture:SetBlendMode ("ADD")
				plateFrame.unitFrame.targetOverlayTexture:SetAllPoints()
			
			--> create the highlight texture (when the mouse passes over the nameplate and receives a highlight)
				Plater.CreateHighlightNameplate (plateFrame)

			--> health bar overlay
				--> create an overlay frame that sits just above the health bar
				--this is ideal for adding borders and other overlays
				healthBar.FrameOverlay = CreateFrame ("frame", "$parentOverlayFrame", healthBar, BackdropTemplateMixin and "BackdropTemplate")
				healthBar.FrameOverlay:SetAllPoints()
			
			--> execute range textures and animations
				--health cutoff texture shown inside the health bar
				local healthCutOff = healthBar:CreateTexture (nil, "overlay")
				healthCutOff:SetDrawLayer ("overlay", 7)
				healthCutOff:SetTexture ([[Interface\AddOns\Plater\images\health_bypass_indicator]])

				healthCutOff:SetBlendMode ("ADD")
				healthCutOff:Hide()
				healthBar.healthCutOff = healthCutOff
				healthBar.ExecuteRangeHealthCutOff = healthCutOff --alias for scripting
			
				local cutoffAnimationOnPlay = function()
					healthCutOff:Show()
				end
				local cutoffAnimationOnStop = function()
					healthCutOff:SetAlpha (.5)
				end
				
				local healthCutOffShowAnimation = DF:CreateAnimationHub (healthCutOff, cutoffAnimationOnPlay, cutoffAnimationOnStop)
				DF:CreateAnimation (healthCutOffShowAnimation, "Scale", 1, .2, .3, .3, 1.2, 1.2)
				DF:CreateAnimation (healthCutOffShowAnimation, "Scale", 2, .2, 1.2, 1.2, 1, 1)
				DF:CreateAnimation (healthCutOffShowAnimation, "Alpha", 1, .2, .2, 1)
				DF:CreateAnimation (healthCutOffShowAnimation, "Alpha", 2, .2, 1, .5)
				healthCutOff.ShowAnimation = healthCutOffShowAnimation
				
				--shield indicator
				local shieldIndicator = healthBar:CreateTexture(nil, "overlay", nil, 7)
				shieldIndicator:SetPoint("bottomleft", healthBar, "bottomleft", 0, 0)
				shieldIndicator:SetHeight(3)
				shieldIndicator:SetTexture([[Interface\AddOns\Plater\images\shieldbar]])
				shieldIndicator:SetAlpha(0.85)
				shieldIndicator:Hide()
				healthBar.shieldIndicator = shieldIndicator

				--overlay for the healthbar showing the healthbar of the execute (shown when the unit is on execute range)
				local executeRange = healthBar:CreateTexture (nil, "border")
				executeRange:SetTexture ([[Interface\AddOns\Plater\images\execute_bar]])
				PixelUtil.SetPoint (executeRange, "left", healthBar, "left", 0, 0)
				healthBar.executeRange = executeRange
				healthBar.ExecuteRangeBar = executeRange --alias for scripting
				executeRange:Hide()

				--two extra execute glow placed outside the healthbar (disabled by default)
				local executeGlowUp = healthBar:CreateTexture (nil, "overlay")
				executeGlowUp:SetTexture ([[Interface\AddOns\Plater\images\blue_neon]])
				executeGlowUp:SetTexCoord (0, 1, 0, 0.5)
				executeGlowUp:SetHeight (32)		
				executeGlowUp:SetBlendMode ("ADD")
				executeGlowUp:Hide()
				PixelUtil.SetPoint (executeGlowUp, "bottomright", healthBar, "topright", 0, 0)
				PixelUtil.SetPoint (executeGlowUp, "bottomleft", healthBar, "topleft", 0, 0)
				healthBar.ExecuteGlowUp = executeGlowUp
				
				local executeGlowDown = healthBar:CreateTexture (nil, "overlay")
				executeGlowDown:SetTexture ([[Interface\AddOns\Plater\images\blue_neon]])
				executeGlowDown:SetTexCoord (0, 1, 0.5, 1)
				executeGlowDown:SetHeight (32)
				executeGlowDown:SetBlendMode ("ADD")
				executeGlowDown:Hide()
				PixelUtil.SetPoint (executeGlowDown, "topright", healthBar, "bottomright", 0, 0)
				PixelUtil.SetPoint (executeGlowDown, "topleft", healthBar, "bottomleft", 0, 0)
				healthBar.ExecuteGlowDown = executeGlowDown
				
				local executeGlowAnimationOnPlay = function (self)
					self:GetParent():Show()
				end
				local executeGlowAnimationOnStop = function()
					
				end
				
				executeGlowUp.ShowAnimation = DF:CreateAnimationHub (executeGlowUp, executeGlowAnimationOnPlay, executeGlowAnimationOnStop)
				DF:CreateAnimation (executeGlowUp.ShowAnimation, "Scale", 1, .2, 1, .1, 1, 1.2, "bottom", 0, 0)
				DF:CreateAnimation (executeGlowUp.ShowAnimation, "Scale", 1, .2, 1, 1, 1, 1)
				
				executeGlowDown.ShowAnimation = DF:CreateAnimationHub (executeGlowDown, executeGlowAnimationOnPlay, executeGlowAnimationOnStop)
				DF:CreateAnimation (executeGlowDown.ShowAnimation, "Scale", 1, .2, 1, .1, 1, 1.2, "top", 0, 0)
				DF:CreateAnimation (executeGlowDown.ShowAnimation, "Scale", 1, .2, 1, 1.1, 1, 1)

			--> create the raid target widgets
				--raid target inside the health bar
				local raidTarget = healthBar:CreateTexture (nil, "overlay")
				PixelUtil.SetPoint (raidTarget, "right", raidTarget:GetParent(), "right", -2, 0)
				plateFrame.RaidTarget = raidTarget
				healthBar.ExtraRaidMark = raidTarget --alias for scripting

				--raid target outside the health bar
				plateFrame.unitFrame.PlaterRaidTargetFrame = CreateFrame ("frame", nil, plateFrame.unitFrame, BackdropTemplateMixin and "BackdropTemplate")
				--plateFrame.unitFrame.PlaterRaidTargetFrame = CreateFrame ("frame", nil, plateFrame.unitFrame.healthBar, BackdropTemplateMixin and "BackdropTemplate")
				local targetFrame = plateFrame.unitFrame.PlaterRaidTargetFrame
				targetFrame:SetSize (22, 22)
				PixelUtil.SetPoint (targetFrame, "right", healthBar, "left", -15, 0)
				
				--icon
				targetFrame.RaidTargetIcon = targetFrame:CreateTexture (nil, "artwork")
				targetFrame.RaidTargetIcon:SetAllPoints()
				targetFrame.RaidTargetIcon:SetTexture ([[Interface\TargetingFrame\UI-RaidTargetingIcons]])
				
				--create show animation to play when the icon is set to show
				local raidMarkAnimation = DF:CreateAnimationHub (targetFrame.RaidTargetIcon)
				DF:CreateAnimation (raidMarkAnimation, "Scale", 1, .075, .1, .1, 1.2, 1.2)
				DF:CreateAnimation (raidMarkAnimation, "Scale", 2, .075, 1.2, 1.2, 1, 1)
				targetFrame.RaidTargetIcon.ShowAnimation = raidMarkAnimation
			
			--> create details! integration strings
				healthBar.DetailsRealTime = healthBar:CreateFontString (nil, "overlay", "GameFontNormal")
				healthBar.DetailsRealTimeFromPlayer = healthBar:CreateFontString (nil, "overlay", "GameFontNormal")
				healthBar.DetailsDamageTaken = healthBar:CreateFontString (nil, "overlay", "GameFontNormal")
			
			--> tick frames (these frames are used for OnUpdate scripts)
				local onTickFrame = CreateFrame ("frame", nil, plateFrame, BackdropTemplateMixin and "BackdropTemplate")
				plateFrame.OnTickFrame = onTickFrame
				onTickFrame.unit = plateFrame [MEMBER_UNITID]
				onTickFrame.HealthBar = healthBar
				onTickFrame.PlateFrame = plateFrame
				onTickFrame.unitFrame = plateFrame.unitFrame
				onTickFrame.BuffFrame = plateFrame.unitFrame.BuffFrame
				onTickFrame.BuffFrame2 = plateFrame.unitFrame.BuffFrame2
				
				
				--> create a second castbar
				local castBar2 = DF:CreateCastBar (plateFrame.unitFrame, "$parentCastBar2")
				plateFrame.unitFrame.castBar2 = castBar2
				castBar2.Icon:ClearAllPoints()
				castBar2.Icon:SetPoint("right", castBar2, "left", -1, 0)

				castBar2.FrameOverlay = CreateFrame ("frame", "$parentOverlayFrame", castBar2, BackdropTemplateMixin and "BackdropTemplate")
				castBar2.FrameOverlay:SetAllPoints()

				--pushing the spell name up
				castBar2.Text:SetParent (castBar2.FrameOverlay)
				
				--does have a border but its alpha is zero by default
				castBar2.FrameOverlay:SetBackdrop ({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1})
				castBar2.FrameOverlay:SetBackdropBorderColor (1, 1, 1, 0)
				castBar2:SetPoint("topleft", plateFrame.unitFrame.castBar, "bottomleft", 0, -2)
				castBar2:SetPoint("topright", plateFrame.unitFrame.castBar, "bottomright", 0, -2)

			
			--> unit name
				--regular name
				plateFrame.unitFrame.unitName:SetParent (healthBar) --the name is parented to unitFrame in the framework, parent it to health bar
				healthBar.unitName = plateFrame.unitFrame.unitName
				healthBar.PlateFrame = plateFrame
				plateFrame.unitName = plateFrame.unitFrame.unitName
				plateFrame.CurrentUnitNameString = plateFrame.unitFrame.unitName
				healthBar.unitName:SetDrawLayer ("overlay", 7)
				
				--special name and title
				local ActorNameSpecial = plateFrame.unitFrame:CreateFontString (nil, "artwork", "GameFontNormal")
				plateFrame.unitFrame.ActorNameSpecial = ActorNameSpecial --alias for scripts
				plateFrame.ActorNameSpecial = ActorNameSpecial
				PixelUtil.SetPoint (plateFrame.ActorNameSpecial, "center", plateFrame, "center", 0, 0)
				plateFrame.ActorNameSpecial:Hide()
				
				local ActorTitleSpecial = plateFrame.unitFrame:CreateFontString (nil, "artwork", "GameFontNormal")
				plateFrame.unitFrame.ActorTitleSpecial = ActorTitleSpecial --alias for scripts
				plateFrame.ActorTitleSpecial = ActorTitleSpecial
				PixelUtil.SetPoint (plateFrame.ActorTitleSpecial, "top", ActorNameSpecial, "bottom", 0, -2)
				plateFrame.ActorTitleSpecial:Hide()
				
				
			--> level text
				local actorLevel = healthBar:CreateFontString (nil, "overlay", "GameFontNormal")
				healthBar.actorLevel = actorLevel
			
			--> life percent text
				local lifePercent = healthBar:CreateFontString (nil, "overlay", "GameFontNormal")
				lifePercent:SetDrawLayer ("overlay", 5)
				healthBar.lifePercent = lifePercent
				
			--> non target occlusion texture (disabled by default, it applies alayer of darkness above nameplates of units that aren't the player target)
				local obscuredTexture = healthBar:CreateTexture (nil, "overlay")
				obscuredTexture:SetDrawLayer ("overlay", 6)
				obscuredTexture:SetAllPoints()
				obscuredTexture:SetTexture ("Interface\\Tooltips\\UI-Tooltip-Background")
				obscuredTexture:SetVertexColor (0, 0, 0, 1)
				plateFrame.Obscured = obscuredTexture

				obscuredTexture.Mask = healthBar:CreateMaskTexture(nil, "artwork")
				obscuredTexture.Mask:SetAllPoints(obscuredTexture)
				obscuredTexture.Mask:SetTexture([[Interface\AddOns\Plater\masks\mask1]])
				obscuredTexture.Mask:Hide()
				obscuredTexture:AddMaskTexture(obscuredTexture.Mask)

			--> create the extra icon frame (used for the special aura)
				local options = {
					icon_width = 20, 
					icon_height = 20, 
					texcoord = {.1, .9, .1, .9},
					show_text = true,
				}
				
				plateFrame.unitFrame.ExtraIconFrame = DF:CreateIconRow (plateFrame.unitFrame, "$parentExtraIconRow", options)
				plateFrame.unitFrame.ExtraIconFrame:ClearIcons()
				plateFrame.unitFrame.ExtraIconFrame.RefreshID = 0
				plateFrame.unitFrame.ExtraIconFrame.AuraCache = {}
				--> cache the extra icon frame inside the buff frame for speed
				plateFrame.unitFrame.BuffFrame.ExtraIconFrame = plateFrame.unitFrame.ExtraIconFrame
			
			--> Support for DBM and BigWigs Nameplate Auras
				Plater.CreateBossModAuraFrame(plateFrame.unitFrame)
			
			--> 3D model frame
				plateFrame.Top3DFrame = CreateFrame ("playermodel", plateFrame:GetName() .. "3DFrame", plateFrame, "ModelWithControlsTemplate")
				plateFrame.Top3DFrame:SetPoint ("bottom", plateFrame, "top", 0, -100)
				plateFrame.Top3DFrame:SetSize (200, 250)
				plateFrame.Top3DFrame:EnableMouse (false)
				plateFrame.Top3DFrame:EnableMouseWheel (false)
				plateFrame.Top3DFrame:Hide()
				plateFrame.unitFrame.Top3DFrame = plateFrame.Top3DFrame
			
			--> castbar // create custom widgets for the cast bar
				--[=[
					castbar members:
						.Text			text string to show the spell name
						.background 		default background created in the unit frame
						.extraBackground 	an extra background shown when it has low alpha
						.barTexture		cast bar texture
						.Icon			texture to show the spell icon
						.Spark			texture that follows the cast progress
						.percentText		text showing that cast progress, usually is the time left to finish the cast
						.BorderShield 	border shown in the icon when the cast isn't interruptible
						.FrameOverlay 	invisible frame shown above the nameplate, useful for creating overlays
						.TargetName 		text showing the name for the target
						
				--]=]
			
				--set a UnitFrame member so scripts can get a quick reference of the unit frame from the castbar without calling for GetParent()
				local castBar = plateFrame.unitFrame.castBar
				castBar.PlateFrame = plateFrame
				castBar.unitFrame = plateFrame.unitFrame
				castBar.IsCastBar = true
				castBar.isNamePlate = true
				castBar.ThrottleUpdate = 0
				
				--check if Masque is enabled on Plater and reskin the cast icon
				local castIconFrame = castBar.Icon
				if (Plater.Masque and not castIconFrame.Masqued) then
					--as masque only skins buttons and not textures alone, work around that with a dummy frame and some meta-table shenannigans to not break anything...
					--create the button frame and anchor the original icon within
					local dummyMasqueIconButton = CreateFrame ("Button", castBar:GetName() .. "dummyMasqueIconButton", castBar, BackdropTemplateMixin and "BackdropTemplate")
					dummyMasqueIconButton:SetSize(castIconFrame:GetSize())
					castIconFrame:ClearAllPoints()
					castIconFrame:SetParent(dummyMasqueIconButton)
					castIconFrame:SetPoint("TOPLEFT")
					castIconFrame:SetPoint("BOTTOMRIGHT")
					
					dummyMasqueIconButton:EnableMouse (false)
					if dummyMasqueIconButton.EnableMouseMotion then
						dummyMasqueIconButton:EnableMouseMotion (false)
					end
					
					castIconFrame:Show()
					
					--overwrite original and keep a reference
					dummyMasqueIconButton.Icon = castIconFrame
					castBar.IconOrig = castIconFrame
					castBar.Icon = dummyMasqueIconButton
					
					--now ensure all calls to the icon which are directed towards the original texture are re-routed to the icon texture
					local origIndex = getmetatable(dummyMasqueIconButton).__index
					local metaTable = {
						__index = function (t,k)
							--print(k, rawget(dummyMasqueIconButton, k), rawget(origIndex, k), castIconFrame[k])
							local v = rawget(dummyMasqueIconButton, k) or rawget(origIndex, k)
							if not v then
								v = castIconFrame[k]--rawget(castIconFrame, k) or rawget(getmetatable(castIconFrame).__index, k) or rawget(t, k)
								if type(v) == "function" then
									return function(self, ...) return castIconFrame[k](castIconFrame, ...) end
								end
							end
							return v
						end,
					
						__newindex = function (t,k,v)
							rawset(t, k, v)
						end
						
					}
					setmetatable(dummyMasqueIconButton, metaTable)
					dummyMasqueIconButton:Show()
					
					-- now skin!
					local t = {
						Icon = castIconFrame,
					}
					Plater.Masque.CastIcon:AddButton (dummyMasqueIconButton, t, "Frame", true)
					Plater.Masque.CastIcon:ReSkin(dummyMasqueIconButton)
					castIconFrame.Masqued = true
					dummyMasqueIconButton.Masqued = true
				end
				
				--mix the plater functions into the castbar (most of the functions are for scripting support)
				DF:Mixin (castBar, Plater.ScriptMetaFunctions)
				castBar:HookScript ("OnHide", castBar.OnHideWidget)
				
				--> create an overlay frame that sits just above the castbar
				--this is ideal for adding borders and other overlays
				castBar.FrameOverlay = CreateFrame ("frame", "$parentOverlayFrame", castBar, BackdropTemplateMixin and "BackdropTemplate")
				castBar.FrameOverlay:SetAllPoints()

				--create a frame that are always below the castbar, this frame help with extra backdrops
				--textures, animations are need to be placed below the cast bar
				castBar.FrameDownlayer = CreateFrame ("frame", "$parentDownlayerFrame", castBar, BackdropTemplateMixin and "BackdropTemplate")
				castBar.FrameDownlayer:SetFrameLevel(castBar:GetFrameLevel()-1)
				castBar.FrameDownlayer:SetAllPoints()

				--pushing the spell name and timer up
				castBar.Text:SetParent (castBar.FrameOverlay)
				castBar.percentText:SetParent (castBar.FrameOverlay)
				--does have a border but its alpha is zero by default
				castBar.FrameOverlay:SetBackdrop ({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1})
				castBar.FrameOverlay:SetBackdropBorderColor (1, 1, 1, 0)
				--creates the target name overlay which shows who the unit is targetting while casting (this is disabled by default)
				castBar.FrameOverlay.TargetName = castBar.FrameOverlay:CreateFontString (nil, "overlay", "GameFontNormal")
				castBar.TargetName = castBar.FrameOverlay.TargetName --alias for scripts
			
				--> create the spell color texture
				castBar.castColorTexture = castBar:CreateTexture("$parentCastColor", "background", nil, -6)

				--create custom border frame for modeling
				if (Plater.CreateCustomDesignBorder) then
					Plater.CreateCustomDesignBorder(castBar)
				end

			--> border
				--create a border using default borders from the retail game
				local healthBarBorder = DF:CreateFullBorder(nil, plateFrame.unitFrame.healthBar)
				local borderOffset = 0 -- -1 * (Plater.db.profile.use_ui_parent and (UIParent:GetEffectiveScale() * Plater.db.profile.ui_parent_scale_tune)) or 1
				PixelUtil.SetPoint (healthBarBorder, "TOPLEFT", plateFrame.unitFrame.healthBar, "TOPLEFT", -borderOffset, borderOffset)
				PixelUtil.SetPoint (healthBarBorder, "TOPRIGHT", plateFrame.unitFrame.healthBar, "TOPRIGHT", borderOffset, borderOffset)
				PixelUtil.SetPoint (healthBarBorder, "BOTTOMLEFT", plateFrame.unitFrame.healthBar, "BOTTOMLEFT", -borderOffset, -borderOffset)
				PixelUtil.SetPoint (healthBarBorder, "BOTTOMRIGHT", plateFrame.unitFrame.healthBar, "BOTTOMRIGHT", borderOffset, -borderOffset)
				healthBarBorder.Left:SetDrawLayer("OVERLAY", 6)
				healthBarBorder.Right:SetDrawLayer("OVERLAY", 6)
				healthBarBorder.Top:SetDrawLayer("OVERLAY", 6)
				healthBarBorder.Bottom:SetDrawLayer("OVERLAY", 6)
				plateFrame.unitFrame.healthBar.border = healthBarBorder
				
				local powerBarBorder = DF:CreateFullBorder(nil, plateFrame.unitFrame.powerBar)
				PixelUtil.SetPoint (powerBarBorder, "TOPLEFT", plateFrame.unitFrame.powerBar, "TOPLEFT", -borderOffset, borderOffset)
				PixelUtil.SetPoint (powerBarBorder, "TOPRIGHT", plateFrame.unitFrame.powerBar, "TOPRIGHT", borderOffset, borderOffset)
				PixelUtil.SetPoint (powerBarBorder, "BOTTOMLEFT", plateFrame.unitFrame.powerBar, "BOTTOMLEFT", -borderOffset, -borderOffset)
				PixelUtil.SetPoint (powerBarBorder, "BOTTOMRIGHT", plateFrame.unitFrame.powerBar, "BOTTOMRIGHT", borderOffset, -borderOffset)
				powerBarBorder.Left:SetDrawLayer("OVERLAY", 6)
				powerBarBorder.Right:SetDrawLayer("OVERLAY", 6)
				powerBarBorder.Top:SetDrawLayer("OVERLAY", 6)
				powerBarBorder.Bottom:SetDrawLayer("OVERLAY", 6)
				plateFrame.unitFrame.powerBar.border = powerBarBorder
				powerBarBorder:SetVertexColor (0, 0, 0, 1)

				--create custom border frame for modeling
				if (Plater.CreateCustomDesignBorder) then
					Plater.CreateCustomDesignBorder(healthBar)
				end

				--create custom border frame for modeling
				if (Plater.CreateCustomDesignBorder) then
					Plater.CreateCustomDesignBorder(plateFrame.unitFrame.powerBar)
				end
			
			--> focus indicator
				local focusIndicator = healthBar:CreateTexture(nil, "overlay")
				focusIndicator:SetDrawLayer("overlay", 2)
				PixelUtil.SetPoint(focusIndicator, "topleft", healthBar, "topleft", 0, 0)
				PixelUtil.SetPoint(focusIndicator, "bottomright", healthBar, "bottomright", 0, 0)
				focusIndicator:Hide()
				healthBar.FocusIndicator = focusIndicator
				plateFrame.FocusIndicator = focusIndicator
				plateFrame.unitFrame.FocusIndicator = focusIndicator
			
			--> low aggro warning
				plateFrame.unitFrame.aggroGlowUpper = plateFrame:CreateTexture (nil, "background", nil, -4)
				PixelUtil.SetPoint (plateFrame.unitFrame.aggroGlowUpper, "bottomleft", plateFrame.unitFrame.healthBar, "topleft", -3, 0)
				PixelUtil.SetPoint (plateFrame.unitFrame.aggroGlowUpper, "bottomright", plateFrame.unitFrame.healthBar, "topright", 3, 0)
				plateFrame.unitFrame.aggroGlowUpper:SetTexture ([[Interface\BUTTONS\UI-Panel-Button-Glow]])
				plateFrame.unitFrame.aggroGlowUpper:SetTexCoord (0, 95/128, 0, 9/64)
				plateFrame.unitFrame.aggroGlowUpper:SetBlendMode ("ADD")
				plateFrame.unitFrame.aggroGlowUpper:SetHeight (4)
				plateFrame.unitFrame.aggroGlowUpper:Hide()
				
				plateFrame.unitFrame.aggroGlowLower = plateFrame:CreateTexture (nil, "background", nil, -4)
				PixelUtil.SetPoint (plateFrame.unitFrame.aggroGlowLower, "topleft", plateFrame.unitFrame.healthBar, "bottomleft", -3, 0)
				PixelUtil.SetPoint (plateFrame.unitFrame.aggroGlowLower, "topright", plateFrame.unitFrame.healthBar, "bottomright", 3, 0)
				plateFrame.unitFrame.aggroGlowLower:SetTexture ([[Interface\BUTTONS\UI-Panel-Button-Glow]])
				plateFrame.unitFrame.aggroGlowLower:SetTexCoord (0, 95/128, 30/64, 38/64)
				plateFrame.unitFrame.aggroGlowLower:SetBlendMode ("ADD")
				plateFrame.unitFrame.aggroGlowLower:SetHeight (4)
				plateFrame.unitFrame.aggroGlowLower:Hide()

			--> soft-interact icon
				plateFrame.unitFrame.softInteractIconFrame = CreateFrame ("frame",plateFrame.unitFrame:GetName() .. "softInteractIconFrame", plateFrame, BackdropTemplateMixin and "BackdropTemplate")
				plateFrame.unitFrame.softInteractIcon = plateFrame.unitFrame.softInteractIconFrame:CreateTexture("$parentIcon", "OVERLAY")
				plateFrame.unitFrame.softInteractIcon:SetParent(plateFrame)
				plateFrame.unitFrame.softInteractIcon:SetTexture(136243)
				plateFrame.unitFrame.softInteractIcon:Show()
				plateFrame.unitFrame.softInteractIconFrame:SetFrameLevel(plateFrame.unitFrame.healthBar:GetFrameLevel() + 25)
				plateFrame.unitFrame.softInteractIconFrame.Mask = plateFrame.unitFrame.softInteractIconFrame:CreateMaskTexture(nil, "OVERLAY", nil, 1)
				plateFrame.unitFrame.softInteractIconFrame.Mask:Show()
				plateFrame.unitFrame.softInteractIconFrame.Mask:SetAtlas("CircleMaskScalable", true)
				--plateFrame.unitFrame.softInteractIconFrame.Mask:SetScale(1)
				plateFrame.unitFrame.softInteractIcon:AddMaskTexture(plateFrame.unitFrame.softInteractIconFrame.Mask)
				plateFrame.unitFrame.softInteractIconFrame.Mask:ClearAllPoints()
				PixelUtil.SetPoint(plateFrame.unitFrame.softInteractIconFrame.Mask, "CENTER", plateFrame.unitFrame.softInteractIconFrame, "CENTER", 0, 0)
				plateFrame.unitFrame.softInteractIconFrame.Mask:SetAllPoints(plateFrame.unitFrame.softInteractIcon)
				plateFrame.unitFrame.softInteractIconFrame:Hide()
				plateFrame.unitFrame.softInteractIcon.anchor = { side = 8, x = 0, y = 18, }
				plateFrame.unitFrame.softInteractIcon.size = 24
				--Plater.SetAnchor(plateFrame.unitFrame.softInteractIconFrame, plateFrame.unitFrame.softInteractIcon.anchor or { side = 8, x = 0, y = 18, }, plateFrame.unitFrame.healthBar)
				--Plater.SetAnchor(plateFrame.unitFrame.softInteractIconFrame, plateFrame.unitFrame.softInteractIcon.anchor or { side = 8, x = 0, y = 18, }, plateFrame.unitFrame.PlateFrame)
			
			--> name plate created hook
				if (HOOK_NAMEPLATE_CREATED.ScriptAmount > 0) then
					for i = 1, HOOK_NAMEPLATE_CREATED.ScriptAmount do
						local globalScriptObject = HOOK_NAMEPLATE_CREATED [i]
						local scriptContainer = plateFrame.unitFrame:ScriptGetContainer()
						local scriptInfo = plateFrame.unitFrame:HookGetInfo (globalScriptObject, scriptContainer, "Nameplate Created")
						plateFrame.unitFrame:ScriptRunHook (scriptInfo, "Nameplate Created")
					end
				end
		end,
		
		---@param event string
		---@param unitBarId string
		FORBIDDEN_NAME_PLATE_UNIT_ADDED = function (event, unitBarId)
			local unitID = unitBarId
		
			local plateFrame = C_NamePlate.GetNamePlateForUnit (unitID, true)
			if (plateFrame) then -- and plateFrame.template == "ForbiddenNamePlateUnitFrameTemplate"
			
				if (not IS_WOW_PROJECT_MAINLINE) then
					-- this is for classic cast bars on blizzard default nameplates
					if GetCVarBool ("nameplateShowOnlyNames") or Plater.db.profile.saved_cvars.nameplateShowOnlyNames == "1" then
						TextureLoadingGroupMixin.RemoveTexture({ textures = plateFrame.UnitFrame.CastBar }, "showCastbar")
					else
						TextureLoadingGroupMixin.AddTexture({ textures = plateFrame.UnitFrame.CastBar }, "showCastbar")
					end
				end
			end
		end,

		-- ~added ãdded 
		---@param event string
		---@param unitBarId string
		NAME_PLATE_UNIT_ADDED = function (event, unitBarId)
			--ViragDevTool_AddData({ctime = GetTime(), unit = unitBarId or "nil", stack = debugstack()}, "NAME_PLATE_UNIT_ADDED - " .. (unitBarId or "nil"))
			--debug for hunter faith death
--			if (select (2, UnitClass (unitBarId)) == "HUNTER") then
--				print ("nameplate added", UnitName (unitBarId))
--			end
		
			local unitID = unitBarId

			---@type plateframe
			local plateFrame = C_NamePlate.GetNamePlateForUnit (unitID)
			if (not plateFrame) then
				--try forbidden as well for hiding stuff
				plateFrame = C_NamePlate.GetNamePlateForUnit (unitID, true)
				if (plateFrame) then
					if (not IS_WOW_PROJECT_MAINLINE) then
						if GetCVarBool ("nameplateShowOnlyNames") or Plater.db.profile.saved_cvars.nameplateShowOnlyNames == "1" then
							TextureLoadingGroupMixin.RemoveTexture({ textures = plateFrame.UnitFrame.CastBar }, "showCastbar")
						else
							TextureLoadingGroupMixin.AddTexture({ textures = plateFrame.UnitFrame.CastBar }, "showCastbar")
						end
					end
				end
				return
			end
			
			--> check the unit frame integrity, several times some weakaura or script mess with the unit frame
			if (not plateFrame.unitFrame or not plateFrame.unitFrame.SetUnit) then
				plateFrame.unitFrame = plateFrame.unitFramePlater
			end
			
			--get and format the reaction to always be the value of the constants, then cache the reaction in some widgets for performance
			Plater.UpdateSoftInteractTarget(plateFrame)
			local reaction = UnitReaction (unitID, "player")
			local isSoftInteract = plateFrame.isSoftInteract
			local isObject = plateFrame.isObject
			local isSoftInteractObject = isObject and isSoftInteract
			reaction = reaction or isSoftInteract and Plater.UnitReaction.UNITREACTION_NEUTRAL or Plater.UnitReaction.UNITREACTION_HOSTILE
			reaction = reaction <= Plater.UnitReaction.UNITREACTION_HOSTILE and Plater.UnitReaction.UNITREACTION_HOSTILE or reaction >= Plater.UnitReaction.UNITREACTION_FRIENDLY and Plater.UnitReaction.UNITREACTION_FRIENDLY or Plater.UnitReaction.UNITREACTION_NEUTRAL
			
			local isWidgetOnlyMode = (IS_WOW_PROJECT_MAINLINE) and UnitNameplateShowsWidgetsOnly (unitID) or false
			local isBattlePet = (IS_WOW_PROJECT_MAINLINE) and UnitIsBattlePet(unitID) or false
			local isPlayer = UnitIsPlayer (unitID)
			local isSelf = UnitIsUnit (unitID, "player")
			
			plateFrame [MEMBER_NPCID] = nil
			plateFrame.unitFrame [MEMBER_NPCID] = nil
			plateFrame [MEMBER_GUID] = UnitGUID (unitID) or ""
			plateFrame.unitFrame [MEMBER_GUID] = plateFrame [MEMBER_GUID]
			
			if (not isPlayer) then
				Plater.GetNpcID (plateFrame)
			end
			
			local actorType
			if (unitID) then
				
				if (isSelf) then
					--> personal health bar
					actorType = ACTORTYPE_PLAYER
					
				else
					--> regular nameplate
					
					if (isPlayer) then
						--unit is a player
						
						if (reaction >= Plater.UnitReaction.UNITREACTION_FRIENDLY) then
							actorType = ACTORTYPE_FRIENDLY_PLAYER
							
						else
							actorType = ACTORTYPE_ENEMY_PLAYER
							
						end
					else
						--the unit is a npc
						
						if (reaction >= Plater.UnitReaction.UNITREACTION_FRIENDLY) then
							actorType = ACTORTYPE_FRIENDLY_NPC
							
						elseif isBattlePet then
							actorType = ACTORTYPE_FRIENDLY_NPC
							
						else
							--includes neutral npcs
							actorType = ACTORTYPE_ENEMY_NPC
							
						end
					end
				end
			end
			local isPlateEnabled = not isSoftInteractObject and (DB_PLATE_CONFIG [actorType].module_enabled and not isWidgetOnlyMode) or (not Plater.db.profile.ignore_softinteract_objects and isSoftInteractObject)
			isPlateEnabled = (isPlayer or not Plater.ForceBlizzardNameplateUnits[plateFrame [MEMBER_NPCID]]) and isPlateEnabled
			
			local blizzardPlateFrameID = tostring(plateFrame.UnitFrame)
			plateFrame.unitFrame.blizzardPlateFrameID = blizzardPlateFrameID
			
			--if (not plateFrame.UnitFrame.HasPlaterHooksRegistered) then
			if not HOOKED_BLIZZARD_PLATEFRAMES[blizzardPlateFrameID] then
				--print(HOOKED_BLIZZARD_PLATEFRAMES[tostring(plateFrame.UnitFrame)], tostring(plateFrame.UnitFrame), plateFrame.UnitFrame.HasPlaterHooksRegistered)
                --hook the retail nameplate
                --plateFrame.UnitFrame:HookScript("OnShow", Plater.OnRetailNamePlateShow)
				hooksecurefunc(plateFrame.UnitFrame, "Show", Plater.OnRetailNamePlateShow)
                --plateFrame.UnitFrame.HasPlaterHooksRegistered = true
				HOOKED_BLIZZARD_PLATEFRAMES[blizzardPlateFrameID] = true
				
            end
			
			-- we should clear stuff here, tbh...
			
			if isPlateEnabled then
				ENABLED_BLIZZARD_PLATEFRAMES[blizzardPlateFrameID] = false
				
			else
				plateFrame.unitFrame.PlaterOnScreen = false
				ENABLED_BLIZZARD_PLATEFRAMES[blizzardPlateFrameID] = true
				plateFrame.unitFrame:Hide()
				
				-- this is for classic cast bars on blizzard default nameplates
				if (not IS_WOW_PROJECT_MAINLINE) then
					if GetCVarBool ("nameplateShowOnlyNames") or Plater.db.profile.saved_cvars.nameplateShowOnlyNames == "1" then
						TextureLoadingGroupMixin.RemoveTexture({ textures = plateFrame.UnitFrame.CastBar }, "showCastbar")
					else
						TextureLoadingGroupMixin.AddTexture({ textures = plateFrame.UnitFrame.CastBar }, "showCastbar")
					end
				end
				
				return
			end
			
			local requiresScheduledUpdate = false
			if not NAMEPLATES_ON_SCREEN_CACHE[unitID] then
				NAMEPLATES_ON_SCREEN_CACHE[unitID] = true
				NUM_NAMEPLATES_ON_SCREEN = NUM_NAMEPLATES_ON_SCREEN + 1
			else
				requiresScheduledUpdate = true
			end
			
			--hide blizzard namepaltes
			--plateFrame.UnitFrame:Hide()
			Plater.OnRetailNamePlateShow(plateFrame.UnitFrame)
			--show plater unit frame
			plateFrame.unitFrame:Show()
			
			plateFrame.unitFrame.PlaterOnScreen = true
			
			Plater.AddToAuraUpdate(unitID)
			-- update DBM and BigWigs nameplate auras
			Plater.EnsureUpdateBossModAuras(plateFrame [MEMBER_GUID])
			
			--save the last unit type shown in this plate
			plateFrame.PreviousUnitType = plateFrame.actorType
			
			--caching frames
			local unitFrame = plateFrame.unitFrame
			local castBar = unitFrame.castBar
			local healthBar = unitFrame.healthBar

			unitFrame.IsNeutralOrHostile = actorType == ACTORTYPE_ENEMY_NPC or actorType == ACTORTYPE_ENEMY_PLAYER
			
			if (unitFrame.ShowUIParentAnimation) then
				unitFrame.ShowUIParentAnimation:Play()
			end
			
			unitFrame.nameplateScaleAdjust = 1
			
			if (DB_USE_UIPARENT) then
				plateFrame:HookScript("OnSizeChanged", Plater.UpdateUIParentScale)
				Plater.UpdateUIParentScale(plateFrame)
			else
				unitFrame:SetScale (1) --reset scale
			end
			
			--check if the hide hook is registered on this Blizzard nameplate
			if (not unitFrame.HasHideHookRegistered) then
				--onHide for unitFrame
				plateFrame.unitFrame:HookScript ("OnHide", unitFrame.OnHideWidget)
				--onShow for castbar
				castBar:SetHook ("OnShow", Plater.CastBarOnShow_Hook)
			
				unitFrame.HasHideHookRegistered = true
			end
			
			--powerbar are disabled by default in the settings table, called SetUnit will make the framework hide the power bar
			--SetPowerBarSize() will show the power bar or the personal resource bar update also will show it
			
			--ensure castBar is enabled properly when switching actorType or unit (with unit changing, it will be properly enabled)
			local castBarWasEnabled = (unitFrame.Settings.ShowCastBar and (plateFrame.PreviousUnitType == actorType)) or (unitFrame.unit ~= unitID)
			unitFrame.Settings.ShowCastBar = true -- reset to default, clearing later.
		
			--set the unit
			unitFrame:SetUnit (unitID)
			
			--reset performance unit
			unitFrame.isPerformanceUnit = nil
			unitFrame.healthBar.isPerformanceUnit = nil
			
			if (Plater.PerformanceUnits[plateFrame[MEMBER_NPCID]]) then
				--print("perf", plateFrame[MEMBER_NPCID])
				unitFrame.castBar:SetUnit(nil) -- no casts
				Plater.RemoveFromAuraUpdate (unitID) -- no auras
				unitFrame.isPerformanceUnit = true
				unitFrame.healthBar.isPerformanceUnit = true
			end
			
			--show unit name, the frame work will hide it due to ShowUnitName is set to false
			unitFrame.unitName:Show()
			
			--set the unitID in the unitFrame, several script and external addons read this member, adding different variations to be compatible with all
			unitFrame.unit = unitID
			unitFrame.namePlateUnitToken = unitID
			unitFrame.displayedUnit = unitID
			unitFrame.DenyColorChange = nil
			
			--was causing taints because MEMBER_UNITID is an actually member from the default blizzard nameplate
			--so when this nameplate is reclycled to be in a proteecteed nameplate, it was causing taints
			--plateFrame [MEMBER_UNITID] = unitID --causing taints
			
			plateFrame.QuestAmountCurrent = nil
			plateFrame.QuestAmountTotal = nil
			plateFrame.QuestText = nil
			plateFrame.QuestName = nil
			plateFrame.QuestIsCampaign = nil
			unitFrame.QuestAmountCurrent = nil
			unitFrame.QuestAmountTotal = nil
			unitFrame.QuestText = nil
			unitFrame.QuestName = nil
			unitFrame.QuestIsCampaign = nil
			
			--cache the unit target id, so it doesnt need to waste cycles building up on aggro checks
			unitFrame.targetUnitID = unitID .. "target"

			--clear values
			plateFrame.CurrentUnitNameString = plateFrame.unitName
			
			plateFrame.isSelf = nil
			plateFrame.IsSelf = nil
			unitFrame.IsSelf = nil --value exposed to scripts
			castBar.IsSelf = nil --value exposed to scripts

			unitFrame.unitName.isRenamed = nil
			
			plateFrame.PlayerCannotAttack = nil
			plateFrame.playerGuildName = nil
			plateFrame [MEMBER_NOCOMBAT] = nil
			
			plateFrame [MEMBER_TARGET] = nil
			unitFrame [MEMBER_TARGET] = nil
			
			--reset custom size set by the user
			unitFrame.customHealthBarWidth = nil
			unitFrame.customHealthBarHeight = nil
			
			unitFrame.customCastBarWidth = nil
			unitFrame.customCastBarHeight = nil
			
			unitFrame.customPowerBarWidth = nil
			unitFrame.customPowerBarHeight = nil
			
			--reset border
			unitFrame.customBorderColor = nil
			Plater.UpdateBorderColor (unitFrame)

			--reset custom color flag
			unitFrame.UsingCustomColor = nil
			
			unitFrame.InExecuteRange = false
			
			unitFrame.IsInRange = nil
			
			--check if this nameplate has an update scheduled and cancel it in any case
			if (plateFrame.HasUpdateScheduled) then
				if (not plateFrame.HasUpdateScheduled._cancelled) then
					plateFrame.HasUpdateScheduled:Cancel()
				end
				plateFrame.HasUpdateScheduled = nil
			end
			
			if requiresScheduledUpdate then --this COULD counter some rare issues with feign death instant cancel where nameplate hide/show is weird
				Plater.ScheduleUpdateForNameplate (plateFrame, unitID, 0.5) --half second enough maybe
			end
			
			--cache values
			local unitName = UnitName (unitID) or ""
			local unitNameTranslit = unitName
			if DB_USE_NAME_TRANSLIT then
				unitNameTranslit = LibTranslit:Transliterate(unitName, TRANSLIT_MARK)
			end
			plateFrame [MEMBER_NAME] = unitNameTranslit
			plateFrame [MEMBER_NAMELOWER] = lower (plateFrame [MEMBER_NAME])
			plateFrame ["namePlateClassification"] = UnitClassification (unitID)
			plateFrame.unitNameInternal = unitName
			
			--clear name schedules
			unitFrame.ScheduleNameUpdate = nil
			
			unitFrame.InCombat = UnitAffectingCombat (unitID) or (Plater.ForceInCombatUnits[unitFrame [MEMBER_NPCID]] and PLAYER_IN_COMBAT) or false
			
			--cache values into the unitFrame as well to reduce the overhead on scripts and hooks
			unitFrame [MEMBER_NAME] = plateFrame [MEMBER_NAME]
			unitFrame [MEMBER_NAMELOWER] = plateFrame [MEMBER_NAMELOWER]
			unitFrame ["namePlateClassification"] = plateFrame ["namePlateClassification"]
			unitFrame.unitNameInternal = unitName
			unitFrame [MEMBER_UNITID] = unitID
			unitFrame.namePlateThreatPercent = 0
			unitFrame.namePlateThreatIsTanking = nil
			unitFrame.namePlateThreatStatus = nil
			unitFrame.namePlateThreatOffTankIsTanking = false
			unitFrame.namePlateThreatOffTankName = nil
			
			plateFrame [MEMBER_REACTION] = reaction
			unitFrame [MEMBER_REACTION] = reaction
			unitFrame.BuffFrame [MEMBER_REACTION] = reaction
			unitFrame.BuffFrame2 [MEMBER_REACTION] = reaction
			unitFrame.BuffFrame.unit = unitID
			unitFrame.BuffFrame2.unit = unitID
			unitFrame.ExtraIconFrame.unit = unitID
			
			plateFrame.isBattlePet = isBattlePet
			unitFrame.isBattlePet = isBattlePet
			
			plateFrame.isWidgetOnlyMode = isWidgetOnlyMode
			unitFrame.isWidgetOnlyMode = isWidgetOnlyMode
			
			plateFrame.isPlayer = isPlayer
			unitFrame.isPlayer = isPlayer
			
			--clear the custom indicators table
			wipe (unitFrame.CustomIndicators)
			
			--health amount
			Plater.QuickHealthUpdate (unitFrame)
			healthBar.IsAnimating = false
			
			--hide execute indicators
			healthBar.healthCutOff:Hide()
			healthBar.executeRange:Hide()
			healthBar.ExecuteGlowUp:Hide()
			healthBar.ExecuteGlowDown:Hide()
			
			--reset color values
			healthBar.R, healthBar.G, healthBar.B, healthBar.A = nil, nil, nil, nil
			
			--reset the frame level and strata if using UIParent as the parent of the unitFrame
			--the function checks if the option is enabled, no need to check here
			Plater.UpdateUIParentLevels (unitFrame)
			
			if (unitFrame.unit) then
				
				if (isSelf) then
					--> personal health bar
					plateFrame.isSelf = true
					plateFrame.IsSelf = true
					unitFrame.IsSelf = true --this is the value exposed to scripts
					castBar.IsSelf = true --this is the value exposed to scripts
					plateFrame.NameAnchor = 0
					plateFrame.PlayerCannotAttack = true
					unitFrame.PlayerCannotAttack = true
					
					--do not allow the framework to show the unit name
					unitFrame.Settings.ShowUnitName = false
					unitFrame.unitName:Hide()
					
					--setup castbar
					unitFrame.Settings.ShowCastBar = DB_PLATE_CONFIG.player.castbar_enabled
					if (not DB_PLATE_CONFIG.player.castbar_enabled) then
						--CastingBarFrame_SetUnit (castBar, nil, nil, nil)
						unitFrame.castBar:SetUnit(nil, nil)
					elseif not castBarWasEnabled then
						unitFrame.castBar:SetUnit (unitID, unitID)
					end
					
					plateFrame.PlateConfig = DB_PLATE_CONFIG.player
					Plater.UpdatePlateFrame (plateFrame, ACTORTYPE_PLAYER, nil, true)
					Plater.OnUpdateHealth (healthBar)
					
				else
					--> regular nameplate
					
					plateFrame.PlayerCannotAttack = not UnitCanAttack ("player", unitID)
					unitFrame.PlayerCannotAttack = plateFrame.PlayerCannotAttack --expose to scripts
					
					if (isPlayer) then
						--unit is a player
						plateFrame.playerGuildName = GetGuildInfo (unitID)
						
						if (reaction >= Plater.UnitReaction.UNITREACTION_FRIENDLY) then
							plateFrame.NameAnchor = DB_NAME_PLAYERFRIENDLY_ANCHOR
							plateFrame.PlateConfig = DB_PLATE_CONFIG.friendlyplayer
							Plater.UpdatePlateFrame (plateFrame, ACTORTYPE_FRIENDLY_PLAYER, nil, true)
							unitFrame.Settings.ShowCastBar = not DB_CASTBAR_HIDE_FRIENDLY
							if (DB_CASTBAR_HIDE_FRIENDLY) then
								--CastingBarFrame_SetUnit (castBar, nil, nil, nil)
								unitFrame.castBar:SetUnit(nil, nil)
							elseif not castBarWasEnabled then
								unitFrame.castBar:SetUnit (unitID, unitID)
							end
						else
							plateFrame.NameAnchor = DB_NAME_PLAYERENEMY_ANCHOR
							plateFrame.PlateConfig = DB_PLATE_CONFIG.enemyplayer
							Plater.UpdatePlateFrame (plateFrame, ACTORTYPE_ENEMY_PLAYER, nil, true)
							unitFrame.Settings.ShowCastBar = not DB_CASTBAR_HIDE_ENEMIES
							if (DB_CASTBAR_HIDE_ENEMIES) then
								--CastingBarFrame_SetUnit (castBar, nil, nil, nil)
								unitFrame.castBar:SetUnit(nil, nil)
							elseif not castBarWasEnabled then
								unitFrame.castBar:SetUnit (unitID, unitID)
							end
						end
					else
						--the unit is a npc
						 
						if (reaction >= Plater.UnitReaction.UNITREACTION_FRIENDLY) then
							plateFrame.NameAnchor = DB_NAME_NPCFRIENDLY_ANCHOR
							plateFrame.PlateConfig = DB_PLATE_CONFIG.friendlynpc
							Plater.UpdatePlateFrame (plateFrame, ACTORTYPE_FRIENDLY_NPC, nil, true)
							unitFrame.Settings.ShowCastBar = not DB_CASTBAR_HIDE_FRIENDLY
							if (DB_CASTBAR_HIDE_FRIENDLY) then
								--CastingBarFrame_SetUnit (castBar, nil, nil, nil)
								unitFrame.castBar:SetUnit(nil, nil)
							elseif not castBarWasEnabled then
								unitFrame.castBar:SetUnit (unitID, unitID)
							end
						elseif isBattlePet then
							plateFrame.NameAnchor = DB_NAME_NPCFRIENDLY_ANCHOR
							plateFrame.PlateConfig = DB_PLATE_CONFIG.friendlynpc
							Plater.UpdatePlateFrame (plateFrame, ACTORTYPE_FRIENDLY_NPC, nil, true)
							unitFrame.Settings.ShowCastBar = not DB_CASTBAR_HIDE_FRIENDLY
							if (DB_CASTBAR_HIDE_FRIENDLY) then
								--CastingBarFrame_SetUnit (castBar, nil, nil, nil)
								unitFrame.castBar:SetUnit(nil, nil)
							elseif not castBarWasEnabled then
								unitFrame.castBar:SetUnit (unitID, unitID)
							end
							
						else
							--includes neutral npcs
							
							--add the npc in the npcid cache
							if (Plater.ZoneInstanceType == "raid" or Plater.ZoneInstanceType == "party" or Plater.ZoneInstanceType == "scenario") then
								if (plateFrame[MEMBER_NPCID] and plateFrame[MEMBER_NAME] ~= UNKNOWN) then --UNKNOWN is the global string from blizzard
									--npcCacheInfo: [1] npc name [2] zone name [3] language
									local npcCacheInfo = DB_NPCIDS_CACHE[plateFrame[MEMBER_NPCID]]
									if (not npcCacheInfo) then
										DB_NPCIDS_CACHE[plateFrame[MEMBER_NPCID]] = {plateFrame.unitNameInternal, Plater.ZoneName or "UNKNOWN", Plater.Locale or "enUS"}
									else
										--the npc is already cached, check if the language is different
										if (npcCacheInfo[3] ~= Plater.Locale) then
											--the npc is cached but the language is different, update the name
											npcCacheInfo[1] = plateFrame[MEMBER_NAME]
											npcCacheInfo[2] = Plater.ZoneName or "UNKNOWN"
											npcCacheInfo[3] = Plater.Locale
										end
									end
								end
							end
							
							plateFrame.NameAnchor = DB_NAME_NPCENEMY_ANCHOR
							plateFrame.PlateConfig = DB_PLATE_CONFIG.enemynpc
							Plater.UpdatePlateFrame (plateFrame, ACTORTYPE_ENEMY_NPC, nil, true)
							unitFrame.Settings.ShowCastBar = not DB_CASTBAR_HIDE_ENEMIES
							if (DB_CASTBAR_HIDE_ENEMIES) then
								--CastingBarFrame_SetUnit (castBar, nil, nil, nil)
								unitFrame.castBar:SetUnit(nil, nil)
							elseif not castBarWasEnabled then
								unitFrame.castBar:SetUnit (unitID, unitID)
							end
							
							--get threat situation to expose it to scripts already in the nameplate added hook
							local isTanking, threatStatus, threatpct, threatrawpct, threatValue = UnitDetailedThreatSituation ("player", unitID)
							unitFrame.namePlateThreatIsTanking = isTanking
							unitFrame.namePlateThreatStatus = threatStatus
							unitFrame.namePlateThreatPercent = threatpct or 0
							unitFrame.namePlateThreatRawPercent = threatrawpct or 0
							unitFrame.namePlateThreatValue = threatValue or 0

							Plater.UpdateNameOnRenamedUnit(plateFrame)
						end
					end
				end
			end
			
			plateFrame.actorType = actorType
			unitFrame.actorType = actorType
			unitFrame.ActorType = actorType --exposed to scripts
			
			--sending true to force the color update when the color overrider is enabled
			Plater.FindAndSetNameplateColor (unitFrame, true)
			
			--icone da cast bar
			Plater.UpdateCastbarIcon(castBar)
			
			--esconde os glow de aggro
			unitFrame.aggroGlowUpper:Hide()
			unitFrame.aggroGlowLower:Hide()
			
			--widget container update
			if IS_WOW_PROJECT_MAINLINE then
				plateFrame.unitFrame.WidgetContainer = plateFrame.UnitFrame.WidgetContainer
				if plateFrame.unitFrame.WidgetContainer then
					plateFrame.unitFrame.WidgetContainer:SetParent(plateFrame.unitFrame)
					plateFrame.unitFrame.WidgetContainer:ClearAllPoints()
					plateFrame.unitFrame.WidgetContainer:SetIgnoreParentScale(true)
					plateFrame.unitFrame.WidgetContainer:SetScale(Plater.db.profile.widget_bar_scale)
					Plater.SetAnchor (plateFrame.unitFrame.WidgetContainer, Plater.db.profile.widget_bar_anchor, plateFrame.unitFrame)
				end
			end
			
			--can check aggro
			unitFrame.CanCheckAggro = unitFrame.displayedUnit == unitID and actorType == ACTORTYPE_ENEMY_NPC and not unitFrame.isPerformanceUnit
			
			--tick-setup
			plateFrame.OnTickFrame.ThrottleUpdate = DB_TICK_THROTTLE
			plateFrame.OnTickFrame.actorType = actorType
			plateFrame.OnTickFrame.unit = unitID
			plateFrame.OnTickFrame:SetScript ("OnUpdate", Plater.NameplateTick)

			--highlight check
			if (DB_HOVER_HIGHLIGHT and (not plateFrame.PlayerCannotAttack or (plateFrame.PlayerCannotAttack and DB_SHOW_HEALTHBARS_FOR_NOT_ATTACKABLE)) and (actorType == ACTORTYPE_ENEMY_PLAYER or actorType == ACTORTYPE_ENEMY_NPC)) then
				Plater.EnableHighlight (unitFrame)
			else
				Plater.DisableHighlight (unitFrame)
			end
			
			--range
			--Plater.CheckRange (plateFrame, true)
			
			--resources - TODO:
			Plater.Resources.UpdateResourceFramePosition() --~resource

			--hooks
			if (HOOK_NAMEPLATE_ADDED.ScriptAmount > 0) then
				for i = 1, HOOK_NAMEPLATE_ADDED.ScriptAmount do
					local globalScriptObject = HOOK_NAMEPLATE_ADDED [i]
					local scriptContainer = unitFrame:ScriptGetContainer()
					local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Nameplate Added")
					--run
					unitFrame:ScriptRunHook (scriptInfo, "Nameplate Added")
				end
			end
			
			--tick
			Plater.NameplateTick (plateFrame.OnTickFrame, 999)
			
			unitFrame.PlaterOnScreen = true
			
			-- add private aura anchors
			Plater.HandlePrivateAuraAnchors(plateFrame.unitFrame) -- requires namePlateUnitToken, PlaterOnScreen and IsSelf to be set

			--check if the cast bar test is enabled
			if (Plater.IsShowingCastBarTest) then
				--start a castbar test for this unit
				platerInternal.CastBar.StartTestCastBarForNameplate(plateFrame)
			end
		end,

		-- ~removed
		---@param event string
		---@param unitBarId string
		NAME_PLATE_UNIT_REMOVED = function (event, unitBarId)
			--ViragDevTool_AddData({ctime = GetTime(), unit = unitBarId or "nil", stack = debugstack()}, "NAME_PLATE_UNIT_REMOVED - " .. (unitBarId or "nil"))
			---@type plateframe
			local plateFrame = C_NamePlate.GetNamePlateForUnit (unitBarId)
			
			Plater.RemoveFromAuraUpdate (unitBarId) -- ensure no updates
			
			ENABLED_BLIZZARD_PLATEFRAMES[plateFrame.unitFrame.blizzardPlateFrameID] = true -- OnRetailNamePlateShow is called first. ensure the plate might show!
			if not plateFrame.unitFrame.PlaterOnScreen then
				return
			end
			
			NAMEPLATES_ON_SCREEN_CACHE[unitBarId] = false
			NUM_NAMEPLATES_ON_SCREEN = NUM_NAMEPLATES_ON_SCREEN - 1
			
			--check if this nameplate has an update scheduled
			if (plateFrame.HasUpdateScheduled) then
				if (not plateFrame.HasUpdateScheduled._cancelled) then
					plateFrame.HasUpdateScheduled:Cancel()
				end
				plateFrame.HasUpdateScheduled = nil
			end
			
			--debug for hunter faith death
			--if (select (2, UnitClass (unitBarId)) == "HUNTER") then
			--	print ("nameplate removed", UnitName (unitBarId))
			--end
			
			--hooks
			if (HOOK_NAMEPLATE_REMOVED.ScriptAmount > 0) then
				for i = 1, HOOK_NAMEPLATE_REMOVED.ScriptAmount do
					local globalScriptObject = HOOK_NAMEPLATE_REMOVED [i]
					local unitFrame = plateFrame.unitFrame
					local scriptContainer = unitFrame:ScriptGetContainer()
					local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Nameplate Removed")
					--run
					plateFrame.unitFrame:ScriptRunHook (scriptInfo, "Nameplate Removed")
				end
			end
			
			plateFrame.OnTickFrame:SetScript ("OnUpdate", nil)
			plateFrame.unitFrame.HighlightFrame:SetScript ("OnUpdate", nil)
			
			plateFrame [MEMBER_QUEST] = false
			plateFrame.unitFrame [MEMBER_QUEST] = false
			plateFrame.QuestInfo = {}
			plateFrame.unitFrame.QuestInfo = {}
			plateFrame [MEMBER_TARGET] = nil
			
			plateFrame.isObject = nil
			plateFrame.unitFrame.isObject = nil
			plateFrame.isSoftInteract = nil
			plateFrame.unitFrame.isSoftInteract = nil
			plateFrame.isSoftInteractObject = nil
			plateFrame.unitFrame.isSoftInteractObject = nil
			
			local healthBar = plateFrame.unitFrame.healthBar
			if (healthBar.TargetHeight) then
				healthBar:SetHeight (healthBar.TargetHeight)
			end
			healthBar.IsIncreasingHeight = nil
			healthBar.IsDecreasingHeight = nil
			
			--hide the highlight
			--is mouse over ~highlight ~mouseover
			plateFrame.unitFrame.HighlightFrame:Hide()
			plateFrame.unitFrame.HighlightFrame.Shown = false
			
			--hide target highlight
			plateFrame.TargetNeonUp:Hide()
			plateFrame.TargetNeonDown:Hide()
			
			--hide threat highlight
			plateFrame.unitFrame.aggroGlowUpper:Hide()
			plateFrame.unitFrame.aggroGlowLower:Hide()
			
			--> check if is running any script
			plateFrame.unitFrame:OnHideWidget()
			plateFrame.unitFrame.castBar:OnHideWidget()
			for _, auraIconFrame in ipairs (plateFrame.unitFrame.BuffFrame.PlaterBuffList) do
				auraIconFrame:OnHideWidget()
			end
			for _, auraIconFrame in ipairs (plateFrame.unitFrame.BuffFrame2.PlaterBuffList) do
				auraIconFrame:OnHideWidget()
			end
			
			--stop animations
			if plateFrame.unitFrame.BodyFlashFrame and plateFrame.unitFrame.BodyFlashFrame.animation then
				plateFrame.unitFrame.BodyFlashFrame.animation:Stop()
				plateFrame.unitFrame.BodyFlashFrame:Hide()
			end
			if plateFrame.unitFrame.healthBar.HealthFlashFrame and plateFrame.unitFrame.healthBar.HealthFlashFrame.animation then
				plateFrame.unitFrame.healthBar.HealthFlashFrame.animation:Stop()
				plateFrame.unitFrame.healthBar.HealthFlashFrame:Hide()
			end
			
			plateFrame.unitFrame.PlaterOnScreen = nil
			
			--reset auras
			Plater.ResetAuraContainer (plateFrame.unitFrame.BuffFrame, true, true)
			Plater.HideNonUsedAuraIcons (plateFrame.unitFrame.BuffFrame)
			
			--remove private aura anchors
			Plater.HandlePrivateAuraAnchors(plateFrame.unitFrame)
			
			--tell the framework to execute a cleanup on the unit frame, this is required since Plater set .ClearUnitOnHide to false
			plateFrame.unitFrame:SetUnit (nil)
			
			-- remove widgets
			if IS_WOW_PROJECT_MAINLINE and plateFrame.unitFrame.WidgetContainer then
				plateFrame.unitFrame.WidgetContainer:SetIgnoreParentScale(false)
				plateFrame.unitFrame.WidgetContainer:SetParent(plateFrame)
				plateFrame.unitFrame.WidgetContainer:ClearAllPoints()
				plateFrame.unitFrame.WidgetContainer:SetPoint('TOP', plateFrame.castBar, 'BOTTOM')
			end
			
			--if plateFrame.UnitFrame and plateFrame.UnitFrame.HealthBarsContainerOrigParent then
			--	DevTool:AddData("removing")
			--	plateFrame.UnitFrame.HealthBarsContainer:SetParent(plateFrame.UnitFrame.HealthBarsContainerOrigParent)
			--	plateFrame.UnitFrame.HealthBarsContainerOrigParent = nil
			--end
			
			--community patch by Ariani#0960 (discord)
			--make the unitFrame be parented to UIParent allowing frames to be moved between strata levels
			--March 3rd, 2019
			if (DB_USE_UIPARENT) then
				-- need to explicitly hide the frame now, as it is not tethered to the blizz nameplate
				plateFrame.unitFrame:Hide()
			end
			--end of patch
			
		end,
		
		UNIT_INVENTORY_CHANGED = function()
			UpdatePlayerTankState()
			--Plater.UpdateAllNameplateColors()
			--Plater.UpdateAllPlates()
		end,
		
		UPDATE_SHAPESHIFT_FORM = function()
			local curTime = GetTime()
			--this is to work around UPDATE_SHAPESHIFT_FORM firing for all units and not just the player... causing lag...
			if last_GetShapeshiftFormID == GetShapeshiftFormID() then
				return
			end
			last_GetShapeshiftFormID = GetShapeshiftFormID()
			
			UpdatePlayerTankState()
			Plater.UpdateAllNameplateColors()
			Plater.UpdateAllPlates()
			Plater.Resources.UpdateResourceFramePosition()
		end,
		
		TALENT_GROUP_ROLE_CHANGED = function()
			UpdatePlayerTankState()
			Plater.UpdateAllNameplateColors()
			Plater.UpdateAllPlates()
		end,
	}

	--allow other files of the addon to have access to event functions
	platerInternal.Events.GetEventFunction = function(event)
		return eventFunctions[event]
	end

	function Plater.EventHandler (_, event, ...) --private
		local func = eventFunctions [event]
		if (func) then
			Plater.StartLogPerformanceCore("Plater-Core", "Events", event)
			func (event, ...)
			Plater.EndLogPerformanceCore("Plater-Core", "Events", event)
		else
			Plater:Msg ("no registered function for event " .. (event or "unknown event"))
		end
	end

	Plater.EventHandlerFrame:SetScript ("OnEvent", Plater.EventHandler)
	Plater.EventHandlerFrame:RegisterEvent ("PLAYER_ENTERING_WORLD")
	
	function Plater.RunFunctionForEvent (event, ...) --private
		Plater.EventHandler (nil, event, ...)
	end
	
	local run_scheduled_event_function = function (timerObject)
		local event = timerObject.event
		local args = timerObject.args
		Plater.RunFunctionForEvent (event, unpack (args))
	end
	
	function Plater.ScheduleRunFunctionForEvent (delay, event, ...) --private
		local timer = C_Timer.NewTimer (delay, run_scheduled_event_function)
		timer.event = event
		timer.args = {...}
	end

	--function for plateFrame.UnitFrame OnShow script
	--it'll hide the retail nameplate when it shown
	function Plater.OnRetailNamePlateShow (self) --private
		if ENABLED_BLIZZARD_PLATEFRAMES[tostring(self)] then
			-- do not hide
			return
		end
		
		--self:Hide()
		
		if self:IsProtected() then
			self:ClearAllPoints()
			self:SetParent(nil)
			
			if self.HealthBarsContainer then
				self.HealthBarsContainerOrigParent = self.HealthBarsContainer:GetParent() or self.HealthBarsContainerOrigParent
				self.HealthBarsContainer:ClearAllPoints()
				--self.HealthBarsContainer:SetParent(nil)
			end
			
			--for _, f in pairs(self:GetChildren() or {}) do
			--	--DevTool:AddData(f, "child")
			--	if type(f) == "table" and f.IsProtected then
			--		local p, ep = f:IsProtected()
			--		--DevTool:AddData({p, ep, f}, "protected?")
			--		if ep then
			--			--DevTool:AddData(f, "protected!")
			--			f:ClearAllPoints()
			--			f:SetParent(nil)
			--			f:Hide()
			--		end
			--	end
			--end
			if not self:IsProtected() then
				self:Hide()
			elseif DevTool then
				DevTool:AddData(self, "protected nameplate...")
			end
		else
			self:Hide()
		end
		
		
		if not SUPPORT_BLIZZARD_PLATEFRAMES then
			-- should be done if events are not needed
			-- CompactUnitFrame_UnregisterEvents only removes event hanlder functions
			self:UnregisterAllEvents()
		end
		
		if (CompactUnitFrame_UnregisterEvents) then
			CompactUnitFrame_UnregisterEvents (self)
		end
	end
	
	function Plater.SetFontOutlineAndShadow (fontString, outline, shadowColor, shadowXOffSet, shadowYOffSet)
		--update the outline
		DF:SetFontOutline (fontString, outline)
		
		--update shadow color and shadow offset
		if (shadowColor) then
			local r, g, b, a = DF:ParseColors (shadowColor)
			DF:SetFontShadow (fontString, r, g, b, a, shadowXOffSet, shadowYOffSet)
		end
	end
	
	---import scripts from the library database, after that apply patches and recompile scripts
	function platerInternal.Scripts.UpdateFromLibrary()
		Plater.ImportScriptsFromLibrary()
		Plater.ApplyPatches()
		--and compile all scripts and hooks
		Plater.CompileAllScripts("script")
		Plater.CompileAllScripts("hook")
	end

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> addon initialization

function Plater.InitializeSavedVariables()
	local PlaterDB = _G.PlaterDB

	--table to store casts with SPELL_CAST_START
	PlaterDB.captured_casts = PlaterDB.captured_casts or {}
	--table to store auras and any spell cast
	PlaterDB.captured_spells = PlaterDB.captured_spells or {}

	--table to store npcIds of performance units
	PlaterDB.performance_units = PlaterDB.performance_units or {}
end

function Plater.OnInit() --private --~oninit ~init
	LibStub ("AceDBOptions-3.0"):GetOptionsTable (Plater.db, true) -- register this now, to ensure no default "realm", "char - realm" profiles are shown in profiles management
	
	do
		local languageCurrentVersion = 1
		if (not PlaterLanguage) then
			PlaterLanguage = {
				language = GetLocale(), 
				version = languageCurrentVersion,
			}
		end

		if (PlaterLanguage.version < languageCurrentVersion) then
			--do stuff in the future
		end

		DF.Language.SetCurrentLanguage(addonId, PlaterLanguage.language)
	end

	--PlaterBackup is a table to store data that has been removed by the player might want to restore in another time
	PlaterBackup = PlaterBackup or {}

	Plater.InitializeSavedVariables()
	Plater.RefreshDBUpvalues()

	C_Timer.After(0, function()
		platerInternal.CreatePerformanceUnits(Plater)
	end)
	
	Plater.UpdateBlizzardNameplateFonts()
	
	-- do we need to support blizzard frames?
	SUPPORT_BLIZZARD_PLATEFRAMES = (not DB_PLATE_CONFIG [ACTORTYPE_PLAYER].module_enabled) or (not DB_PLATE_CONFIG [ACTORTYPE_FRIENDLY_PLAYER].module_enabled) or (not DB_PLATE_CONFIG [ACTORTYPE_ENEMY_PLAYER].module_enabled) or (not DB_PLATE_CONFIG [ACTORTYPE_FRIENDLY_NPC].module_enabled) or (not DB_PLATE_CONFIG [ACTORTYPE_ENEMY_NPC].module_enabled)
	
	Plater.CombatTime = GetTime()

	PLAYER_IN_COMBAT = false
	if (InCombatLockdown()) then
		PLAYER_IN_COMBAT = true
	end

	Plater.Locale =  GetLocale()

	do --log initialization version
		pcall(function()
			local GetAddOnMetadata = C_AddOns and C_AddOns.GetAddOnMetadata or GetAddOnMetadata
			local platerVersion = GetAddOnMetadata("Plater", "Version")
			local frameworkVersion = "Framework v" .. select(2,LibStub:GetLibrary("DetailsFramework-1.0"))
			local gameVersion = GetBuildInfo()
			local gameLocale = Plater.Locale
			local charName = UnitName("player")
			platerInternal.Logs.Log("INIT | " .. platerVersion .. " | " .. frameworkVersion .. " | " .. gameVersion .. " | " .. gameLocale .. " | " .. charName)
		end)
	end

	PlaterDB.InterruptableSpells = PlaterDB.InterruptableSpells or {}

	--check if details is loaded and if the version has support for mythic+ overall event
	if (Details and Details.RegistredEvents["COMBAT_MYTHICPLUS_OVERALL_READY"]) then
		platerInternal.DetailsEvents = Details:CreateEventListener()
		platerInternal.DetailsEvents:RegisterEvent("COMBAT_MYTHICPLUS_OVERALL_READY", function()
			local interruptableSpells = {}
			local combatObject = Details:GetCurrentCombat()
			if (combatObject:GetCombatType() == DETAILS_SEGMENTTYPE_MYTHICDUNGEON_OVERALL) then
				local utilityContainer = combatObject:GetContainer(DETAILS_ATTRIBUTE_MISC)
				for index, actorObject in utilityContainer:ListActors() do
					local interrupttedSpells = actorObject:GetSpellContainer("interruptwhat")
					if (interrupttedSpells) then
						for spellId in pairs(interrupttedSpells) do
							PlaterDB.InterruptableSpells[spellId] = true --~interrupt ~interruptable
						end
					end
				end
			end
		end)
	end

	--Plater:BossModsLink()
	
	--character settings
		PlaterDBChr = PlaterDBChr or {first_run3 = {}}
		PlaterDBChr.first_run3 = PlaterDBChr.first_run3 or {}
		PlaterDBChr.debuffsBanned = PlaterDBChr.debuffsBanned or {}
		PlaterDBChr.buffsBanned = PlaterDBChr.buffsBanned or {}
		PlaterDBChr.spellRangeCheckRangeEnemy = PlaterDBChr.spellRangeCheckRangeEnemy or {}
		PlaterDBChr.spellRangeCheckRangeFriendly = PlaterDBChr.spellRangeCheckRangeFriendly or {}

	--Register LDB
	Plater.InitLDB()

	--to fix: attempt to index field 'spellRangeCheck' (a string value)
		if (type (PlaterDBChr.spellRangeCheckRangeEnemy) ~= "table") then
			PlaterDBChr.spellRangeCheckRangeEnemy = {}
		end
		if (type (PlaterDBChr.spellRangeCheckRangeFriendly) ~= "table") then
			PlaterDBChr.spellRangeCheckRangeFriendly = {}
		end
	
	--run once per profile per expansion
	platerInternal.ScriptTriggers.WipeDeprecatedScriptTriggersFromProfile(Plater.db.profile)

	--ensure global nameplate width/height setting is initialized
		Plater.db.profile.plate_config.global_health_width = Plater.db.profile.plate_config.global_health_width or Plater.db.profile.plate_config.enemynpc.health[1]
		Plater.db.profile.plate_config.global_health_height = Plater.db.profile.plate_config.global_health_height or Plater.db.profile.plate_config.enemynpc.health[2]
	
	--range check spells
		if IS_WOW_PROJECT_MAINLINE then
			for specID, _ in pairs (Plater.SpecList [select (2, UnitClass ("player"))]) do
				--if (PlaterDBChr.spellRangeCheckRangeEnemy [specID] == nil or not LibRangeCheck:GetHarmMaxChecker (PlaterDBChr.spellRangeCheckRangeEnemy [specID])) then
				if (PlaterDBChr.spellRangeCheckRangeEnemy [specID] == nil) then
					PlaterDBChr.spellRangeCheckRangeEnemy [specID] = Plater.DefaultSpellRangeList [specID]
				end
				--if (PlaterDBChr.spellRangeCheckRangeFriendly [specID] == nil or not LibRangeCheck:GetFriendMaxChecker(PlaterDBChr.spellRangeCheckRangeFriendly [specID])) then
				if (PlaterDBChr.spellRangeCheckRangeFriendly [specID] == nil) then
					PlaterDBChr.spellRangeCheckRangeFriendly [specID] = Plater.DefaultSpellRangeListF [specID]
				end
			end
		else
			local playerClass = select (3, UnitClass ("player"))
			--if (PlaterDBChr.spellRangeCheckRangeEnemy [playerClass] == nil or not LibRangeCheck:GetHarmMaxChecker (PlaterDBChr.spellRangeCheckRangeEnemy [playerClass])) then
			if (PlaterDBChr.spellRangeCheckRangeEnemy [playerClass] == nil) then
				PlaterDBChr.spellRangeCheckRangeEnemy [playerClass] = Plater.DefaultSpellRangeList [playerClass]
			end
			--if (PlaterDBChr.spellRangeCheckRangeFriendly [playerClass] == nil or not LibRangeCheck:GetFriendMaxChecker(PlaterDBChr.spellRangeCheckRangeFriendly [playerClass])) then
			if (PlaterDBChr.spellRangeCheckRangeFriendly [playerClass] == nil) then
				PlaterDBChr.spellRangeCheckRangeFriendly [playerClass] = Plater.DefaultSpellRangeListF [playerClass]
			end
		end
		Plater.RangeCheckRangeEnemy = nil
		Plater.RangeCheckRangeFriendly = nil
		Plater.RangeCheckFunctionEnemy = nil
		Plater.RangeCheckFunctionFriendly = nil
		
		LibRangeCheck.RegisterCallback(Plater, LibRangeCheck.CHECKERS_CHANGED, function() Plater.GetSpellForRangeCheck() end)
	
	--who is the player
		Plater.PlayerGUID = UnitGUID ("player")
		Plater.PlayerClass = select (2, UnitClass ("player"))
	
	--track player auras
		Plater.AddToAuraUpdate("player")

	--load scripts from the script library
		platerInternal.Scripts.UpdateFromLibrary()
	
	--check if masque is installed and add support for masque addon
		local Masque = LibStub ("Masque", true)
		if (Masque and Plater.db.profile.enable_masque_support) then
			Plater.Masque = {}
			Plater.Masque.Callback = function(group, option, value)
				group:ReSkin(true)
			end
			Plater.Masque.AuraFrame1 = Masque:Group ("Plater Nameplates", "Aura Frame 1")
			Plater.Masque.AuraFrame1:RegisterCallback(Plater.Masque.Callback)
			Plater.Masque.AuraFrame2 = Masque:Group ("Plater Nameplates", "Aura Frame 2")
			Plater.Masque.AuraFrame2:RegisterCallback(Plater.Masque.Callback)
			Plater.Masque.BuffSpecial = Masque:Group ("Plater Nameplates", "Buff Special")
			Plater.Masque.BuffSpecial:RegisterCallback(Plater.Masque.Callback)
			Plater.Masque.BossModIconFrame = Masque:Group ("Plater Nameplates", "Boss Mod Icons")
			Plater.Masque.BossModIconFrame:RegisterCallback(Plater.Masque.Callback)
			Plater.Masque.CastIcon = Masque:Group ("Plater Nameplates", "Cast Bar Icons")
			Plater.Masque.CastIcon:RegisterCallback(Plater.Masque.Callback)
		end
	
	--set some cvars that we want to set
		function Plater.ForceCVars()
			if (InCombatLockdown()) then
				return C_Timer.After (1, function() Plater.ForceCVars() end)
			end
			SetCVar ("nameplateMinAlpha", 0.90135484)
			SetCVar ("nameplateMinAlphaDistance", -10^5.2)
			SetCVar ("nameplateSelectedAlpha", 1)
			SetCVar ("nameplateNotSelectedAlpha", 1)
			SetCVar ("nameplateRemovalAnimation", DB_USE_QUICK_HIDE and 0 or 1)
			SetCVar ("nameplateShowFriendlyBuffs", 0)
			SetCVar ("nameplateShowPersonalCooldowns", 0)
			if IS_WOW_PROJECT_MAINLINE and not GetCVar("nameplatePlayerMaxDistance") then -- this is 10.1 workaround.
				SetCVar ("nameplatePlayerMaxDistance", 60)
			end
		end
	
	--schedule data update
		--C_Timer.After (1, Plater.GetSpellForRangeCheck)
		C_Timer.After (4, Plater.GetHealthCutoffValue)
	
	--Mythic Dungeon Tools
		platerInternal.InstallMDTHooks()

	--hooking scripts has load conditions, here it creates a load filter for plater
	--so when a load condition is changed it reload hooks
		function Plater.HookLoadCallback (encounterID) --private
			Plater.StartLogPerformanceCore("Plater-Core", "Mod/Script", "HookLoadCallback")
			
			Plater.EncounterID = encounterID
			Plater.WipeAndRecompileAllScripts ("hook", true) --sending true to not dispatch a hotReload in the scripts
			
			Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "HookLoadCallback")
		end
		DF:CreateLoadFilterParser (Plater.HookLoadCallback)
	
	--refresh the color overrider
		Plater.RefreshColorOverride()
	
	--update the current zone
		local _, zoneType = GetInstanceInfo()
		Plater.ZoneInstanceType = zoneType
	
	--check if is the first time Plater is running in the account or in the character
		local check_first_run = function()
			if (not UnitGUID ("player")) then
				C_Timer.After (1, Plater.CheckFirstRun)
				return
			end
			
			if (not Plater.db.profile.first_run3) then
				C_Timer.After (15, Plater.SetCVarsOnFirstRun)
				
				--enable UIParent nameplates for new installs of Plater
				--this setting is disabled by default and will be enabled for new people
				Plater.db.profile.use_ui_parent = true
				--adjust the fine tune to player's screen scale
				Plater.db.profile.ui_parent_scale_tune = 1 / UIParent:GetEffectiveScale()
				
			elseif (not PlaterDBChr.first_run3 [UnitGUID ("player")]) then
				--do not run cvars for individual characters
				C_Timer.After (15, Plater.SetCVarsOnFirstRun)
			else
				Plater.CreatePlaterButtonAtInterfaceOptions()
			end
		end
		
		function Plater.CheckFirstRun() --private
			check_first_run()
		end
		Plater.CheckFirstRun()
		
	--load a table with a copy of the plateConfigs table to be accessed by scripts
		Plater.UpdateSettingsCache()
	
	--events
		Plater.EventHandlerFrame:RegisterEvent ("NAME_PLATE_CREATED")
		Plater.EventHandlerFrame:RegisterEvent ("NAME_PLATE_UNIT_ADDED")
		Plater.EventHandlerFrame:RegisterEvent ("FORBIDDEN_NAME_PLATE_UNIT_ADDED")
		Plater.EventHandlerFrame:RegisterEvent ("NAME_PLATE_UNIT_REMOVED")
		
		Plater.EventHandlerFrame:RegisterEvent ("PLAYER_TARGET_CHANGED")
		Plater.EventHandlerFrame:RegisterEvent ("PLAYER_FOCUS_CHANGED")
		if IS_WOW_PROJECT_MAINLINE then
			Plater.EventHandlerFrame:RegisterEvent ("PLAYER_SOFT_INTERACT_CHANGED")
			Plater.EventHandlerFrame:RegisterEvent ("PLAYER_SOFT_FRIEND_CHANGED")
			Plater.EventHandlerFrame:RegisterEvent ("PLAYER_SOFT_ENEMY_CHANGED")
		end
		
		Plater.EventHandlerFrame:RegisterEvent ("PLAYER_REGEN_DISABLED")
		Plater.EventHandlerFrame:RegisterEvent ("PLAYER_REGEN_ENABLED")
		
		Plater.EventHandlerFrame:RegisterEvent ("ZONE_CHANGED_NEW_AREA")
		Plater.EventHandlerFrame:RegisterEvent ("ZONE_CHANGED_INDOORS")
		Plater.EventHandlerFrame:RegisterEvent ("ZONE_CHANGED")
		Plater.EventHandlerFrame:RegisterEvent ("FRIENDLIST_UPDATE")
		Plater.EventHandlerFrame:RegisterEvent ("PLAYER_LOGOUT")
		Plater.EventHandlerFrame:RegisterEvent ("PLAYER_UPDATE_RESTING")
		Plater.EventHandlerFrame:RegisterEvent ("RAID_TARGET_UPDATE")
		
		Plater.EventHandlerFrame:RegisterEvent ("QUEST_ACCEPTED")
		Plater.EventHandlerFrame:RegisterEvent ("QUEST_REMOVED")
		Plater.EventHandlerFrame:RegisterEvent ("QUEST_ACCEPT_CONFIRM")
		Plater.EventHandlerFrame:RegisterEvent ("QUEST_COMPLETE")
		if IS_WOW_PROJECT_MAINLINE then
			Plater.EventHandlerFrame:RegisterEvent ("QUEST_POI_UPDATE")
		end
		Plater.EventHandlerFrame:RegisterEvent ("QUEST_DETAIL")
		Plater.EventHandlerFrame:RegisterEvent ("QUEST_FINISHED")
		Plater.EventHandlerFrame:RegisterEvent ("QUEST_GREETING")
		Plater.EventHandlerFrame:RegisterEvent ("QUEST_LOG_UPDATE")
		Plater.EventHandlerFrame:RegisterEvent ("UNIT_QUEST_LOG_CHANGED")
		if IS_WOW_PROJECT_MAINLINE then
			Plater.EventHandlerFrame:RegisterEvent ("PLAYER_SPECIALIZATION_CHANGED")
			Plater.EventHandlerFrame:RegisterEvent (C_Traits and "TRAIT_CONFIG_UPDATED" or "PLAYER_TALENT_UPDATE")
		elseif IS_WOW_PROJECT_CLASSIC_WRATH then
			Plater.EventHandlerFrame:RegisterEvent ("ACTIVE_TALENT_GROUP_CHANGED")
			Plater.EventHandlerFrame:RegisterEvent ("PLAYER_TALENT_UPDATE")
		end
		
		Plater.EventHandlerFrame:RegisterEvent ("ENCOUNTER_START")
		Plater.EventHandlerFrame:RegisterEvent ("ENCOUNTER_END")
		if IS_WOW_PROJECT_MAINLINE then
			Plater.EventHandlerFrame:RegisterEvent ("CHALLENGE_MODE_START")
		end
		
		Plater.EventHandlerFrame:RegisterEvent ("UNIT_NAME_UPDATE")
		
		Plater.EventHandlerFrame:RegisterEvent ("UNIT_FLAGS")
		Plater.EventHandlerFrame:RegisterEvent ("UNIT_FACTION")
		
		Plater.EventHandlerFrame:RegisterEvent ("DISPLAY_SIZE_CHANGED")
		Plater.EventHandlerFrame:RegisterEvent ("UI_SCALE_CHANGED")
		
		Plater.EventHandlerFrame:RegisterEvent ("GROUP_ROSTER_UPDATE")
		
		Plater.EventHandlerFrame:RegisterEvent ("UNIT_PET")
		
		if IS_WOW_PROJECT_NOT_MAINLINE then -- tank spec detection
			Plater.EventHandlerFrame:RegisterEvent ("UNIT_INVENTORY_CHANGED")
			Plater.EventHandlerFrame:RegisterEvent ("UPDATE_SHAPESHIFT_FORM")
			if IS_WOW_PROJECT_CLASSIC_WRATH then
				Plater.EventHandlerFrame:RegisterEvent ("TALENT_GROUP_ROLE_CHANGED")
			end
		elseif Plater.PlayerClass == "DRUID" then
			Plater.EventHandlerFrame:RegisterEvent ("UPDATE_SHAPESHIFT_FORM")
		end
		
		Plater.EventHandlerFrame:RegisterEvent ("PLAYER_LOGIN")
		Plater.EventHandlerFrame:RegisterEvent ("VARIABLES_LOADED")

		--power update for hooking scripts
		local hookPowerEventFrame = CreateFrame ("frame")
		--hookPowerEventFrame:RegisterUnitEvent ("UNIT_POWER_UPDATE", "player")
		hookPowerEventFrame:RegisterUnitEvent ("UNIT_POWER_FREQUENT", "player")
		hookPowerEventFrame:RegisterUnitEvent ("UNIT_MAXPOWER", "player")
		--hookPowerEventFrame:RegisterUnitEvent ("UNIT_DISPLAYPOWER", "player")
		--hookPowerEventFrame:RegisterUnitEvent ("UNIT_POWER_BAR_HIDE", "player")

		hookPowerEventFrame:SetScript ("OnEvent", function(self, event, target, powerType)
			-- target is always 'player'
			if (HOOK_PLAYER_POWER_UPDATE.ScriptAmount > 0) then
				for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
					---@cast plateFrame plateframe
					if (plateFrame) then
						for i = 1, HOOK_PLAYER_POWER_UPDATE.ScriptAmount do
							local globalScriptObject = HOOK_PLAYER_POWER_UPDATE [i]
							local unitFrame = plateFrame.unitFrame
							local scriptContainer = unitFrame:ScriptGetContainer()
							local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Player Power Update")
							--run
							unitFrame:ScriptRunHook (scriptInfo, "Player Power Update", unitFrame, powerType)
						end
					end
				end
			end
		end)
	
		--this should pull the resources bar up and down based on if the target has debuffs shown on it or not
		function Plater.UpdateResourceFrameAnchor (buffFrame)
			if (Plater.CurrentTargetResourceFrame) then
				--if this buffFrame anchored in the current target?
				if (buffFrame.unitFrame [MEMBER_TARGET]) then
					--has any aura shown?
					--algumas vezes o amtdebbufs esta retornando 0 por alguma razao...
					if (buffFrame.amountAurasShown > 0 or (DB_AURA_SEPARATE_BUFFS and buffFrame.BuffFrame2.amountAurasShown > 0)) then
						Plater.CurrentTargetResourceFrame:SetPoint ("bottom", buffFrame.healthBar, "top", 0, Plater.db.profile.resources.y_offset_target + Plater.db.profile.resources.y_offset_target_withauras)
					else
						Plater.CurrentTargetResourceFrame:SetPoint ("bottom", buffFrame.healthBar, "top", 0, Plater.db.profile.resources.y_offset_target)
					end
				end
			end
		end
	
		--this function is declared inside 'NamePlateDriverMixin' at Blizzard_NamePlates.lua
		--self if the nameplate driver frame: _G.NamePlateDriverFrame
		--at the moment self isn't being used ~personal
		function Plater.UpdatePersonalBar (self)
			local showSelf = GetCVarBool ("nameplateShowSelf") and Plater.db.profile.plate_config.player.module_enabled
			if (not showSelf) then
				if PlaterDBChr.resources_on_target then
					Plater.UpdateResourceFrame()
				end
				return
			end

			--show Plater power bar for the player personal nameplate
			---@type plateframe
			local plateFrame = C_NamePlate.GetNamePlateForUnit ("player")
			if (plateFrame) then
			
				if (not plateFrame.Plater) then
					return
				end
			
				if (NamePlateDriverFrame.classNamePlatePowerBar and NamePlateDriverFrame.classNamePlatePowerBar:IsShown()) then
					--hide the power bar from default ui
					NamePlateDriverFrame.classNamePlatePowerBar:Hide()
					NamePlateDriverFrame.classNamePlatePowerBar:UnregisterAllEvents()
				end
				
				local unitFrame = plateFrame.unitFrame
				
				--setup the power bar and cast bar from the details! framework unit frame
				local powerBar = unitFrame.powerBar
				local castBar = unitFrame.castBar
				local healthBar = unitFrame.healthBar
				
				if (not DB_PLATE_CONFIG.player.healthbar_enabled) then
					--the health bar is set when the nameplate is shown
					healthBar:SetUnit (nil)
					
					-- hide target glow
					plateFrame.TargetNeonUp:Hide()
					plateFrame.TargetNeonDown:Hide()
				end
				
				if (DB_PLATE_CONFIG.player.power_enabled) then
					powerBar:SetUnit (unitFrame.unit)
					
					--update the power percent text
					local plateConfigs = DB_PLATE_CONFIG.player
					if (plateConfigs.power_percent_text_enabled) then
						unitFrame.powerBar.Settings.ShowPercentText = true
						
						local textString = powerBar.percentText
						textString:Show()
						
						DF:SetFontSize (textString, plateConfigs.power_percent_text_size)
						DF:SetFontFace (textString, plateConfigs.power_percent_text_font)
						
						DF:SetFontColor (textString, plateConfigs.power_percent_text_color)
						Plater.SetAnchor (textString, plateConfigs.power_percent_text_anchor)
						textString:SetAlpha (plateConfigs.power_percent_text_alpha)
						
						Plater.SetFontOutlineAndShadow (textString, plateConfigs.power_percent_text_outline, plateConfigs.power_percent_text_shadow_color, plateConfigs.power_percent_text_shadow_color_offset[1], plateConfigs.power_percent_text_shadow_color_offset[2])
					else
						unitFrame.powerBar.Settings.ShowPercentText = false
						powerBar.percentText:Hide()
					end
				else
					powerBar:SetUnit (nil)
				end
				
				--setup the cast bar from details! framework unit frame
				if (DB_PLATE_CONFIG.player.castbar_enabled) then
					castBar:SetUnit (unitFrame.unit)
					castBar.extraBackground:Show()
				else
					castBar:SetUnit (nil)
				end
				
			end
			
			--update resource bar
			Plater.UpdateResourceFrame()
		end
		
		--can also hook 'ClassNameplateBar:ShowNameplateBar()' which will show and call NamePlateDriverFrame:SetClassNameplateBar(self); which will call SetupClassNameplateBars()
		if IS_WOW_PROJECT_MAINLINE then
			hooksecurefunc (NamePlateDriverFrame, "SetupClassNameplateBars", function (self)
				return Plater.UpdatePersonalBar (self)
			end)
			
			--[[ -- fuck things up a bit...
			hooksecurefunc (NamePlateBaseMixin, "OnAdded", function(self, namePlateUnitToken, driverFrame)
				local plateFrame = C_NamePlate.GetNamePlateForUnit (namePlateUnitToken)
				Plater.OnRetailNamePlateShow(plateFrame.UnitFrame)
			end)
			
			hooksecurefunc (NamePlateDriverFrame, "OnNamePlateAdded", function(self, namePlateUnitToken)
				if not ENABLED_BLIZZARD_PLATEFRAMES[tostring(frame)] then
					local plateFrame = C_NamePlate.GetNamePlateForUnit (namePlateUnitToken)
					DevTool:AddData(plateFrame, "OnNamePlateAdded")
					C_Timer.After(0, function() Plater.OnRetailNamePlateShow(plateFrame.UnitFrame) end)
				end
			end)
			hooksecurefunc ("DefaultCompactNamePlateFrameSetupInternal", function(frame)
				DevTool:AddData(frame, "DefaultCompactNamePlateFrameSetupInternal")
				if not ENABLED_BLIZZARD_PLATEFRAMES[tostring(frame)] then
					
					--Plater.OnRetailNamePlateShow (frame)
				end
			end)
			--]]
			
		end

		--update the resource location and anchor
		function Plater.UpdateResourceFrame()
			if IS_WOW_PROJECT_NOT_MAINLINE then return end
			--this holds a reference of the current resource frame anchored into the 'target' namepate
			--it is used when checking if the unit has auras to move the resources up to make room for the auras
			Plater.CurrentTargetResourceFrame = nil
		
			local showSelf = GetCVarBool ("nameplateShowSelf") and Plater.db.profile.plate_config.player.module_enabled
			local onCurrentTarget = PlaterDBChr.resources_on_target
			
			if (not showSelf) then
				if (not onCurrentTarget) then
					return
				end
			end
			
			local resourceFrame = NamePlateDriverFrame.classNamePlateMechanicFrame
			if (resourceFrame and not resourceFrame:IsForbidden()) then
				if Plater.db.profile.resources_settings.global_settings.show then
					resourceFrame:SetAlpha (0)
					resourceFrame:Hide()
					return
				end
				
				--> set scale based on Plater user settings
				resourceFrame:SetScale (Plater.db.profile.resources.scale)
				resourceFrame:SetAlpha (Plater.db.profile.resources.alpha)
				
				--check if resources are placed on the current target
				if (onCurrentTarget) then
					--resource bar are placed on the current target nameplate
					local targetPlateFrame = C_NamePlate.GetNamePlateForUnit ("target", false) -- don't attach to secure frames to avoid tainting!
					if (targetPlateFrame) then
						resourceFrame:Show()
						resourceFrame:SetParent (targetPlateFrame.unitFrame)
						resourceFrame:ClearAllPoints()
						resourceFrame:SetPoint ("bottom", targetPlateFrame.unitFrame.healthBar, "top", 0, Plater.db.profile.resources.y_offset_target)
						resourceFrame:SetFrameStrata(targetPlateFrame.unitFrame.healthBar:GetFrameStrata())
						resourceFrame:SetFrameLevel(targetPlateFrame.unitFrame.healthBar:GetFrameLevel() + 25)
						Plater.CurrentTargetResourceFrame = resourceFrame
						
						Plater.UpdateResourceFrameAnchor (targetPlateFrame.unitFrame.BuffFrame)
					else
						resourceFrame:Hide()
					end
				else
					--resource bar are placed below the mana bar at the personal bar
					local personalPlateFrame = C_NamePlate.GetNamePlateForUnit ("player", issecure())
					if (personalPlateFrame) then
						resourceFrame:Show()
						resourceFrame:SetParent (personalPlateFrame.unitFrame)
						resourceFrame:ClearAllPoints()
						
						--> attach to powerbar if shown
						if (personalPlateFrame.unitFrame.powerBar:IsShown()) then
							resourceFrame:SetPoint ("top", personalPlateFrame.unitFrame.powerBar, "bottom", 0, -3 + Plater.db.profile.resources.y_offset)
						else
							resourceFrame:SetPoint ("top", personalPlateFrame.unitFrame.healthBar, "bottom", 0, -3 + Plater.db.profile.resources.y_offset)
						end
						
						resourceFrame:SetFrameStrata(personalPlateFrame.unitFrame.healthBar:GetFrameStrata())
						resourceFrame:SetFrameLevel(personalPlateFrame.unitFrame.healthBar:GetFrameLevel() + 25)
					else
						resourceFrame:Hide()
					end
				end
			end
			
			local alternatePowerFrame = NamePlateDriverFrame.classNamePlateAlternatePowerBar
			if (alternatePowerFrame and not alternatePowerFrame:IsForbidden()) then
				if Plater.db.profile.resources_settings.global_settings.show then
					alternatePowerFrame:SetAlpha (0)
					alternatePowerFrame:Hide()
					return
				end
				
				--> set scale based on Plater user settings
				alternatePowerFrame:SetScale (Plater.db.profile.resources.scale * (resourceFrame and 2 or 2)) --augvoker and stagger bars, separate handling. same size for now
				alternatePowerFrame:SetAlpha (Plater.db.profile.resources.alpha)
				
				--check if resources are placed on the current target
				if (onCurrentTarget) then
					--resource bar are placed on the current target nameplate
					local targetPlateFrame = C_NamePlate.GetNamePlateForUnit ("target", false) -- don't attach to secure frames to avoid tainting!
					if (targetPlateFrame) then
						alternatePowerFrame:Show()
						alternatePowerFrame:SetParent (targetPlateFrame.unitFrame)
						alternatePowerFrame:ClearAllPoints()
						if resourceFrame then
							alternatePowerFrame:SetPoint ("bottom", resourceFrame, "top", 0, 2)
						else
							alternatePowerFrame:SetPoint ("bottom", targetPlateFrame.unitFrame.healthBar, "top", 0, Plater.db.profile.resources.y_offset_target)
						end
						alternatePowerFrame:SetFrameStrata(targetPlateFrame.unitFrame.healthBar:GetFrameStrata())
						alternatePowerFrame:SetFrameLevel(targetPlateFrame.unitFrame.healthBar:GetFrameLevel() + 25)
						Plater.CurrentTargetResourceFrame = resourceFrame or alternatePowerFrame
						
						Plater.UpdateResourceFrameAnchor (targetPlateFrame.unitFrame.BuffFrame)
					else
						alternatePowerFrame:Hide()
					end
				else
					--resource bar are placed below the mana bar at the personal bar
					local personalPlateFrame = C_NamePlate.GetNamePlateForUnit ("player", issecure())
					if (personalPlateFrame) then
						alternatePowerFrame:Show()
						alternatePowerFrame:SetParent (personalPlateFrame.unitFrame)
						alternatePowerFrame:ClearAllPoints()
						
						--> attach to powerbar if shown
						if resourceFrame then
							alternatePowerFrame:SetPoint ("top", resourceFrame, "bottom", 0, -2)
						else
							if (personalPlateFrame.unitFrame.powerBar:IsShown()) then
								alternatePowerFrame:SetPoint ("top", personalPlateFrame.unitFrame.powerBar, "bottom", 0, -3 + Plater.db.profile.resources.y_offset)
							else
								alternatePowerFrame:SetPoint ("top", personalPlateFrame.unitFrame.healthBar, "bottom", 0, -3 + Plater.db.profile.resources.y_offset)
							end
						end
						
						alternatePowerFrame:SetFrameStrata(personalPlateFrame.unitFrame.healthBar:GetFrameStrata())
						alternatePowerFrame:SetFrameLevel(personalPlateFrame.unitFrame.healthBar:GetFrameLevel() + 25)
					else
						alternatePowerFrame:Hide()
					end
				end
			end
		end

		if IS_WOW_PROJECT_MAINLINE then
			--C_CVar.RegisterCVar("nameplateShowOnlyNames") -- ensure this is still available and usable for our purposes, as it was removed with 10.0.5, but re-added with amnesia shortly after. not needed now.
		end
		
		-- do this now
		Plater.UpdateBaseNameplateOptions()
		
		--this function is declared inside 'NamePlateDriverMixin' at Blizzard_NamePlates.lua
		hooksecurefunc (NamePlateDriverFrame, "UpdateNamePlateOptions", function()
			Plater.UpdateSelfPlate()
			Plater.UpdateBaseNameplateOptions()
			Plater.UpdatePlateClickSpace()
		end)
		
		--this might come in useful
		function Plater.SetNamePlatePreferredClickInsets(nameplateType, left, right, top, bottom)
			if not InCombatLockdown() then
				if nameplateType == "friendly" then
					C_NamePlate.SetNamePlateFriendlyPreferredClickInsets (left or 0, right or 0, top or 0, bottom or 0)
				elseif nameplateType == "enemy" then
					C_NamePlate.SetNamePlateEnemyPreferredClickInsets (left or 0, right or 0, top or 0, bottom or 0)
				elseif nameplateType == "player" then
					C_NamePlate.SetNamePlateSelfPreferredClickInsets (left or 0, right or 0, top or 0, bottom or 0)
				end
			else
				C_Timer.After(1, function() Plater.SetNamePlatePreferredClickInsets(nameplateType, left, right, top, bottom) end)
			end
		end
		hooksecurefunc(NamePlateDriverFrame.namePlateSetInsetFunctions, "friendly", function()
			--C_NamePlate.SetNamePlateFriendlyPreferredClickInsets (0, 0, 0, 0)
			Plater.SetNamePlatePreferredClickInsets("friendly", 0, 0, 0, 0)
		end)
		hooksecurefunc(NamePlateDriverFrame.namePlateSetInsetFunctions, "enemy", function()
			--C_NamePlate.SetNamePlateEnemyPreferredClickInsets (0, 0, 0, 0)
			Plater.SetNamePlatePreferredClickInsets("enemy", 0, 0, 0, 0)
		end)
		if IS_WOW_PROJECT_MAINLINE then
			hooksecurefunc(NamePlateDriverFrame.namePlateSetInsetFunctions, "player", function()
				--C_NamePlate.SetNamePlateSelfPreferredClickInsets (0, 0, 0, 0)
				Plater.SetNamePlatePreferredClickInsets("player", 0, 0, 0, 0)
			end)
		end
		

	--> cast frame ~castbar
	
		--test castbar ~test
		Plater.CastBarTestFrame = CreateFrame ("frame", nil, UIParent, BackdropTemplateMixin and "BackdropTemplate")
		
		function Plater.StartCastBarTest(castNoInterrupt, castTime, isLoop)
			castTime = castTime or 3

			if (isLoop) then
				Plater.CastBarTestFrame.castNoInterrupt = Plater.CastBarTestFrame.castNoInterrupt
				Plater.CastBarTestFrame.castTime = Plater.CastBarTestFrame.castTime
			else
				Plater.CastBarTestFrame.castNoInterrupt = castNoInterrupt
				Plater.CastBarTestFrame.castTime = castTime
			end

			Plater.IsShowingCastBarTest = true
			Plater.DoCastBarTest(castNoInterrupt, castTime)

			Plater.IsTestRunning = true
		end

		function platerInternal.CastBar.StartTestCastBarForNameplate(plateFrame)
			local castTime = Plater.CastBarTestFrame.castTime
			---@cast plateFrame plateframe
			if plateFrame.unitFrame.PlaterOnScreen then
				local castBar = plateFrame.unitFrame.castBar
				local castNoInterrupt = Plater.CastBarTestFrame.castNoInterrupt
				local spellName, _, spellIcon = GetSpellInfo(116)

				castBar.Text:SetText(spellName)
				castBar.Icon:SetTexture(spellIcon)
				castBar.Icon:SetAlpha(1)
				castBar.Icon:Show()
				castBar.percentText:Show()
				castBar:SetMinMaxValues(0, (castTime or 3))
				castBar:SetValue(0)
				castBar.Spark:Show()
				castBar.casting = true
				castBar.finished = false
				castBar.value = 0
				castBar.maxValue = (castTime or 3)
				castBar.canInterrupt = castNoInterrupt or math.random (1, 2) == 1
				--castBar.canInterrupt = true
				--castBar.channeling = true
				castBar:UpdateCastColor()

				castBar.spellName = 		spellName
				castBar.spellID = 			116
				castBar.spellTexture = 		spellIcon
				castBar.spellStartTime = 	GetTime()
				castBar.spellEndTime = 		GetTime() + (castTime or 3)
				
				castBar.SpellStartTime = 	GetTime()
				castBar.SpellEndTime = 		GetTime() + (castTime or 3)
				
				castBar.playedFinishedTest = nil
				
				castBar.flashTexture:Hide()
				castBar:Animation_StopAllAnimations()

				if (castBar.channeling) then
					Plater.CastBarOnEvent_Hook(castBar, "UNIT_SPELLCAST_CHANNEL_START", plateFrame.unitFrame.unit, plateFrame.unitFrame.unit)
				else
					Plater.CastBarOnEvent_Hook(castBar, "UNIT_SPELLCAST_START", plateFrame.unitFrame.unit, plateFrame.unitFrame.unit)
				end

				platerInternal.Audio.PlaySoundForCastStart(castBar.spellID)
				
				if (not castBar:IsShown()) then
					castBar:Animation_FadeIn()
					castBar:Show()
				end

				Plater.UpdateCastbarTargetText(castBar)
				local textString = castBar.FrameOverlay.TargetName
				textString:Show()
				textString:SetText("Target Name")
			end
		end
		
		function Plater.DoCastBarTest (castNoInterrupt, castTime)
			Plater.CastBarTestFrame.castTime = castTime or 3
			
			for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
				platerInternal.CastBar.StartTestCastBarForNameplate(plateFrame)
			end
			
			local totalTime = 0
			local checkEachSeconds = 0.4 --0.4 default
			local forward = true

			Plater.CastBarTestFrame:SetScript ("OnUpdate", function (self, deltaTime)
				if (totalTime >= checkEachSeconds) then --(Plater.CastBarTestFrame.castTime + 0.1)
					totalTime = 0

					for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
						---@cast plateFrame plateframe
						if plateFrame.unitFrame.PlaterOnScreen then
							local castBar = plateFrame.unitFrame.castBar
							--local textString = castBar.FrameOverlay.TargetName
							--textString:Show()
							--textString:SetText("Target Name")

							if (castBar.finished and not castBar.playedFinishedTest) then
								Plater.CastBarOnEvent_Hook (castBar, "UNIT_SPELLCAST_STOP", plateFrame.unitFrame.unit, plateFrame.unitFrame.unit)
								castBar.playedFinishedTest = true
							end
						end
					end
					
					if (Plater.IsShowingCastBarTest) then
						--run another cycle
						if (not Plater.CastBarTestFrame.ScheduleNewCycle) then
							Plater.CastBarTestFrame.ScheduleNewCycle = C_Timer.NewTimer(0.5, function()
								if (Plater.IsShowingCastBarTest) then
									for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
										if (not plateFrame.unitFrame.castBar:IsShown()) then
											platerInternal.CastBar.StartTestCastBarForNameplate(plateFrame)
										end
									end
								end
								Plater.CastBarTestFrame.ScheduleNewCycle = nil
							end)
						end
					else
						--don't run another cycle
						Plater.CastBarTestFrame:SetScript("OnUpdate", nil)
						Plater.IsTestRunning = nil
					end
				else
					totalTime = totalTime + deltaTime
				end
			end)
		end
		
		function Plater.StopCastBarTest()
			for _, plateFrame in ipairs(Plater.GetAllShownPlates()) do
				local castBar = plateFrame.unitFrame.castBar
				if (castBar:IsShown()) then
					Plater.CastBarOnEvent_Hook(castBar, "UNIT_SPELLCAST_STOP", plateFrame.unitFrame.unit, plateFrame.unitFrame.unit)
					castBar.playedFinishedTest = true
					castBar:Hide()
				end
			end

			if (Plater.CastBarTestFrame.ScheduleNewCycle and not Plater.CastBarTestFrame.ScheduleNewCycle:IsCancelled()) then
				Plater.CastBarTestFrame.ScheduleNewCycle:Cancel()
				Plater.CastBarTestFrame.ScheduleNewCycle = nil
			end

			Plater.IsTestRunning = nil
			Plater.IsShowingCastBarTest = false
			Plater.CastBarTestFrame:SetScript("OnUpdate", nil)
		end
	
		--> when the option to show the target of the cast is enabled, this function update the text settings but not the target name
		function Plater.UpdateCastbarTargetText (castBar)
			local profile = Plater.db.profile
			
			if (profile.castbar_target_show) then
				local textString = castBar.FrameOverlay.TargetName
				textString:Show()
				
				DF:SetFontSize (textString, profile.castbar_target_text_size)
				DF:SetFontOutline (textString, profile.castbar_target_shadow)
				
				Plater.SetFontOutlineAndShadow (textString, profile.castbar_target_outline, profile.castbar_target_shadow_color, profile.castbar_target_shadow_color_offset[1], profile.castbar_target_shadow_color_offset[2])
				
				DF:SetFontColor (textString, profile.castbar_target_color)
				DF:SetFontFace (textString, profile.castbar_target_font)
				
				Plater.SetAnchor (textString, profile.castbar_target_anchor)
			else
				castBar.FrameOverlay.TargetName:Hide()
			end
		end
		
		--> self is the castBar object from the details! framework unit frame widget
		--> this hook is set inside the nameplate created event
		function Plater.CastBarOnShow_Hook (self, unit) --private
			--> this cast bar is a nameplate widget?
			if (self.isNamePlate) then
				if (self.IsSelf) then
					self.extraBackground:Show()
				else
					--in case the unit is out of range, add some background color for the cast
					if (self:GetAlpha() < 0.4) then
						self.extraBackground:Show()
					else
						self.extraBackground:Hide()
					end
				end
			end
		end
		
		function Plater.UpdateCastbarIcon(castBar)
			local profile = Plater.db.profile
			local icon = castBar.Icon
			local unitFrame = castBar.unitFrame
			local borderShield = castBar.BorderShield
			
			--icon:SetDrawLayer ("OVERLAY", 5)
			--borderShield:SetDrawLayer ("OVERLAY", 6)
			local castBarHeight = castBar:GetHeight()
			
			if (profile.castbar_icon_customization_enabled) then

				if (profile.castbar_icon_show) then
					icon:ClearAllPoints()
					borderShield:ClearAllPoints()
					borderShield:SetTexture ([[Interface\GROUPFRAME\UI-GROUP-MAINTANKICON]])
					borderShield:SetTexCoord (0, 1, 0, 1)
					borderShield:SetDesaturated (true)
					PixelUtil.SetSize (borderShield, castBarHeight * 0.8, castBarHeight)

					if (profile.castbar_icon_attach_to_side == "left") then
						if (profile.castbar_icon_size == "same as castbar") then
							icon:SetPoint("topright", castBar, "topleft", profile.castbar_icon_x_offset, 0)
							icon:SetPoint("bottomright", castBar, "bottomleft", profile.castbar_icon_x_offset, 0)
							
							PixelUtil.SetPoint (borderShield, "center", castBar, "left", 0, 0)

						elseif (profile.castbar_icon_size == "same as castbar plus healthbar") then
							local actorType = unitFrame.actorType
							local plateConfigs = DB_PLATE_CONFIG [actorType]
							local isInCombat = profile.use_player_combat_state and PLAYER_IN_COMBAT or unitFrame.InCombat
							local castBarConfigKey, healthBarConfigKey, manaConfigKey = Plater.GetHashKey (isInCombat)

							local healthBarHeight = unitFrame.customHealthBarHeight or (plateConfigs and plateConfigs [healthBarConfigKey][2]) or 0
							local castBarOffSetY = plateConfigs and plateConfigs.castbar_offset or 0
							
							if castBarOffSetY > healthBarHeight then
								icon:SetPoint("topright", castBar, "topleft", profile.castbar_icon_x_offset, 0)
								icon:SetPoint("bottomright", unitFrame.healthBar, "bottomleft", profile.castbar_icon_x_offset, 0)
							else
								icon:SetPoint("topright", unitFrame.healthBar, "topleft", profile.castbar_icon_x_offset, 0)
								icon:SetPoint("bottomright", castBar, "bottomleft", profile.castbar_icon_x_offset, 0)
							end
							
							PixelUtil.SetPoint (borderShield, "center", castBar, "left", 0, 0)
						end

					elseif (profile.castbar_icon_attach_to_side == "right") then
						if (profile.castbar_icon_size == "same as castbar") then
							icon:SetPoint("topleft", castBar, "topright", profile.castbar_icon_x_offset, 0)
							icon:SetPoint("bottomleft", castBar, "bottomright", profile.castbar_icon_x_offset, 0)
							
							PixelUtil.SetPoint (borderShield, "center", castBar, "right", 0, 0)

						elseif (profile.castbar_icon_size == "same as castbar plus healthbar") then
							local actorType = unitFrame.actorType
							local plateConfigs = DB_PLATE_CONFIG [actorType]
							local isInCombat = profile.use_player_combat_state and PLAYER_IN_COMBAT or unitFrame.InCombat
							local castBarConfigKey, healthBarConfigKey, manaConfigKey = Plater.GetHashKey (isInCombat)

							local healthBarHeight = unitFrame.customHealthBarHeight or (plateConfigs and plateConfigs [healthBarConfigKey][2]) or 0
							local castBarOffSetY = plateConfigs and plateConfigs.castbar_offset or 0
							
							if castBarOffSetY > healthBarHeight then
								icon:SetPoint("topleft", castBar, "topright", profile.castbar_icon_x_offset, 0)
								icon:SetPoint("bottomleft", unitFrame.healthBar, "bottomright", profile.castbar_icon_x_offset, 0)
							else
								icon:SetPoint("topleft", unitFrame.healthBar, "topright", profile.castbar_icon_x_offset, 0)
								icon:SetPoint("bottomleft", castBar, "bottomright", profile.castbar_icon_x_offset, 0)
							end
							
							PixelUtil.SetPoint (borderShield, "center", castBar, "right", 0, 0)
						end
					end

					icon:SetWidth(icon:GetHeight())
				else
					icon:Hide()
					borderShield:Hide()
				end
			else
				icon:ClearAllPoints()
				PixelUtil.SetPoint (icon, "left", castBar, "left", 0, 0)
				PixelUtil.SetSize (icon, castBarHeight, castBarHeight)
				
				--setup non interruptible cast shield
				borderShield:SetTexture ([[Interface\ACHIEVEMENTFRAME\UI-Achievement-Progressive-IconBorder]])
				borderShield:SetTexCoord (5/64, 37/64, 1/64, 36/64)
				borderShield:ClearAllPoints()
				borderShield:SetPoint ("center", castBar.Icon, "center")
				PixelUtil.SetSize (borderShield, castBarHeight * 1.4, castBarHeight * 1.4)
				borderShield:SetDesaturated (false)
			end
			if castBar.Icon.Masqued then
				Plater.Masque.CastIcon:ReSkin(castBar.Icon)
			end
		end
		
		
		---return if the unit is casting a spell, if the spellId is passed, will return if the unit is casting the spellId, otherwise will return the spellId of the spell being casted
		---@param unitId string
		---@param spellId number|string|nil
		---@return boolean|number
		function Plater.UnitIsCasting(unitId, spellId)
			if (UnitExists(unitId)) then
				---@type plateframe
				local plateFrame = C_NamePlate.GetNamePlateForUnit(unitId)
				if (plateFrame) then
					local castBar = plateFrame.unitFrame.castBar
					if (castBar:IsShown()) then
						if (spellId) then
							return castBar.SpellID == spellId or castBar.SpellName == spellId
						else
							return castBar.SpellID
						end
					end
				else
					return false
				end
			end
			return false
		end

		---return true if the spell can be interrupted
		---the function can only return results for spells that the addon observed being interrupted.
		---@param spellId number
		---@return boolean|nil
		function Plater.IsSpellInterruptable(spellId)
			return PlaterDB.InterruptableSpells[spellId]
		end

		--hook for all castbar events --~cast
		function Plater.CastBarOnEvent_Hook (self, event, unit, ...) --private
	
			if (event == "PLAYER_ENTERING_WORLD") then
				if (not self.isNamePlate) then
					return
				end
				
				unit = self.unit
				
				if (self.casting) then
					event = "UNIT_SPELLCAST_START"
					
				elseif (self.channeling) then
					event = "UNIT_SPELLCAST_CHANNEL_START"
				else
					return
				end
			end
			
			if (self.isNamePlate) then
				local unitFrame = self.unitFrame
				local shouldRunCastStartHook = false
				
				if (event == "UNIT_SPELLCAST_START" or event == "UNIT_SPELLCAST_CHANNEL_START") then
					local unitCast = unit
					if (unitCast ~= self.unit) then
						return
					end

					-- if we are starting a cast but it is an immediate chained cast, then needs to trigger OnHide and OnShow again afterwards
					local globalScriptObject = SCRIPT_CASTBAR_TRIGGER_CACHE[self.SpellName]
					if (globalScriptObject and (self.casting or self.channeling) and not self.IsInterrupted) then
						self:OnHideWidget()
					end

					--reset the visibility of the spell name text
					self.Text:Show()
					
					local curTime = GetTime()
					--local name, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo (unitCast)
					self.SpellName = 		self.spellName
					self.SpellID = 		self.spellID
					self.SpellTexture = 	self.spellTexture
					self.SpellStartTime = 	self.spellStartTime or curTime
					self.SpellEndTime = 	self.spellEndTime or curTime
					
					local notInterruptible = not self.canInterrupt
					
					self.IsInterrupted = false
					self.InterruptSourceName = nil
					self.InterruptSourceGUID = nil
					self.ReUpdateNextTick = true
					self.ThrottleUpdate = -1
					
					if (notInterruptible) then
						self.BorderShield:Show()
					else
						self.BorderShield:Hide()
					end

					if (notInterruptible) then
						self.CanInterrupt = false
					else
						self.CanInterrupt = true
					end
					
					self.FrameOverlay:SetBackdropBorderColor (0, 0, 0, 0)
					
					local profile = Plater.db.profile
					local isInCombat = profile.use_player_combat_state and PLAYER_IN_COMBAT or unitFrame.InCombat
					
					--reset spark color and size
					self.Spark:SetVertexColor(unpack(profile.cast_statusbar_spark_color))
					self.Spark:SetAlpha (profile.cast_statusbar_spark_alpha)
					PixelUtil.SetSize(self.Spark, profile.cast_statusbar_spark_width, self:GetHeight())

					--cut the spell name text to fit within the castbar
					Plater.UpdateSpellNameSize (self.Text, unitFrame.ActorType, nil, isInCombat)

					Plater.UpdateCastbarTargetText (self)

					--castbar icon
					Plater.UpdateCastbarIcon(self)

					shouldRunCastStartHook = true

					--spell color
					self.castColorTexture:Hide()

					--cast color (from options tab Cast Colors)
					local castColors = profile.cast_colors
					local customColor = castColors[self.spellID]
					local customRenamed = false
					if (customColor) then
						local isEnabled, color, customSpellName = customColor[1], customColor[2], customColor[3]
						if (color and isEnabled) then
							local originalCastColor = profile.cast_color_settings.enabled

							--set the new cast color
							if (color == "white") then
								--the color white is used as a default disabled color
								originalCastColor = false
							else
								self:SetColor(color)
							end

							if (customSpellName and customSpellName ~= "") then
								self.Text:SetText(customSpellName)
								customRenamed = true
							end

							--check if the original cast color is enabled
							if (originalCastColor) then
								--get the original cast color
								local castColor = self:GetCastColor()
								self.castColorTexture:Show()
								local r, g, b = Plater:ParseColors(castColor)
								self.castColorTexture:SetColorTexture(r, g, b)
								self.castColorTexture:SetHeight(self:GetHeight() + profile.cast_color_settings.height_offset)
							end
						end
					end
					
					if not customRenamed and Plater.db.profile.bossmod_castrename_enabled then
						local bmSpellName = ((BigWigsAPI and BigWigsAPI.GetSpellRename and BigWigsAPI.GetSpellRename(self.spellID)) or (DBM and DBM.GetAltSpellName and DBM:GetAltSpellName(self.spellID))) or nil
						if bmSpellName then
							self.Text:SetText(bmSpellName)
						end
					end
					
					if (self.channeling and (self.SpellStartTime + 0.25 > curTime)) then
						platerInternal.Audio.PlaySoundForCastStart(self.spellID) --fallback for edge cases. should not double play
					end
					
					-- in some occasions channeled casts don't have a CLEU entry... check this here
					if (unitFrame.ActorType == "enemynpc" and event == "UNIT_SPELLCAST_CHANNEL_START" and (not DB_CAPTURED_SPELLS[self.spellID] or DB_CAPTURED_SPELLS[self.spellID].isChanneled == nil)) then
						parserFunctions.SPELL_CAST_SUCCESS (nil, "SPELL_CAST_SUCCESS", nil, unitFrame[MEMBER_GUID], unitFrame.unitNameInternal, 0x00000000, nil, nil, nil, nil, nil, self.spellID, nil, nil, nil, nil, nil, nil, nil, nil, nil)
					end

				elseif (event == "UNIT_SPELLCAST_INTERRUPTED") then
					local unitCast = unit
					if (unitCast ~= self.unit) then
						return
					end

					--self:Hide()
					
					-- this is called in SPELL_INTERRUPT event
					--self:OnHideWidget()
					--self.IsInterrupted = true

				elseif (event == "UNIT_SPELLCAST_STOP" or event == "UNIT_SPELLCAST_CHANNEL_STOP") then
					local unitCast = unit
					if (unitCast ~= self.unit) then
						return
					end

					self:OnHideWidget()

					if (Plater.db.profile.cast_statusbar_quickhide) then
						self:Hide()
					end
				end
				
				--hooks
				if (shouldRunCastStartHook) then
					if (HOOK_CAST_START.ScriptAmount > 0) then
						for i = 1, HOOK_CAST_START.ScriptAmount do
							local globalScriptObject = HOOK_CAST_START [i]
							local scriptContainer = unitFrame:ScriptGetContainer()
							local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Cast Start")
							
							--update envTable
							local scriptEnv = scriptInfo.Env
							scriptEnv._SpellID = self.SpellID
							scriptEnv._UnitID = self.unit
							scriptEnv._SpellName = self.SpellName
							scriptEnv._Texture = self.SpellTexture
							scriptEnv._Caster = self.unit
							scriptEnv._Duration = self.SpellEndTime - self.SpellStartTime
							scriptEnv._StartTime = self.SpellStartTime
							scriptEnv._CanInterrupt = self.CanInterrupt
							scriptEnv._EndTime = self.SpellEndTime
							scriptEnv._RemainingTime = max (self.SpellEndTime - GetTime(), 0)
							scriptEnv._CanStealOrPurge = self.CanStealOrPurge
							scriptEnv._AuraType = self.AuraType
							scriptEnv._CastBarHeight = unitFrame.castBar:GetHeight()
							
							--run
							unitFrame:ScriptRunHook (scriptInfo, "Cast Start", self)
						end
					end
				end
			end
		end
		
		Plater.CastBarOnTick_Hook = function (self, deltaTime) --private
			if (self.percentText) then --check if is a plater cast bar
			
				Plater.StartLogPerformanceCore("Plater-Core", "Update", "CastBarOnTick")
				
				self.ThrottleUpdate = self.ThrottleUpdate - deltaTime
				
				if (self.ThrottleUpdate < 0) then
				
					Plater.StartLogPerformanceCore("Plater-Core", "Update", "CastBarOnTick-Full")

					self.SpellStartTime = self.spellStartTime or GetTime()
					self.SpellEndTime = self.spellEndTime or GetTime()
				
					if (self.ReUpdateNextTick) then
						self.ReUpdateNextTick = nil
					end
					
					if (self.unit and Plater.db.profile.castbar_target_show and not UnitIsUnit (self.unit, "player")) then
						local targetName = UnitName (self.unit .. "target")
						if (targetName) then

							local canShowTargetName = true
							local notInTank = Plater.db.profile.castbar_target_notank
							if (notInTank) then
								if (Plater.PlayerIsTank and targetName == UnitName("player")) then
									canShowTargetName = false
								end
							end

							if (canShowTargetName) then
								if DB_USE_NAME_TRANSLIT then
									targetName = LibTranslit:Transliterate(targetName, TRANSLIT_MARK)
								end
								
								local _, class = UnitClass (self.unit .. "target")
								if (class) then 
									self.FrameOverlay.TargetName:SetText (targetName)
									self.FrameOverlay.TargetName:SetTextColor (DF:ParseColors (class))
								else
									self.FrameOverlay.TargetName:SetText (targetName)
									DF:SetFontColor (self.FrameOverlay.TargetName, Plater.db.profile.castbar_target_color)
								end
							else
								self.FrameOverlay.TargetName:SetText ("")
							end
						else
							self.FrameOverlay.TargetName:SetText ("")
						end
					else
						self.FrameOverlay.TargetName:SetText ("")
					end
					
					self.ThrottleUpdate = self.unitFrame.PlateFrame.OnTickFrame.ThrottleUpdate + DB_TICK_THROTTLE

					--get the script object of the aura which will be showing in this icon frame
					local globalScriptObject = SCRIPT_CASTBAR_TRIGGER_CACHE[self.SpellName]

					--check if this aura has a custom script
					if (globalScriptObject and self.SpellEndTime and GetTime() < self.SpellEndTime and (self.casting or self.channeling) and not self.IsInterrupted) then
						--stored information about scripts
						local scriptContainer = self:ScriptGetContainer()
						--get the info about this particularly script
						local scriptInfo = self:ScriptGetInfo (globalScriptObject, scriptContainer)
						
						local scriptEnv = scriptInfo.Env
						
						scriptEnv._SpellID = self.SpellID
						scriptEnv._UnitID = self.unit
						scriptEnv._SpellName = self.SpellName
						scriptEnv._Texture = self.SpellTexture
						scriptEnv._Caster = self.unit
						scriptEnv._Duration = self.SpellEndTime - self.SpellStartTime
						scriptEnv._StartTime = self.SpellStartTime
						scriptEnv._CanInterrupt = self.CanInterrupt
						scriptEnv._EndTime = self.SpellEndTime
						scriptEnv._RemainingTime = max (self.SpellEndTime - GetTime(), 0)
						scriptEnv._CanStealOrPurge = self.CanStealOrPurge
						scriptEnv._AuraType = self.AuraType
						
						if (self.casting) then
							scriptEnv._CastPercent = self.value / self.maxValue * 100
							
						elseif (self.channeling) then
							scriptEnv._CastPercent = abs (self.value - self.maxValue) / self.maxValue * 100
						end
					
						--run onupdate script
						self:ScriptRunOnUpdate (scriptInfo)
					end
					
					--hooks
					if (HOOK_CAST_UPDATE.ScriptAmount > 0) then
						for i = 1, HOOK_CAST_UPDATE.ScriptAmount do
							local globalScriptObject = HOOK_CAST_UPDATE [i]
							local unitFrame = self.unitFrame
							local scriptContainer = unitFrame:ScriptGetContainer()
							local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Cast Update")
							
							--update envTable
							local scriptEnv = scriptInfo.Env
							scriptEnv._SpellID = self.SpellID
							scriptEnv._UnitID = self.unit
							scriptEnv._SpellName = self.SpellName
							scriptEnv._Texture = self.SpellTexture
							scriptEnv._Caster = self.unit
							scriptEnv._Duration = self.SpellEndTime - self.SpellStartTime
							scriptEnv._StartTime = self.SpellStartTime
							scriptEnv._CanInterrupt = self.CanInterrupt
							scriptEnv._EndTime = self.SpellEndTime
							scriptEnv._RemainingTime = max (self.SpellEndTime - GetTime(), 0)
							scriptEnv._CanStealOrPurge = self.CanStealOrPurge
							scriptEnv._AuraType = self.AuraType
							
							--run
							unitFrame:ScriptRunHook (scriptInfo, "Cast Update", self)
						end
					end
					
					Plater.EndLogPerformanceCore("Plater-Core", "Update", "CastBarOnTick-Full")
					
				end
				
				Plater.EndLogPerformanceCore("Plater-Core", "Update", "CastBarOnTick")
			end
		end
		
	--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	--> health frame

	function Plater.QuickHealthUpdate (unitFrame)
		local unitHealth = UnitHealth (unitFrame.unit)
		local unitHealthMax = UnitHealthMax (unitFrame.unit)
		unitFrame.healthBar:SetMinMaxValues (0, unitHealthMax)
		unitFrame.healthBar:SetValue (unitHealth)
		
		unitFrame.healthBar.CurrentHealth = unitHealth
		unitFrame.healthBar.CurrentHealthMax = unitHealthMax
		unitFrame.healthBar.currentHealth = unitHealth
		unitFrame.healthBar.currentHealthMax = unitHealthMax
	end
	
	local run_on_health_change_hook = function (unitFrame)
		if unitFrame.isPerformanceUnit then return end -- don't run health update hooks on performance units
		for i = 1, HOOK_HEALTH_UPDATE.ScriptAmount do
			local globalScriptObject = HOOK_HEALTH_UPDATE [i]
			local scriptContainer = unitFrame:ScriptGetContainer()
			local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Health Update")
			--run
			unitFrame:ScriptRunHook (scriptInfo, "Health Update")
		end
	end
	
	function Plater.OnUpdateHealth (self) --self is unitFrame.healthBar
		if (not self.isNamePlate) then
			--this is not a nameplate, perhaps another frame from the framework
			return
		end
		
		Plater.StartLogPerformanceCore("Plater-Core", "Health", "OnUpdateHealth")

		-- update - for whatever weird reason max health event does not give proper values sometimes...
		if self.displayedUnit then --failsafe?!
			local maxHealth = UnitHealthMax (self.displayedUnit)
			self:SetMinMaxValues (0, maxHealth)
			self.currentHealthMax = maxHealth
		end

		---@type plateframe
		local plateFrame = self.PlateFrame
		local currentHealth = self.currentHealth
		local currentHealthMax = self.currentHealthMax
		local unitFrame = self.unitFrame
		local oldHealth = self.CurrentHealth
		
		--> exposed values to scripts
		self.CurrentHealth = currentHealth
		self.CurrentHealthMax = currentHealthMax
	
		if (plateFrame.IsSelf) then
			self.CurrentHealth = currentHealth
			self.CurrentHealthMax = currentHealthMax
		
			--> flash if low health
			if (currentHealth / currentHealthMax < 0.27) then
				if (not self.PlayHealthFlash) then
					Plater.CreateHealthFlashFrame (plateFrame)
				end
				self.PlayHealthFlash()
			else
				if (self.PlayHealthFlash) then
					if (currentHealth / currentHealthMax > 0.5) then
						self.canHealthFlash = true
					end
				end
			end
			
			if (plateFrame.PlateConfig.healthbar_color_by_hp) then
				local originalColor = plateFrame.PlateConfig.healthbar_color
				local r, g, b = DF:LerpLinearColor (abs (currentHealth / currentHealthMax - 1), 1, originalColor[1], originalColor[2], originalColor[3], 1, .4, 0)
				Plater.ChangeHealthBarColor_Internal (self, r, g, b, (originalColor[4] or 1), true)
			--else
				--Plater.ChangeHealthBarColor_Internal (self, unpack (plateFrame.PlateConfig.healthbar_color))
			end
			
			Plater.CheckLifePercentText (unitFrame)
			
		else

			--quick hide the nameplate if the unit doesn't exists or if the unit died
			if (DB_USE_QUICK_HIDE and (IS_WOW_PROJECT_MAINLINE)) then
				if (not UnitExists (unitFrame.unit) or self.CurrentHealth < 1) then
					--the unit died!
					unitFrame:Hide()
					Plater.EndLogPerformanceCore("Plater-Core", "Health", "OnUpdateHealth")
					return
				end
			end
			
			if (DB_DO_ANIMATIONS) then
				--do healthbar animation ~animation ~healthbar
				oldHealth = oldHealth or self.CurrentHealth
				
				self.CurrentHealthMax = currentHealthMax
				self.AnimationStart = oldHealth
				self.AnimationEnd = currentHealth

				self:SetValue (oldHealth)
				
				self.IsAnimating = true
				
				if (self.AnimationEnd > self.AnimationStart) then
					self.AnimateFunc = Plater.AnimateRightWithAccel
				else
					self.AnimateFunc = Plater.AnimateLeftWithAccel
				end
			else
				self.CurrentHealth = currentHealth
				self.CurrentHealthMax = currentHealthMax
			end
			
			if (plateFrame.actorType == ACTORTYPE_FRIENDLY_PLAYER) then
				local isWithoutHealthbar = plateFrame.IsFriendlyPlayerWithoutHealthBar
				plateFrame.IsNpcWithoutHealthBar = false --ensure this.
				Plater.ParseHealthSettingForPlayer (plateFrame)
				self.ScheduleNameUpdate = plateFrame.IsFriendlyPlayerWithoutHealthBar ~= isWithoutHealthbar
				--Plater.UpdatePlateText (plateFrame, DB_PLATE_CONFIG [ACTORTYPE_FRIENDLY_PLAYER], false)
			end
			
			Plater.CheckLifePercentText (unitFrame)
		end
		
		Plater.EndLogPerformanceCore("Plater-Core", "Health", "OnUpdateHealth")
	end

	--self is the healthBar (it's parent is the unitFrame)
	function Plater.OnUpdateHealthMax (self)
		Plater.StartLogPerformanceCore("Plater-Core", "Health", "OnUpdateHealthMax")
		
		-- ensure updated values...
		Plater.QuickHealthUpdate (self.unitFrame)
		
		Plater.CheckLifePercentText (self.unitFrame)
		
		Plater.EndLogPerformanceCore("Plater-Core", "Health", "OnUpdateHealthMax")
	end

	function Plater.OnHealthChange (self, unitId) --~health
		Plater.OnUpdateHealth (self)
		
		--> run on health changed hook
		if (HOOK_HEALTH_UPDATE.ScriptAmount > 0) then
			return run_on_health_change_hook (self.unitFrame)
		end
	end
	
	function Plater.OnHealthMaxChange (self, unitId)
		Plater.OnUpdateHealthMax (self)
		
		--> run on health changed hook
		if (HOOK_HEALTH_UPDATE.ScriptAmount > 0) then
			return run_on_health_change_hook (self.unitFrame)
		end
	end
	
	--> profile changes and refreshes ~db
		Plater.db.RegisterCallback (Plater, "OnProfileChanged", "RefreshConfigProfileChanged")
		Plater.db.RegisterCallback (Plater, "OnProfileCopied", "RefreshConfig")
		Plater.db.RegisterCallback (Plater, "OnProfileReset", "RefreshConfig")
		--Plater.db.RegisterCallback (Plater, "OnDatabaseShutdown", "SaveConsoleVariables")
		
		function Plater.OnProfileCreated()
			C_Timer.After (.5, function()
				Plater:Msg ("new profile created, applying patches and adding default scripts.")
				platerInternal.Scripts.UpdateFromLibrary()
				
				--enable UIParent nameplates for new installs of Plater
				--this setting is disabled by default and will be enabled for new users and new profiles
				Plater.db.profile.use_ui_parent = true
				--adjust the fine tune to player's screen scale
				Plater.db.profile.ui_parent_scale_tune = 1 / UIParent:GetEffectiveScale()
				
				--call major refresh
				Plater:RefreshConfig()
				Plater.UpdatePlateClickSpace()
				
				--call the user to /reload UI
				DF:ShowPromptPanel ("Plater profile created, do you want /reload now (recommended)?", function() ReloadUI() end, function() end, true, 500)
			end)
		end
		
		Plater.db.RegisterCallback (Plater, "OnNewProfile", "OnProfileCreated")

		Plater.UpdateSelfPlate()
		
		--update quest log after the addon initialization
		C_Timer.After (4.1, Plater.QuestLogUpdated)

		--update all nameplate after the addon initialized
		C_Timer.After (5.1, function() 
			Plater.IncreaseRefreshID()
			Plater.FullRefreshAllPlates()
		end)
	
		for i = 1, 3 do
			C_Timer.After (i, Plater.RefreshDBUpvalues)
		end
		
	if CastingBarFrame then
		CastingBarFrame:HookScript ("OnShow", function (self)
			if (Plater.db.profile.hide_blizzard_castbar) then
				self:Hide()
			end
		end)
	elseif PlayerCastingBarFrame then
		PlayerCastingBarFrame:HookScript ("OnShow", function (self)
			if (Plater.db.profile.hide_blizzard_castbar) then
				self:Hide()
			end
		end)		
	end
	
	-- fill class-info cache data
	if IS_WOW_PROJECT_MAINLINE then
		for classID = 1, MAX_CLASSES do
			local _, classFile = GetClassInfo(classID)
			CLASS_INFO_CACHE[classFile] = {}
			for i = 1, GetNumSpecializationsForClassID(classID) do
				local specID, maleName, _, iconID, role = GetSpecializationInfoForClassID(classID, i, 2) -- male
				local _, femaleName, _, iconID, role = GetSpecializationInfoForClassID(classID, i, 3) -- female
				CLASS_INFO_CACHE[classFile][maleName] = {role = role, specID = specID, iconID = iconID}
				CLASS_INFO_CACHE[classFile][femaleName] = CLASS_INFO_CACHE[classFile][maleName]
			end
		end
	end
	
	-- hook to the InterfaceOptionsFrame and VideoOptionsFrame to update the nameplate sizes, as blizzard somehow messes things up there on hide...
	if InterfaceOptionsFrame then
		InterfaceOptionsFrame:HookScript('OnHide',Plater.UpdatePlateClickSpace)
		VideoOptionsFrame:HookScript('OnHide',Plater.UpdatePlateClickSpace)
	elseif SettingsPanel then
		SettingsPanel:HookScript('OnHide',Plater.UpdatePlateClickSpace)
	end
end


function Plater.FormatTime (time)
	if (time >= 3600) then
		return floor (time / 3600) .. "h"
	elseif (time >= 60) then
		return floor (time / 60) .. "m"
	else
		return floor (time)
	end
end

function Plater.FormatTimeDecimal (time)
	if time < 10 then
		return ("%.1f"):format(time)
	elseif time < 60 then
		return ("%d"):format(time)
	elseif time < 3600 then
		return ("%d:%02d"):format(time/60%60, time%60)
	elseif time < 86400 then
		return ("%dh %02dm"):format(time/(3600), time/60%60)
	else
		return ("%dd %02dh"):format(time/86400, (time/3600) - (floor(time/86400) * 24))
	end
end


--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> color stuff ~color

	function Plater.SetQuestColorByReaction (unitFrame)
		--unit is a quest mob, reset the color to quest color
		if (unitFrame.ActorType and DB_PLATE_CONFIG [unitFrame.ActorType].quest_color_enabled) then
			if (unitFrame [MEMBER_REACTION] == Plater.UnitReaction.UNITREACTION_NEUTRAL) then
				Plater.ChangeHealthBarColor_Internal (unitFrame.healthBar, unpack (DB_PLATE_CONFIG [unitFrame.ActorType].quest_color_neutral))
				
			elseif (unitFrame [MEMBER_REACTION] < Plater.UnitReaction.UNITREACTION_NEUTRAL) then
				Plater.ChangeHealthBarColor_Internal (unitFrame.healthBar, unpack (DB_PLATE_CONFIG [unitFrame.ActorType].quest_color_enemy))
				
			else
				--there's a bug here where quest_color is nil for a friendly npc
				--this is happening when an enemy quest npc turns friendly and (probably) the actorType doesn't change
				--so in the enemy npc settings table does not have 'quest_color' input
				Plater.ChangeHealthBarColor_Internal (unitFrame.healthBar, unpack (DB_PLATE_CONFIG [unitFrame.ActorType].quest_color or {.5, 1, 0, 1}))
			end
		end
	end

	--override colors
	--this function will set the color of the nameplate by the reaction of the unit shown
	--it can only run if color override is enabled and when not in combat or when in combat but color by aggro is disabled
	function Plater.ColorOverrider (unitFrame, forceRefresh)
		--not in combat or aggro isn't changing the healthbar color
		if (forceRefresh or not InCombatLockdown() or not DB_AGGRO_CHANGE_HEALTHBAR_COLOR) then
			--isn't a quest
			if (not unitFrame [MEMBER_QUEST] or not DB_PLATE_CONFIG [unitFrame.ActorType].quest_color_enabled) then
				local reaction = unitFrame [MEMBER_REACTION]
				--has a valid reaction
				if (reaction) then
					local r, g, b, a = unpack (Plater.db.profile.color_override_colors [reaction])
					Plater.ChangeHealthBarColor_Internal (unitFrame.healthBar, r, g, b, a, true)
				end
			else
				--unit is a quest mob, reset the color to quest color
				Plater.SetQuestColorByReaction (unitFrame)
			end
		end
	end

	function Plater.DenyColorChange(unitFrame, state)
		unitFrame.DenyColorChange = state
	end

	--refresh the use of the color overrider
	--called from the OnInit and from the options panel when the override color settings is changed
	function Plater.RefreshColorOverride() --private
		if (Plater.db.profile.color_override) then
			Plater.CanOverrideColor = true
		else
			Plater.CanOverrideColor = false
		end
		
		Plater.UpdateAllNameplateColors()
	end
	
	--internal function to change the health bar color
	function Plater.ChangeHealthBarColor_Internal (healthBar, r, g, b, a, forceNoLerp) --private
		a = a or 1
		if (r ~= healthBar.R or g ~= healthBar.G or b ~= healthBar.B or a ~= healthBar.A) then
			healthBar.R, healthBar.G, healthBar.B, healthBar.A = r, g, b, a
			if (not DB_LERP_COLOR or forceNoLerp) then -- ~lerpcolor
				healthBar.barTexture:SetVertexColor (r, g, b, a)
			end
		end
	end

	--do several checkes to determine which are the color of this nameplate
	--if force refresh is true, it'll ignore aggro and incombat checks in the ColorOverrider function
	function Plater.FindAndSetNameplateColor (unitFrame, forceRefresh)
		local r, g, b, a = 1, 1, 1, 1
		local unitID = unitFrame [MEMBER_UNITID]
		if (unitFrame.IsSelf or not unitFrame.PlaterOnScreen) then
			return
			
		else
			--check if is a player
			if UnitIsPlayer (unitID) then
				if (unitFrame.ActorType == ACTORTYPE_FRIENDLY_PLAYER) then
					if (Plater.db.profile.use_playerclass_color) then
						local _, class = UnitClass (unitID)
						local classColor = DB_CLASS_COLORS [class]
						if (classColor) then -- and unitFrame.optionTable.useClassColors
							r, g, b, a = classColor.r, classColor.g, classColor.b, classColor.a
						end
					else
						r, g, b, a = unpack(Plater.db.profile.plate_config.friendlyplayer.fixed_class_color)
					end
				elseif (unitFrame.ActorType == ACTORTYPE_ENEMY_PLAYER) then
					if (Plater.db.profile.plate_config.enemyplayer.use_playerclass_color) then
						local _, class = UnitClass (unitID)
						local classColor = DB_CLASS_COLORS [class]
						if (classColor) then -- and unitFrame.optionTable.useClassColors
							r, g, b, a = classColor.r, classColor.g, classColor.b, classColor.a
						end
					else
						r, g, b, a = unpack(Plater.db.profile.plate_config.enemyplayer.fixed_class_color)
					end
				end
				
			--check if is tapped
			elseif (Plater.IsUnitTapDenied (unitID)) then
				r, g, b, a = unpack (Plater.db.profile.tap_denied_color)

			else
				if (Plater.CanOverrideColor) then
					Plater.ColorOverrider (unitFrame, forceRefresh)
					return
				end

				--check if the mob is a quest mob
				if (unitFrame [MEMBER_QUEST] and DB_PLATE_CONFIG [unitFrame.ActorType].quest_color_enabled) then
					Plater.SetQuestColorByReaction (unitFrame)
					return
				end
				
				--get the color from the client
				r, g, b, a = UnitSelectionColor (unitID)
			end
		end
		
		Plater.ChangeHealthBarColor_Internal (unitFrame.healthBar, r, g, b, a, true)
	end

	--force an update on all nameplates showin in the screen
	--called after a refresh color override (on init and option settings changes)
	--called after leaving the combat
	function Plater.UpdateAllNameplateColors() --private
		for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
			---@cast plateFrame plateframe
			if (not plateFrame.IsSelf) then
				--reset the nameplate color
				Plater.FindAndSetNameplateColor (plateFrame.unitFrame)
			end
		end
	end
	
	--get a unit and a text and color the text with the class color of the unit (accepts player GUID as well)
	function Plater.SetTextColorByClass (unit, text)
		--checking if the unit exists because this can be called from the cleu parser
		if (unit) then
			local _, class = nil, nil
			if (unit:sub(1, #"Player-") == "Player-") then
				_, class = GetPlayerInfoByGUID (unit)
			else
				_, class = UnitClass (unit)
			end
			if (class) then
				local color = DB_CLASS_COLORS [class]
				if (color) then
					text = "|c" .. color.colorStr .. DF:RemoveRealName (text) .. "|r"
				end
			else
				text = DF:RemoveRealName (text)
			end
			return text
		else
			return text
		end
	end

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> update functions ~update

	--run the hook "Nameplate Updated", this is called from inside the Tick and UpdateAllPlates()
	function Plater.TriggerNameplateUpdatedEvent(unitFrame)
		if unitFrame.PlaterOnScreen and (HOOK_NAMEPLATE_UPDATED.ScriptAmount > 0) then
			for i = 1, HOOK_NAMEPLATE_UPDATED.ScriptAmount do
				local globalScriptObject = HOOK_NAMEPLATE_UPDATED [i]

				local scriptContainer = unitFrame:ScriptGetContainer()
				local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Nameplate Updated")
				local scriptEnv = scriptInfo.Env
				scriptEnv._HealthPercent = unitFrame.healthBar.CurrentHealth / unitFrame.healthBar.CurrentHealthMax * 100
				
				--run
				unitFrame:ScriptRunHook (scriptInfo, "Nameplate Updated")
			end
		end
	end

	--full refresh calls
	function Plater.UpdateAllPlates (forceUpdate, justAdded, regenDisabled) --private
		for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
			---@cast plateFrame plateframe
			if plateFrame.unitFrame and plateFrame.unitFrame.PlaterOnScreen then
				if not plateFrame.unitFrame.isPerformanceUnit then
					if not IS_WOW_PROJECT_CLASSIC_ERA or (IS_WOW_PROJECT_CLASSIC_ERA and plateFrame.actorType ~= ACTORTYPE_ENEMY_PLAYER) then -- don't force update in classic
						Plater.AddToAuraUpdate(plateFrame.unitFrame.unit) -- force aura update
					end
				end
				
				Plater.UpdatePlateFrame (plateFrame, nil, forceUpdate, justAdded, regenDisabled)
				--trigger a nameplate updated event
				Plater.TriggerNameplateUpdatedEvent(plateFrame.unitFrame)
			end
		end
	end
	
	--called from the options panel | this is the same as calling Name_Plate_Unit_Added for each nameplate
	function Plater.FullRefreshAllPlates() --private
		for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
			---@cast plateFrame plateframe
			--hack to call the update without overriding user settings from scripts
			Plater.RunScheduledUpdate ({unitId = plateFrame [MEMBER_UNITID], GUID = plateFrame [MEMBER_GUID]})
		end
	end

	--update the player bar (personal nameplate)
	local re_update_self_plate = function()
		Plater.UpdateSelfPlate()
	end

	function Plater.UpdateSelfPlate() --private
		if (InCombatLockdown()) then
			return C_Timer.After (.3, re_update_self_plate)
		end
		C_NamePlate.SetNamePlateSelfClickThrough (DB_PLATE_CONFIG.player.click_through)
		
		--disabled due to modifying the player personal nameplate makes it be a little offset in the Y anchor making it to be in front of the player
	--	C_NamePlate.SetNamePlateSelfSize (unpack (DB_PLATE_CONFIG.player.health))
	end
	
	-- ~size ~updatesize
	---update thee nameplate size including healthbar, castbar, etc
	---@param plateFrame plateframe
	function Plater.UpdatePlateSize (plateFrame)
		if (not plateFrame.actorType) then
			return
		end
		
		Plater.StartLogPerformanceCore("Plater-Core", "Update", "UpdatePlateSize")
		
		local profile = Plater.db.profile
		local unitFrame = plateFrame.unitFrame
		local healthBar = unitFrame.healthBar
		local castBar = unitFrame.castBar
		local powerBar = unitFrame.powerBar
		local buffFrame1 = unitFrame.BuffFrame
		local buffFrame2 = unitFrame.BuffFrame2
		
		local isInCombat = profile.use_player_combat_state and PLAYER_IN_COMBAT or unitFrame.InCombat
		
		--use in combat bars when in pvp
		if (plateFrame.actorType == ACTORTYPE_ENEMY_PLAYER) then
			if ((Plater.ZoneInstanceType == "pvp" or Plater.ZoneInstanceType == "arena") and DB_PLATE_CONFIG.player.pvp_always_incombat) then
				isInCombat = true
			end
		end
		
		local actorType = plateFrame.actorType
		
		--get the config for this actor type
		local plateConfigs = DB_PLATE_CONFIG [actorType]
		--get the config key based if the player is in combat
		local castBarConfigKey, healthBarConfigKey, manaConfigKey = Plater.GetHashKey (isInCombat)

		local healthBarWidth, healthBarHeight = unitFrame.customHealthBarWidth or plateConfigs [healthBarConfigKey][1], unitFrame.customHealthBarHeight or plateConfigs [healthBarConfigKey][2]
		local castBarWidth, castBarHeight = unitFrame.customCastBarWidth or plateConfigs [castBarConfigKey][1], unitFrame.customCastBarHeight or plateConfigs [castBarConfigKey][2]
		local powerBarWidth, powerBarHeight = unitFrame.customPowerBarWidth or plateConfigs [manaConfigKey][1], unitFrame.customPowerBarHeight or plateConfigs [manaConfigKey][2]
		
		local castBarOffSetX = plateConfigs.castbar_offset_x
		local castBarOffSetXRel = (healthBarWidth - castBarWidth) / 2
		local castBarOffSetY = plateConfigs.castbar_offset
		
		local powerBarOffSetX = (healthBarWidth - powerBarWidth) / 2
		local powerBarOffSetY = 0

		--calculate the size deviation for pets
		local unitType = Plater.GetUnitType (plateFrame)
		if (unitType == "pet") then
			healthBarHeight = healthBarHeight * Plater.db.profile.pet_height_scale
			healthBarWidth = healthBarWidth * Plater.db.profile.pet_width_scale

		elseif (unitType == "minus") then
			healthBarHeight = healthBarHeight * Plater.db.profile.minor_height_scale
			healthBarWidth = healthBarWidth * Plater.db.profile.minor_width_scale
		end
		
		--community patch by Ariani#0960 (discord)
		--make the unitFrame be parented to UIParent allowing frames to be moved between strata levels
		--March 3rd, 2019
		if (DB_USE_UIPARENT) then
			--unit frame - is set to be the same size as the plateFrame
				unitFrame:ClearAllPoints()
				unitFrame:SetPoint ("topleft", unitFrame.PlateFrame, "topleft", 0, 0)
				unitFrame:SetPoint ("bottomright", unitFrame.PlateFrame, "bottomright", 0, 0)
				
			--health bar
				-- ensure that we are using the configured size, as it will be automatically scaled
				healthBar:ClearAllPoints()
				PixelUtil.SetPoint (healthBar, "center", unitFrame, "center", profile.global_offset_x, profile.global_offset_y)
				PixelUtil.SetSize (healthBar, healthBarWidth, healthBarHeight)
		--end of patch
			--update scale
			Plater.UpdateUIParentScale (plateFrame)
		else
			--unit frame - is set to be the same size as the plateFrame
				unitFrame:ClearAllPoints()
				--unitFrame:SetAllPoints()
				--using the same setpoint pattern on both nameplate parent types to make easy the frameshake to handle the points
				unitFrame:SetPoint ("topleft", unitFrame.PlateFrame, "topleft", 0, 0)
				unitFrame:SetPoint ("bottomright", unitFrame.PlateFrame, "bottomright", 0, 0)
			
			--health bar
				--this calculates the health bar anchor points
				--it will always be placed in the center of the nameplate main frame attached with two anchor points
				local xOffSet = (plateFrame:GetWidth() - (healthBarWidth * unitFrame.nameplateScaleAdjust)) / 2
				local yOffSet = (plateFrame:GetHeight() - (healthBarHeight * unitFrame.nameplateScaleAdjust)) / 2
				
				healthBar:SetScale(1/unitFrame.nameplateScaleAdjust)
				
				healthBar:ClearAllPoints()
				PixelUtil.SetPoint (healthBar, "topleft", unitFrame, "topleft", xOffSet + profile.global_offset_x, -yOffSet + profile.global_offset_y)
				PixelUtil.SetPoint (healthBar, "bottomright", unitFrame, "bottomright", -xOffSet + profile.global_offset_x, yOffSet + profile.global_offset_y)
		end
		
		--execute indicator
			healthBar.healthCutOff:SetSize (healthBarHeight, healthBarHeight)
			healthBar.executeRange:SetHeight (healthBarHeight)
		
		--cast bar - is set by default below the healthbar
			castBar:ClearAllPoints()
			PixelUtil.SetPoint (castBar, "topleft", healthBar, "bottomleft", castBarOffSetXRel + castBarOffSetX, castBarOffSetY)
			PixelUtil.SetPoint (castBar, "topright", healthBar, "bottomright", -castBarOffSetXRel + castBarOffSetX, castBarOffSetY)
			PixelUtil.SetWidth (castBar, castBarWidth)
			PixelUtil.SetHeight (castBar, castBarHeight)
			--PixelUtil.SetSize (castBar.BorderShield, castBarHeight * 1.4, castBarHeight * 1.4)
			PixelUtil.SetSize (castBar.Spark, profile.cast_statusbar_spark_width, castBarHeight)
			castBar.Spark:SetAlpha (profile.cast_statusbar_spark_alpha)
			Plater.UpdateCastbarIcon(castBar)

			castBar._points = {{"topleft", healthBar, "bottomleft", castBarOffSetXRel + castBarOffSetX, castBarOffSetY},
			{"topright", healthBar, "bottomright", -castBarOffSetXRel + castBarOffSetX, castBarOffSetY}}

		--power bar
			powerBar:ClearAllPoints()
			PixelUtil.SetPoint (powerBar, "topleft", healthBar, "bottomleft", powerBarOffSetX, powerBarOffSetY)
			PixelUtil.SetPoint (powerBar, "topright", healthBar, "bottomright", -powerBarOffSetX, powerBarOffSetY)
			PixelUtil.SetSize (powerBar, powerBarWidth, powerBarHeight)
			
			--power bar are hidden by default, show it if there's a custom size for it
			if (unitFrame.customPowerBarWidth and unitFrame.customPowerBarHeight) then
				powerBar:SetUnit (unitFrame.unit)
			end
			
		--aura frame
			--plateConfigs.buff_frame_y_offset is the offset from the actor type, e.g. enemy npc
			local bf1Anchor = Plater.db.profile.aura_frame1_anchor
			Plater.SetAnchor (buffFrame1, {side = bf1Anchor.side, x = bf1Anchor.x, y = bf1Anchor.y + plateConfigs.buff_frame_y_offset}, unitFrame.healthBar, (Plater.db.profile.aura_grow_direction or 2) == 2)
			
			
			local bf2Anchor = Plater.db.profile.aura_frame2_anchor
			Plater.SetAnchor (buffFrame2, {side = bf2Anchor.side, x = bf2Anchor.x, y = bf2Anchor.y + plateConfigs.buff_frame_y_offset}, unitFrame.healthBar, (Plater.db.profile.aura2_grow_direction or 2) == 2)
			
		if (Plater.db.profile.show_health_prediction or Plater.db.profile.show_shield_prediction) and healthBar.displayedUnit then
			healthBar:UpdateHealPrediction() -- ensure health prediction is updated properly
		end
		
		Plater.UpdateUnitName (plateFrame)
		
		Plater.EndLogPerformanceCore("Plater-Core", "Update", "UpdatePlateSize")
	end
	
	--debug function to print the size of the anchor for each aura container
	function Plater.DebugAuraAnchor()
		local profile = Plater.db.profile
		--get the config for this actor type
		local plateConfigs = DB_PLATE_CONFIG ["enemynpc"]
		print ("DB_PLATE_CONFIG [enemynpc].buff_frame_y_offset:", plateConfigs.buff_frame_y_offset)
		
	end
	
	--show the background of the clickable aura, this is also shown when changing the clickable area
	function Plater.SetPlateBackground (plateFrame)
		plateFrame.unitFrame:SetBackdrop ({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16, edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1})
		plateFrame.unitFrame:SetBackdropColor (0, 0, 0, 0.5)
		plateFrame.unitFrame:SetBackdropBorderColor (0, 0, 0, 1)
	end

	local shutdown_platesize_debug = function (timer)
		for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
		
			Plater.HideClickSpace (plateFrame)
			
			if (Plater.db.profile.click_space_always_show) then
				Plater.SetPlateBackground (plateFrame)
			else
				plateFrame.unitFrame:SetBackdrop (nil)
			end
		end
		
		Plater.PlateSizeDebugTimer = nil
	end

	local re_UpdatePlateClickSpace = function()
		Plater.UpdatePlateClickSpace()
	end

	--debug to show the clickable area when adjusting the click space
	function Plater.ShowClickSpace (plateFrame)
		plateFrame.debugAreaText:Show()
		plateFrame.debugAreaTexture:Show()
	end
	function Plater.HideClickSpace (plateFrame)
		plateFrame.debugAreaText:Hide()
		plateFrame.debugAreaTexture:Hide()
	end
	
	-- default blizzard plate shenanigans
	function Plater.UpdateBaseNameplateOptions()
		if GetCVarBool ("nameplateShowOnlyNames") or Plater.db.profile.saved_cvars.nameplateShowOnlyNames == "1" then
			TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "hideHealthbar")
			TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "hideCastbar")
			TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "colorNameBySelection")
			TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "colorNameWithExtendedColors")
			TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "hideHealthbar")
			TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "hideCastbar")
			TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "colorNameBySelection")
			TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "colorNameWithExtendedColors")
			TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "hideHealthbar")
			TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "hideCastbar")
			TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "colorNameBySelection")
			TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "colorNameWithExtendedColors")
			
			TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "showLevel")
			TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "showLevel")
			TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "showLevel")
		else
			TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "hideHealthbar")
			TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "hideCastbar")
			TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "hideHealthbar")
			TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "hideCastbar")
			TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "hideHealthbar")
			TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "hideCastbar")
			
			
			if not IS_WOW_PROJECT_MAINLINE then
				--other defaults
				TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "colorNameBySelection")
				TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "colorNameWithExtendedColors")
				TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "colorNameBySelection")
				TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "colorNameWithExtendedColors")
				TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "colorNameBySelection")
				TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "colorNameWithExtendedColors")
				
				TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "showLevel")
				TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "showLevel")
				TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "showLevel")
			end

		end
		if not IS_WOW_PROJECT_MAINLINE then
			for _, plateFrame in pairs(C_NamePlate.GetNamePlates(true)) do
				if (plateFrame) then
					if GetCVarBool ("nameplateShowOnlyNames") or Plater.db.profile.saved_cvars.nameplateShowOnlyNames == "1" then
						TextureLoadingGroupMixin.RemoveTexture({ textures = plateFrame.UnitFrame.CastBar }, "showCastbar")
					else
						TextureLoadingGroupMixin.AddTexture({ textures = plateFrame.UnitFrame.CastBar }, "showCastbar")
					end
				end
			end
		end
	end
	
	-- ~platesize
	function Plater.UpdatePlateClickSpace (needReorder, isDebug) --private
		if (not Plater.CanChangePlateSize()) then
			return C_Timer.After (1, re_UpdatePlateClickSpace)
		end
		
		Plater.StartLogPerformanceCore("Plater-Core", "Update", "UpdatePlateClickSpace")
		
		local width, height = Plater.db.profile.click_space_friendly[1], Plater.db.profile.click_space_friendly[2]
		C_NamePlate.SetNamePlateFriendlySize (width, height) --classic: {132, 32}, retail: {110, 45},
		
		local width, height = Plater.db.profile.click_space[1], Plater.db.profile.click_space[2]
		C_NamePlate.SetNamePlateEnemySize (width, height) --classic: {132, 32}, retail: {110, 45},
		
		--C_NamePlate.SetNamePlateSelfPreferredClickInsets (0, 0, 0, 0)
		--C_NamePlate.SetNamePlateFriendlyPreferredClickInsets (0, 0, 0, 0)
		--C_NamePlate.SetNamePlateEnemyPreferredClickInsets (0, 0, 0, 0)
		
		C_NamePlate.SetNamePlateFriendlyClickThrough (Plater.db.profile.plate_config.friendlyplayer.click_through) 
		
		if (isDebug and not Plater.db.profile.click_space_always_show) then
			for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
				if not plateFrame.IsSelf and plateFrame.unitFrame.PlaterOnScreen then
					Plater.ShowClickSpace (plateFrame)
					
					if (Plater.PlateSizeDebugTimer and not Plater.PlateSizeDebugTimer._cancelled) then
						Plater.PlateSizeDebugTimer:Cancel()
					end
				end
			end
			if (not Plater.PlateSizeDebugTimer) then
				Plater:Msg ("showing the clickable area for test purposes.")
			end
			Plater.PlateSizeDebugTimer = C_Timer.NewTimer (3, shutdown_platesize_debug)
		end
		
		if (needReorder) then
			for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
				Plater.UpdatePlateFrame (plateFrame, plateFrame.actorType)
			end
		end
		
		Plater.EndLogPerformanceCore("Plater-Core", "Update", "UpdatePlateClickSpace")
	end
	
	function Plater.ForceTickOnAllNameplates() --private
		for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
			if plateFrame.unitFrame.PlaterOnScreen then
				Plater.NameplateTick (plateFrame.OnTickFrame, 10) --GetWorldDeltaSeconds()
			end
		end
	end
	
	--FPS meter to spread NameplateTick evenly
	Plater.FPSData = {
		startTime = GetTime(),
		platesUpdatedThisFrame = 0,
		platesToUpdatePerFrame = 40,
		frames = 1,
		curFPS = 1,
	}
	
	function Plater.EveryFrameFPSCheck()
		-- calculate every .25sec
		local curTime = GetTime()
		local curFPSData = Plater.FPSData
		if (curFPSData.startTime + 0.25) < curTime then
			curFPSData.curFPS = max(curFPSData.frames / (curTime - curFPSData.startTime), 1)
			curFPSData.platesToUpdatePerFrame = math.ceil(NUM_NAMEPLATES_ON_SCREEN / DB_TICK_THROTTLE / curFPSData.curFPS)
			
			--ViragDevTool_AddData({curFPSData=curFPSData, NUM_NAMEPLATES_ON_SCREEN = NUM_NAMEPLATES_ON_SCREEN}, "Plater_FPS")
			
			curFPSData.frames = 0
			curFPSData.startTime = curTime
		else
			curFPSData.frames = curFPSData.frames + 1
		end
		
		--ViragDevTool_AddData(curFPSData.platesUpdatedThisFrame, "platesUpdatedThisFrame")
		curFPSData.platesUpdatedThisFrame = 0
		
		C_Timer.After( 0, Plater.EveryFrameFPSCheck )
	end
	C_Timer.After( 0, Plater.EveryFrameFPSCheck )
	
	-- ~ontick ~onupdate ~tick
	function Plater.NameplateTick (tickFrame, deltaTime) --private
		Plater.StartLogPerformanceCore("Plater-Core", "Update", "NameplateTick")

		tickFrame.ThrottleUpdate = (tickFrame.ThrottleUpdate or 0) - deltaTime
		local unitFrame = tickFrame.unitFrame
		local healthBar = unitFrame.healthBar
		local profile = Plater.db.profile
		
		--throttle updates, things on this block update with the interval set in the advanced tab
		local shouldUpdate = tickFrame.ThrottleUpdate < 0
		local curFPSData = Plater.FPSData
		if shouldUpdate and not ((1.5 * DB_TICK_THROTTLE + tickFrame.ThrottleUpdate) < 0) then --ensure updates are not posponed indefinetely
			if curFPSData.platesUpdatedThisFrame >= curFPSData.platesToUpdatePerFrame then
				shouldUpdate = false
			end
		end

		if (shouldUpdate) then
			Plater.StartLogPerformanceCore("Plater-Core", "Update", "NameplateTick-Full")
		
			curFPSData.platesUpdatedThisFrame = curFPSData.platesUpdatedThisFrame + 1
			
			--make the db path smaller for performance
			local actorTypeDBConfig = DB_PLATE_CONFIG [tickFrame.actorType]
			
			--health cutoff (execute range) - don't show if the nameplate is the personal bar
			if (DB_USE_HEALTHCUTOFF and not unitFrame.IsSelf and not unitFrame.PlayerCannotAttack) then
				local healthPercent = (healthBar.currentHealth or 1) / (healthBar.currentHealthMax or 1)
				if (healthPercent < DB_HEALTHCUTOFF_AT) then
					if (not healthBar.healthCutOff:IsShown() or healthBar.healthCutOff.isUpper) then
						healthBar.healthCutOff.isUpper = false
						healthBar.healthCutOff.isLower = true
						healthBar.healthCutOff:ClearAllPoints()
						healthBar.healthCutOff:SetSize (healthBar:GetHeight(), healthBar:GetHeight())
						healthBar.healthCutOff:SetPoint ("center", healthBar, "left", healthBar:GetWidth() * DB_HEALTHCUTOFF_AT, 0)
						
						if (not profile.health_cutoff_hide_divisor) then
							healthBar.healthCutOff:Show()
							healthBar.healthCutOff.ShowAnimation:Play()
						else
							healthBar.healthCutOff:Show()
							healthBar.healthCutOff:SetAlpha (0)
						end

						healthBar.executeRange:Show()
						healthBar.executeRange:SetTexCoord (0, DB_HEALTHCUTOFF_AT, 0, 1)
						healthBar.executeRange:SetAlpha (0.2)
						healthBar.executeRange:SetVertexColor (.3, .3, .3)
						healthBar.executeRange:SetHeight (healthBar:GetHeight())
						healthBar.executeRange:SetPoint ("right", healthBar.healthCutOff, "center")
						healthBar.executeRange:SetPoint ("left", healthBar, "left")
						
						if (profile.health_cutoff_extra_glow) then
							healthBar.ExecuteGlowUp.ShowAnimation:Play()
							healthBar.ExecuteGlowDown.ShowAnimation:Play()
						end
					end
					
					unitFrame.InExecuteRange = true
				elseif (healthPercent > DB_HEALTHCUTOFF_AT_UPPER and healthPercent < 0.999) then
					if (not healthBar.healthCutOff:IsShown() or healthBar.healthCutOff.isLower) then
						healthBar.healthCutOff.isUpper = true
						healthBar.healthCutOff.isLower = false
						healthBar.healthCutOff:ClearAllPoints()
						healthBar.healthCutOff:SetSize (healthBar:GetHeight(), healthBar:GetHeight())
						healthBar.healthCutOff:SetPoint ("center", healthBar, "right", - (healthBar:GetWidth() * (1-DB_HEALTHCUTOFF_AT_UPPER)), 0)
						
						if (not profile.health_cutoff_hide_divisor) then
							healthBar.healthCutOff:Show()
							healthBar.healthCutOff.ShowAnimation:Play()
						else
							healthBar.healthCutOff:Show()
							healthBar.healthCutOff:SetAlpha (0)
						end

						healthBar.executeRange:Show()
						healthBar.executeRange:SetTexCoord (0, 1-DB_HEALTHCUTOFF_AT_UPPER, 0, 1)
						healthBar.executeRange:SetAlpha (0.2)
						healthBar.executeRange:SetVertexColor (.3, .3, .3)
						healthBar.executeRange:SetHeight (healthBar:GetHeight())
						healthBar.executeRange:SetPoint ("left", healthBar.healthCutOff, "center")
						healthBar.executeRange:SetPoint ("right", healthBar, "right")
						
						if (profile.health_cutoff_extra_glow) then
							healthBar.ExecuteGlowUp.ShowAnimation:Play()
							healthBar.ExecuteGlowDown.ShowAnimation:Play()
						end
					end
					
					unitFrame.InExecuteRange = true
				else
					healthBar.healthCutOff:Hide()
					healthBar.executeRange:Hide()
					healthBar.ExecuteGlowUp:Hide()
					healthBar.ExecuteGlowDown:Hide()
					
					unitFrame.InExecuteRange = false
				end
			end
			
			local isSoftInteract = UnitIsUnit(tickFrame.unit, "softinteract")
			unitFrame.isSoftInteract = isSoftInteract
			unitFrame.PlateFrame.isSoftInteract = isSoftInteract
			unitFrame.isSoftInteractObject = isSoftInteract and (unitFrame.PlateFrame.isObject or unitFrame.isObject)
			unitFrame.PlateFrame.isSoftInteractObject = isSoftInteract and (unitFrame.PlateFrame.isObject or unitFrame.isObject)
			
			local isLooseTarget = IsTargetLoose()
			unitFrame.isLooseTarget = isLooseTarget
			unitFrame.PlateFrame.isLooseTarget = isLooseTarget
			local isSoftEnemy = UnitIsUnit(tickFrame.unit, "softenemy")
			unitFrame.isSoftEnemy = isSoftEnemy
			unitFrame.PlateFrame.isSoftEnemy = isSoftEnemy
			local isSoftFriend = UnitIsUnit(tickFrame.unit, "softfriend")
			unitFrame.isSoftFriend = isSoftFriend
			unitFrame.PlateFrame.isSoftFriend = isSoftFriend
			
			local wasCombat = unitFrame.InCombat
			unitFrame.InCombat = UnitAffectingCombat (tickFrame.unit) or (Plater.ForceInCombatUnits[unitFrame [MEMBER_NPCID]] and PLAYER_IN_COMBAT) or false
			if wasCombat ~= unitFrame.InCombat then
				Plater.UpdatePlateSize (tickFrame.PlateFrame)
			end
			
			--check aggro if is in combat
			if (PLAYER_IN_COMBAT) then
				if (unitFrame.CanCheckAggro) then
					Plater.UpdateNameplateThread (unitFrame)
				end
			end
			
			--perform a range check
			Plater.CheckRange (tickFrame.PlateFrame, (deltaTime == 999))
			
			--if not in combat, check if can show the percent health out of combat
			if (actorTypeDBConfig.percent_text_enabled and (((profile.use_player_combat_state and PLAYER_IN_COMBAT or unitFrame.InCombat)) or actorTypeDBConfig.percent_text_ooc)) then
				Plater.UpdateLifePercentText (healthBar, unitFrame.unit, actorTypeDBConfig.percent_show_health, actorTypeDBConfig.percent_show_percent, actorTypeDBConfig.percent_text_show_decimals)
				healthBar.lifePercent:Show()
			else
				healthBar.lifePercent:Hide()
			end

			if (not unitFrame.DenyColorChange) then --tagged from a script
				--if the unit tapped? (gray color)
				if (Plater.IsUnitTapDenied (tickFrame.unit)) then
					Plater.ChangeHealthBarColor_Internal (healthBar, unpack (profile.tap_denied_color))
				end
			end

			--the color overrider for unitIDs goes after the threat check and before the aura, since auras can run scripts and scripts have priority on setting colors
			if (DB_UNITCOLOR_CACHE [unitFrame [MEMBER_NPCID]]) then
				Plater.ChangeHealthBarColor_Internal (healthBar, unpack (DB_UNITCOLOR_CACHE [unitFrame [MEMBER_NPCID]]))
				unitFrame.UsingCustomColor = true --exposed to scripts
			end
			
			--update buffs and debuffs
			if (DB_AURA_ENABLED) then --should update only when the heathbar is shown?
				--Plater.StartLogPerformanceCore("Plater-Core", "Update", "UpdateAuras")
				
				if (DB_TRACK_METHOD == 0x1) then --automatic
					if (tickFrame.actorType == ACTORTYPE_PLAYER) then
						--update auras on the personal bar
						Plater.UpdateAuras_Self_Automatic (tickFrame.BuffFrame, tickFrame.unit)
					else
						Plater.UpdateAuras_Automatic (tickFrame.BuffFrame, tickFrame.unit)
					end
				else
					--manual aura track
					Plater.UpdateAuras_Manual (tickFrame.BuffFrame, tickFrame.unit, tickFrame.actorType == ACTORTYPE_PLAYER)
				end
				
				--update the buff layout and alpha
				tickFrame.BuffFrame.unit = tickFrame.unit

				--ghost and extra auras only show in the debuff frame
				Plater.ShowGhostAuras(tickFrame.BuffFrame) 
				--check for expired extra auras
				platerInternal.ExtraAuras.ClearExpired()
				--update extra auras
				platerInternal.ExtraAuras.Show(tickFrame.BuffFrame)
				
				--align icons in the aura frame
				Plater.AlignAuraFrames (tickFrame.BuffFrame)
				--update the alignment on the second aura frame as well if enabled
				if (DB_AURA_SEPARATE_BUFFS) then
					Plater.AlignAuraFrames (tickFrame.BuffFrame.BuffFrame2)
				end
				
				Plater.RunScriptTriggersForAuraIcons (unitFrame)
				
				--tickFrame.BuffFrame:SetAlpha (DB_AURA_ALPHA)
				--tickFrame.BuffFrame2:SetAlpha (DB_AURA_ALPHA)
				
				--Plater.EndLogPerformanceCore("Plater-Core", "Update", "UpdateAuras")
			end
			-- update DBM and BigWigs nameplate auras
			Plater.UpdateBossModAuras(unitFrame)
			
			--set the delay to perform another update
			tickFrame.ThrottleUpdate = DB_TICK_THROTTLE * (unitFrame.isPerformanceUnit and 5 or 1)

			--check if the unit name or unit npcID has a script
			local globalScriptObject = SCRIPT_UNIT_TRIGGER_CACHE[tickFrame.PlateFrame [MEMBER_NAMELOWER]] or SCRIPT_UNIT_TRIGGER_CACHE[unitFrame [MEMBER_NPCID]]
			--check if this aura has a custom script
			if (globalScriptObject) then
				--stored information about scripts
				local scriptContainer = unitFrame:ScriptGetContainer()
				--get the info about this particularly script
				local scriptInfo = unitFrame:ScriptGetInfo(globalScriptObject, scriptContainer)
				
				local scriptEnv = scriptInfo.Env
				scriptEnv._UnitID = tickFrame.PlateFrame [MEMBER_UNITID]
				scriptEnv._NpcID = tickFrame.PlateFrame [MEMBER_NPCID]
				scriptEnv._UnitName = tickFrame.PlateFrame [MEMBER_NAME]
				scriptEnv._UnitGUID = tickFrame.PlateFrame [MEMBER_GUID]
				scriptEnv._HealthPercent = healthBar.CurrentHealth / healthBar.CurrentHealthMax * 100
		
				--run onupdate script
				unitFrame:ScriptRunOnUpdate(scriptInfo)
			end
			
			--scheduled name update
			if (unitFrame.ScheduleNameUpdate) then
				if (unitFrame.ActorType == ACTORTYPE_FRIENDLY_PLAYER) then
					tickFrame.PlateFrame.playerGuildName = GetGuildInfo (tickFrame.unit)
					Plater.UpdatePlateText (tickFrame.PlateFrame, DB_PLATE_CONFIG [unitFrame.ActorType], false)
				end
				
				Plater.UpdateUnitName (tickFrame.PlateFrame)
				unitFrame.ScheduleNameUpdate = nil
				
				--run hook
				if (HOOK_UNITNAME_UPDATE.ScriptAmount > 0) then
					for i = 1, HOOK_UNITNAME_UPDATE.ScriptAmount do
						local globalScriptObject = HOOK_UNITNAME_UPDATE [i]
						local scriptContainer = unitFrame:ScriptGetContainer()
						local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Name Updated")
						--run
						unitFrame:ScriptRunHook (scriptInfo, "Name Updated")
					end
				end
			end
			
			--hooks
			Plater.TriggerNameplateUpdatedEvent(unitFrame)
			
			--details! integration
			if (IS_USING_DETAILS_INTEGRATION and not tickFrame.PlateFrame.IsSelf and PLAYER_IN_COMBAT and unitFrame.InCombat) then
				local detailsPlaterConfig = Details.plater

				--> current damage taken from all sources
				if (detailsPlaterConfig.realtime_dps_enabled) then
					local unitDamageTable = DetailsPlaterFrame.DamageTaken [tickFrame.PlateFrame [MEMBER_GUID]]
					if (unitDamageTable) then
						local damage = unitDamageTable.CurrentDamage or 0
						
						local textString = healthBar.DetailsRealTime
						textString:SetText (Plater.FormatNumber (damage / PLATER_DPS_SAMPLE_SIZE))
					else
						local textString = healthBar.DetailsRealTime
						textString:SetText ("")
					end
				end
				
				if (detailsPlaterConfig.realtime_dps_player_enabled) then
					local unitDamageTable = DetailsPlaterFrame.DamageTaken [tickFrame.PlateFrame [MEMBER_GUID]]
					if (unitDamageTable) then
						local damage = unitDamageTable.CurrentDamageFromPlayer or 0
						
						local textString = healthBar.DetailsRealTimeFromPlayer
						textString:SetText (Plater.FormatNumber (damage / PLATER_DPS_SAMPLE_SIZE))
					else
						local textString = healthBar.DetailsRealTimeFromPlayer
						textString:SetText ("")
					end

				end
				
				if (detailsPlaterConfig.damage_taken_enabled) then
					local unitDamageTable = DetailsPlaterFrame.DamageTaken [tickFrame.PlateFrame [MEMBER_GUID]]
					if (unitDamageTable) then
						local damage = unitDamageTable.TotalDamageTaken or 0
						
						local textString = healthBar.DetailsDamageTaken
						textString:SetText (Plater.FormatNumber (damage))
					else
						local textString = healthBar.DetailsDamageTaken
						textString:SetText ("")
					end
				end
			end
			
			--check shield ~shield
			if (IS_WOW_PROJECT_MAINLINE) then
				if (profile.indicator_shield) then
					local amountAbsorb = UnitGetTotalAbsorbs(tickFrame.PlateFrame[MEMBER_UNITID])
					if (amountAbsorb and amountAbsorb > 0) then
						--update the total amount on the shield indicator
						if (not healthBar.shieldIndicator.shieldTotal) then
							healthBar.shieldIndicator.shieldTotal = amountAbsorb
						else
							if (amountAbsorb > healthBar.shieldIndicator.shieldTotal) then
								healthBar.shieldIndicator.shieldTotal = amountAbsorb
							end
						end

						healthBar.shieldIndicator:Show()

						local percent = amountAbsorb / healthBar.shieldIndicator.shieldTotal
						local width = healthBar:GetWidth() * percent
						healthBar.shieldIndicator:SetWidth(width)
					else
						--hide the shield bar is currently shown
						if (healthBar.shieldIndicator:IsShown()) then
							healthBar.shieldIndicator:Hide()
							healthBar.shieldIndicator.shieldTotal = nil
						end
					end
				end
			end
			
			if (unitFrame.castBar:IsShown()) then
				Plater.CastBarOnTick_Hook(unitFrame.castBar, 999)
			end
			
			Plater.EndLogPerformanceCore("Plater-Core", "Update", "NameplateTick-Full")

			--end of throttled updates
		end

		--OnTick updates
			--smooth color transition ~lerpcolor
			if (DB_LERP_COLOR and not unitFrame.isPerformanceUnit) then
				local currentR, currentG, currentB = healthBar.barTexture:GetVertexColor()
				local r, g, b = DF:LerpLinearColor (deltaTime, DB_LERP_COLOR_SPEED, currentR, currentG, currentB, healthBar.R or currentR, healthBar.G or currentG, healthBar.B or currentB)
				healthBar.barTexture:SetVertexColor (r, g, b)
			end
			
			--animate health bar ~animation
			if (DB_DO_ANIMATIONS and not unitFrame.isPerformanceUnit) then
				if (healthBar.IsAnimating) then
					healthBar.AnimateFunc (healthBar, deltaTime)
				end
			end
			
		Plater.EndLogPerformanceCore("Plater-Core", "Update", "NameplateTick")
	end
	
	local set_aggro_color = function (self, r, g, b, a) --self = unitName
		if (DB_AGGRO_CHANGE_HEALTHBAR_COLOR) then
			if (not self.DenyColorChange) then --tagged from a script
				Plater.ChangeHealthBarColor_Internal (self.healthBar, r, g, b, a)
			end
		end
		
		if (DB_AGGRO_CHANGE_BORDER_COLOR) then
			Plater.ForceChangeBorderColor (self, r, g, b)
		end
		
		if (DB_AGGRO_CHANGE_NAME_COLOR) then
			self.unitName:SetTextColor (r, g, b)
		end
	end

	--aggro threat ~aggro ~threat
	function Plater.UpdateNameplateThread (self) --self = unitFrame

		--make sure there's a unitID in the unit frame
		if (not self.displayedUnit or not self.CanCheckAggro) then
			return
		end
		
		Plater.StartLogPerformanceCore("Plater-Core", "Update", "UpdateNameplateThreat")
		
		local profile = Plater.db.profile
		
		local isTanking, threatStatus, threatpct, threatrawpct, threatValue = UnitDetailedThreatSituation ("player", self.displayedUnit)
		
		--expose all threat situation to scripts
		self.namePlateThreatIsTanking = isTanking
		self.namePlateThreatStatus = threatStatus
		self.namePlateThreatPercent = threatpct or 0
		self.namePlateThreatRawPercent = threatrawpct or 0
		self.namePlateThreatValue = threatValue or 0
		-- (3 = securely tanking, 2 = insecurely tanking, 1 = not tanking but higher threat than tank, 0 = not tanking and lower threat than tank)
		self.namePlateThreatOffTankIsTanking = false
		self.namePlateThreatOffTankName = nil
		
		self.aggroGlowUpper:Hide()
		self.aggroGlowLower:Hide()
		
		--player is a tank
		if (Plater.PlayerIsTank) then
			--and isn't tanking the unit
			if (not isTanking) then
				--is the player in combat?
				if (self.InCombat) then
					--is the player in a raid group?
					if (IsInRaid()) then
						--check if another tank is effectively tanking
						--as the other tank may not be targeted due to spell-casts, we need to check the threat situation for tanks
						local unitOffTank = nil
						local otherIsTanking, otherThreatStatus, otherThreatpct
						--loop on all tanks in the group (tank_cache is updated on entering combat or when group roster is updated) 
						for tank, _ in pairs(TANK_CACHE) do
							if UnitExists(tank) and not UnitIsUnit("player", tank) then
								otherIsTanking, otherThreatStatus, otherThreatpct = UnitDetailedThreatSituation (tank, self.displayedUnit)
								if otherIsTanking then
									unitOffTank = tank
									break
								end
							end
						end

						--another tank is tanking the unit
						if (unitOffTank) then
							self.namePlateThreatOffTankIsTanking = true
							self.namePlateThreatOffTankName = unitOffTank
							--as the unit is being tanked by the off-tank, check if the player it self which is the other tank is about to accidently pull aggro just by hitting the mob
							if (threatpct and otherThreatpct) then
								--threatpct = player threat on the mob
								--otherThreatpct = the aggro on the tank tanking the unit
								if ((threatpct + 10) - otherThreatpct > 0) then
									set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.pulling_from_tank))
								else
									set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.anothertank))
								end
							else
								set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.anothertank))
							end
						else
							--no tank is tanking this unit
							set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.noaggro))
						end
						
						if (self.PlateFrame [MEMBER_NOCOMBAT]) then
							self.PlateFrame [MEMBER_NOCOMBAT] = nil
							Plater.CheckRange (self.PlateFrame, true)
						end
					else
						--player isn't tanking this unit
						set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.noaggro))
						
						if (self.PlateFrame [MEMBER_NOCOMBAT]) then
							self.PlateFrame [MEMBER_NOCOMBAT] = nil
							Plater.CheckRange (self.PlateFrame, true)
						end
					end
				else
					--if isn't a quest mob
					if (not self.PlateFrame [MEMBER_QUEST]) then
						--there's no aggro, isn't in combat, isn't a quest mob
						if (self [MEMBER_REACTION] == 4) then
							--do nothing if the mob is neutral
							--set_aggro_color (self, 1, 1, 0) --ticket #185
						else
							set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.nocombat))
						end
						
						if (DB_NOT_COMBAT_ALPHA_ENABLED) then
							self.PlateFrame [MEMBER_NOCOMBAT] = true
							self:SetAlpha (profile.not_affecting_combat_alpha)
						end
					end
				end
			else
				--The player is tanking and:
				if (threatStatus == 3) then --is tanking safely
					set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.aggro))
					
				elseif (threatStatus == 2) then --is tanking with risk of aggro loss
					set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.pulling))
					if profile.show_aggro_glow then
						self.aggroGlowUpper:Show()
						self.aggroGlowLower:Show()
					end
					
				else --not tanking
					set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.noaggro))

				end
				if (self.PlateFrame [MEMBER_NOCOMBAT]) then
					self.PlateFrame [MEMBER_NOCOMBAT] = nil
					Plater.CheckRange (self.PlateFrame, true)

				end
			end
		else

			--dps
			if (isTanking) then
				--the player is tanking as dps
				if profile.dps.use_aggro_solo and not IsInGroup() then
					set_aggro_color (self, unpack (DB_AGGRO_DPS_COLORS.solo))
				else
					set_aggro_color (self, unpack (DB_AGGRO_DPS_COLORS.aggro))
				end
				if (not self.PlateFrame.playerHasAggro and IS_IN_INSTANCE) then
					if profile.show_aggro_flash then
						self.PlateFrame.PlayBodyFlash ("-AGGRO-")
					end
				end
				self.PlateFrame.playerHasAggro = true
				
				if (self.PlateFrame [MEMBER_NOCOMBAT]) then
					self.PlateFrame [MEMBER_NOCOMBAT] = nil
					Plater.CheckRange (self.PlateFrame, true)
				end
			else 	
				if (threatStatus == nil) then
					self.PlateFrame.playerHasAggro = false
					
					--> unit is in combat?
					if (self.InCombat) then
						set_aggro_color (self, unpack (DB_AGGRO_DPS_COLORS.noaggro))
						self.PlateFrame.playerHasAggro = false
						
						if (self.PlateFrame [MEMBER_NOCOMBAT]) then
							self.PlateFrame [MEMBER_NOCOMBAT] = nil
							Plater.CheckRange (self.PlateFrame, true)
						end
					else
						--if isn't a quest mob
						if (not self.PlateFrame [MEMBER_QUEST]) then
							--there's no aggro, isn't in combat, isn't a quest mob
							if (self [MEMBER_REACTION] == 4) then --ticket #185
								--do nothing if the mob is neutral
								--set_aggro_color (self, 1, 1, 0)
							else
								set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.nocombat))
							end
							
							if (DB_NOT_COMBAT_ALPHA_ENABLED) then --not self.PlateFrame [MEMBER_NOCOMBAT] and 
								self.PlateFrame [MEMBER_NOCOMBAT] = true
								self:SetAlpha (profile.not_affecting_combat_alpha)
							end
						end
						
					end
				else
					if (threatStatus == 3) then --player is tanking the mob as dps
						if profile.dps.use_aggro_solo and not IsInGroup() then
							set_aggro_color (self, unpack (DB_AGGRO_DPS_COLORS.solo))
						else
							set_aggro_color (self, unpack (DB_AGGRO_DPS_COLORS.aggro))
						end
						if (not self.PlateFrame.playerHasAggro and IS_IN_INSTANCE) then
							if profile.show_aggro_flash then
								self.PlateFrame.PlayBodyFlash ("-AGGRO-")
							end
						end
						self.PlateFrame.playerHasAggro = true
						
					elseif (threatStatus == 2) then --player is tanking the mob with low aggro
						if profile.dps.use_aggro_solo and not IsInGroup() then
							set_aggro_color (self, unpack (DB_AGGRO_DPS_COLORS.solo))
						else
							set_aggro_color (self, unpack (DB_AGGRO_DPS_COLORS.aggro))
						end
						self.PlateFrame.playerHasAggro = true
						
					else --the unit isn't attacking the player based on the threat situation
					
						--which color the use in the nameplate based on the threat status
						--this color can be overritten by the 'no tank aggro' check
						local colorToUse
						if (threatStatus == 1) then --player is almost aggroing the mob
							--show aggro warning indicators
							if profile.show_aggro_glow then
								self.aggroGlowUpper:Show()
								self.aggroGlowLower:Show()
							end
							if profile.dps.use_aggro_solo and not IsInGroup() then
								colorToUse = DB_AGGRO_DPS_COLORS.solo
							else
								colorToUse = DB_AGGRO_DPS_COLORS.pulling
							end
							
						elseif (threatStatus == 0) then
							colorToUse = DB_AGGRO_DPS_COLORS.noaggro
							
						end
					
						if (Plater.ZoneInstanceType == "party" or Plater.ZoneInstanceType == "raid") then
							--check if can check for no tank aggro
							if (DB_AGGRO_CAN_CHECK_NOTANKAGGRO) then
								local unitTarget = UnitName (self.targetUnitID)
								--check if the unit isn't attacking a tank comparing the target name with tank names
								if (not TANK_CACHE [unitTarget]) then
								
									--check if this isn't a false positive where the mob target another unit to cast a spell
									local hasTankAggro = false
									for tankName, _ in pairs (TANK_CACHE) do
										if UnitExists(tankName) then
											local threatStatus = UnitThreatSituation (tankName, self.displayedUnit)
											if (threatStatus and threatStatus >= 2) then
												--a tank has aggro on this unit, it is a false positive
												hasTankAggro = true
												break
											end
										end
									end
									
									if (not hasTankAggro) then
										--the unit isn't targeting a tank and no tank in the group has threat status of 2 or more, the unit might be attacking a dps or healer
										set_aggro_color (self, unpack (DB_AGGRO_DPS_COLORS.notontank))
									else
										--the unit isn't targeting a tank but a tank in the group has aggro on this unit
										set_aggro_color (self, unpack (colorToUse))
									end
								else
									--the unit is targeting a tank
									set_aggro_color (self, unpack (colorToUse))
								end
							else
								--isn't checking for 'no tank aggro'
								set_aggro_color (self, unpack (colorToUse))
							end
						else
							--player isn't inside a dungeon or raid
							set_aggro_color (self, unpack (colorToUse))
						end
						
						self.PlateFrame.playerHasAggro = false
					end
					
					if (self.PlateFrame [MEMBER_NOCOMBAT]) then
						self.PlateFrame [MEMBER_NOCOMBAT] = nil
						Plater.CheckRange (self.PlateFrame, true)
					end
				end
			end
		end
		
		Plater.EndLogPerformanceCore("Plater-Core", "Update", "UpdateNameplateThreat")
	end	
	
	function Plater.UpdateSoftInteractTarget(plateFrame, updateText)
		local unitFrame = plateFrame.unitFrame
		local unitID = plateFrame [MEMBER_UNITID]
		
		local isSoftInteract = UnitIsUnit(unitID, "softinteract")
		local reaction = UnitReaction (unitID, "player")
		local isObject = (IS_WOW_PROJECT_MAINLINE and UnitIsGameObject(unitID)) or reaction == nil
		local isSoftInteractObject = isObject and isSoftInteract
		plateFrame.isSoftInteract = isSoftInteract
		unitFrame.isSoftInteract = isSoftInteract
		plateFrame.isObject = isObject
		unitFrame.isObject = isObject
		plateFrame.isSoftInteractObject = isSoftInteractObject
		unitFrame.isSoftInteractObject = isSoftInteractObject
		
		local isLooseTarget = IsTargetLoose()
		unitFrame.isLooseTarget = isLooseTarget
		unitFrame.PlateFrame.isLooseTarget = isLooseTarget
		local isSoftEnemy = UnitIsUnit(unitID, "softenemy")
		unitFrame.isSoftEnemy = isSoftEnemy
		unitFrame.PlateFrame.isSoftEnemy = isSoftEnemy
		local isSoftFriend = UnitIsUnit(unitID, "softfriend")
		unitFrame.isSoftFriend = isSoftFriend
		unitFrame.PlateFrame.isSoftFriend = isSoftFriend
		
		if plateFrame.IsNpcWithoutHealthBar and updateText then
			Plater.UpdatePlateText (plateFrame, DB_PLATE_CONFIG [plateFrame.unitFrame.ActorType], false)
		end
		
		if isSoftInteract and Plater.db.profile.show_softinteract_icons then
			--re-anchor
			Plater.SetAnchor(unitFrame.softInteractIcon, unitFrame.softInteractIcon.anchor or { side = 8, x = 0, y = 18, }, plateFrame)
			
			local size = unitFrame.softInteractIcon.size or 24
			unitFrame.softInteractIconFrame:SetSize(size, size)
			unitFrame.softInteractIcon:SetDesaturated(false)
			unitFrame.softInteractIcon:SetIgnoreParentAlpha(true)
			unitFrame.softInteractIcon:SetSize(size, size)
			unitFrame.softInteractIconFrame:Show()
			unitFrame.softInteractIcon:Show()
			
			local hasTexture =  SetUnitCursorTexture(unitFrame.softInteractIcon, plateFrame [MEMBER_UNITID], nil, true)
			if not hasTexture then
				unitFrame.softInteractIcon:SetTexture(136243)
			end
		else
			unitFrame.softInteractIconFrame:Hide()
		end
	end
	
	-- ~target ~selection
	function Plater.UpdateTarget (plateFrame) --private

		local profile = Plater.db.profile
		local unitFrame = plateFrame.unitFrame
		if UnitIsUnit (unitFrame [MEMBER_UNITID], "focus") then
			if profile.focus_indicator_enabled then
				--this is a rare call, no need to cache these values
				local texture = LibSharedMedia:Fetch ("statusbar", Plater.db.profile.focus_texture)
				plateFrame.FocusIndicator:SetTexture (texture)
				plateFrame.FocusIndicator:SetVertexColor (unpack (Plater.db.profile.focus_color))
				plateFrame.FocusIndicator:Show()
			end
			unitFrame.IsFocus = true
		else
			unitFrame.IsFocus = false
			plateFrame.FocusIndicator:Hide()
		end

		if (UnitIsUnit (unitFrame [MEMBER_UNITID], "target")) then
			plateFrame [MEMBER_TARGET] = true
			unitFrame [MEMBER_TARGET] = true
			
			--hide obscured texture
			plateFrame.Obscured:Hide()
			
			--target indicator
			Plater.UpdateTargetIndicator (plateFrame)
			
			--target highlight
			if (profile.target_highlight) then
				if (plateFrame.actorType ~= ACTORTYPE_FRIENDLY_PLAYER and plateFrame.actorType ~= ACTORTYPE_FRIENDLY_NPC and not plateFrame.PlayerCannotAttack and unitFrame.healthBar:IsShown()) then
					plateFrame.TargetNeonUp:Show()
					plateFrame.TargetNeonDown:Show()
				else
					plateFrame.TargetNeonUp:Hide()
					plateFrame.TargetNeonDown:Hide()
				end
				Plater.UpdateTargetHighlight (plateFrame) --neon
			else
				plateFrame.TargetNeonUp:Hide()
				plateFrame.TargetNeonDown:Hide()
			end
			
			if (not unitFrame.healthBar:IsShown()) then
				unitFrame.targetOverlayTexture:Hide()
			else
				unitFrame.targetOverlayTexture:Show()
			end
			
			
			Plater.UpdateUIParentTargetLevels (unitFrame)
			
			Plater.UpdateResourceFrame()
		else
			plateFrame.TargetNeonUp:Hide()
			plateFrame.TargetNeonDown:Hide()
			unitFrame.targetOverlayTexture:Hide()
			
			plateFrame [MEMBER_TARGET] = nil
			unitFrame [MEMBER_TARGET] = nil
			
			if (unitFrame.IsTarget or unitFrame.TargetTextures2Sides [1]:IsShown() or unitFrame.TargetTextures4Sides [1]:IsShown()) then
				for i = 1, 2 do
					unitFrame.TargetTextures2Sides [i]:Hide()
				end
				for i = 1, 4 do
					unitFrame.TargetTextures4Sides [i]:Hide()
				end
				
				unitFrame.IsTarget = false
			end
			
			if (DB_TARGET_SHADY_ENABLED and (not DB_TARGET_SHADY_COMBATONLY or (profile.use_player_combat_state and PLAYER_IN_COMBAT or unitFrame.InCombat)) and not plateFrame.IsSelf) then
				plateFrame.Obscured:Show()
				plateFrame.Obscured:SetAlpha (DB_TARGET_SHADY_ALPHA)
			else
				plateFrame.Obscured:Hide()
			end
			
			Plater.UpdateUIParentLevels (unitFrame)
		end

		Plater.CheckRange (plateFrame, true) --disabled on 2018-10-09 | enabled back on 2020-1-16

	end

	--called when the player targets a new unit, when focus changed or when a unit isn't in the screen any more
	function Plater.OnPlayerTargetChanged() --private
		Plater.PlayerCurrentTargetGUID = UnitGUID ("target")
		Plater.PlayerHasTarget = Plater.PlayerCurrentTargetGUID and true
		Plater.PlayerHasTargetNonSelf = Plater.PlayerHasTarget and Plater.PlayerCurrentTargetGUID ~= Plater.PlayerGUID and true
		Plater.PlayerCurrentFocusTargetGUID = UnitGUID ("focus")
		Plater.PlayerHasFocusTarget = Plater.PlayerCurrentFocusTargetGUID and true
		Plater.PlayerHasFocusTargetNonSelf = Plater.PlayerHasFocusTarget and Plater.PlayerCurrentFocusTargetGUID ~= Plater.PlayerGUID and true
		
		for index, plateFrame in ipairs (Plater.GetAllShownPlates()) do
			if plateFrame.unitFrame.PlaterOnScreen then
				Plater.UpdateTarget (plateFrame)
				
				--hooks
				if (HOOK_TARGET_CHANGED.ScriptAmount > 0) then
					for i = 1, HOOK_TARGET_CHANGED.ScriptAmount do
						local globalScriptObject = HOOK_TARGET_CHANGED [i]
						local unitFrame = plateFrame.unitFrame
						local scriptContainer = unitFrame:ScriptGetContainer()
						local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Target Changed")
						--run
						unitFrame:ScriptRunHook (scriptInfo, "Target Changed")
					end
				end
			end
		end

		Plater.Resources.UpdateResourceFramePosition() --~resource
	end

	function Plater.UpdateTargetHighlight (plateFrame)
		local healthBar = plateFrame.unitFrame.healthBar
		local profile = Plater.db.profile
		
		local alpha = profile.target_highlight_alpha
		local height = profile.target_highlight_height
		local color = profile.target_highlight_color
		local texture = profile.target_highlight_texture
		
		plateFrame.TargetNeonUp:SetVertexColor (unpack (color))
		plateFrame.TargetNeonUp:SetAlpha (alpha)
		plateFrame.TargetNeonUp:SetTexture (texture)
		plateFrame.TargetNeonUp:SetHeight (height)
		PixelUtil.SetPoint (plateFrame.TargetNeonUp, "bottomleft", healthBar, "topleft", 0, 0)
		PixelUtil.SetPoint (plateFrame.TargetNeonUp, "bottomright", healthBar, "topright", 0, 0)

		plateFrame.TargetNeonDown:SetVertexColor (unpack (color))
		plateFrame.TargetNeonDown:SetAlpha (alpha)
		plateFrame.TargetNeonDown:SetTexture (texture)
		plateFrame.TargetNeonDown:SetHeight (height)
		PixelUtil.SetPoint (plateFrame.TargetNeonDown, "topleft", healthBar, "bottomleft", 0, 0)
		PixelUtil.SetPoint (plateFrame.TargetNeonDown, "topright", healthBar, "bottomright", 0, 0)
	end

	function Plater.UpdateTargetIndicator (plateFrame)

		local healthBarHeight = plateFrame.unitFrame.healthBar:GetHeight()
		
		--if the height is lower than 4, just hide all indicators
		if (healthBarHeight < 4) then
			for i = 1, 2 do
				plateFrame.unitFrame.TargetTextures2Sides [i]:Hide()
			end
			for i = 1, 4 do
				plateFrame.unitFrame.TargetTextures4Sides [i]:Hide()
			end
			
			return
		end

		local preset = Plater.TargetIndicators [Plater.db.profile.target_indicator]
		if (not preset) then
			--use default indicator is the indicator isn't found
			preset = Plater.TargetIndicators ["Silver"]
		end
		
		local width, height = preset.width, preset.height
		local wscale, hscale = preset.wscale or 1, preset.hscale or 1
		local x, y = preset.x or 0, preset.y or 0
		local desaturated = preset.desaturated
		local coords = preset.coords
		local path = preset.path
		local blend = preset.blend or "BLEND"
		local alpha = preset.alpha or 1
		local doScale = preset.autoScale
		local custScale = preset.scale
		local overlayColorR, overlayColorG, overlayColorB = DF:ParseColors (preset.color or "white")
		
		local scale = (not doScale and custScale) or (healthBarHeight / (doScale and height or 10))
		
		--four parts (textures)
		if (#coords == 4) then
			for i = 1, 4 do
				local texture = plateFrame.unitFrame.TargetTextures4Sides [i]
				texture:Show()
				texture:SetTexture (path)
				texture:SetTexCoord (unpack (coords [i]))
				texture:SetSize (width * scale * wscale, height * scale * hscale)
				texture:SetAlpha (alpha)
				texture:SetVertexColor (overlayColorR, overlayColorG, overlayColorB)
				texture:SetDesaturated (desaturated)
				
				if (i == 1) then
					--PixelUtil.SetPoint (texture, "topleft", plateFrame.unitFrame.healthBar, "topleft", -x * scale, y * scale)
					texture:SetPoint ("topleft", plateFrame.unitFrame.healthBar, "topleft", -x * scale, y * scale)
					
				elseif (i == 2) then
					--PixelUtil.SetPoint (texture, "bottomleft", plateFrame.unitFrame.healthBar, "bottomleft", -x * scale, -y * scale)
					texture:SetPoint ("bottomleft", plateFrame.unitFrame.healthBar, "bottomleft", -x * scale, -y * scale)
					
				elseif (i == 3) then
					--PixelUtil.SetPoint (texture, "bottomright", plateFrame.unitFrame.healthBar, "bottomright", x * scale, -y * scale)
					texture:SetPoint ("bottomright", plateFrame.unitFrame.healthBar, "bottomright", x * scale, -y * scale)
					
				elseif (i == 4) then
					--PixelUtil.SetPoint (texture, "topright", plateFrame.unitFrame.healthBar, "topright", x * scale, y * scale)
					texture:SetPoint ("topright", plateFrame.unitFrame.healthBar, "topright", x * scale, y * scale)
					
				end
			end
			
			for i = 1, 2 do
				plateFrame.unitFrame.TargetTextures2Sides [i]:Hide()
			end
			
		--two parts
		else
			for i = 1, 2 do
				local texture = plateFrame.unitFrame.TargetTextures2Sides [i]
				texture:Show()
				texture:SetTexture (path)
				texture:SetBlendMode (blend)
				texture:SetTexCoord (unpack (coords [i]))
				--PixelUtil.SetSize (texture, width * scale, height * scale)
				--PixelUtil.SetSize (texture, width * scale * wscale, height * scale * hscale)
				texture:SetSize (width * scale * wscale, height * scale * hscale)
				texture:SetDesaturated (desaturated)
				texture:SetAlpha (alpha)
				texture:SetVertexColor (overlayColorR, overlayColorG, overlayColorB)
				
				if (i == 1) then
					--PixelUtil.SetPoint (texture, "left", plateFrame.unitFrame.healthBar, "left", -x * scale, y * scale)
					texture:SetPoint ("left", plateFrame.unitFrame.healthBar, "left", -x * scale, y * scale)
					
				elseif (i == 2) then
					--PixelUtil.SetPoint (texture, "right", plateFrame.unitFrame.healthBar, "right", x * scale, -y * scale)
					texture:SetPoint ("right", plateFrame.unitFrame.healthBar, "right", x * scale, y * scale)
				end
			end
			
			for i = 1, 4 do
				plateFrame.unitFrame.TargetTextures4Sides [i]:Hide()
			end
		end
		
		plateFrame.unitFrame.IsTarget = true
	end	

	-- ~updatetext ~update - called only from UpdatePlateFrame()
	-- update all texts in the nameplate, settings can variate from different unit types
	-- needReset is true when the previous unit type shown on this place is different from the current unit
	function Plater.UpdatePlateText (plateFrame, plateConfigs, needReset) --private
	
		if plateFrame.unitFrame.isWidgetOnlyMode then
			plateFrame.ActorNameSpecial:Hide()
			plateFrame.ActorTitleSpecial:Hide()
			
			return
		end
		
		-- ensure castBar updates are done, as this needs to be done for all types of plates...
		local spellnameString = plateFrame.unitFrame.castBar.Text
		local spellPercentString = plateFrame.unitFrame.castBar.percentText
		--update spell name text
		if (needReset) then
			DF:SetFontColor (spellnameString, plateConfigs.spellname_text_color)
			
			--DF:SetFontOutline (spellnameString, plateConfigs.spellname_text_shadow)
			Plater.SetFontOutlineAndShadow (spellnameString, plateConfigs.spellname_text_outline, plateConfigs.spellname_text_shadow_color, plateConfigs.spellname_text_shadow_color_offset[1], plateConfigs.spellname_text_shadow_color_offset[2])
			
			DF:SetFontFace (spellnameString, plateConfigs.spellname_text_font)
			DF:SetFontSize (spellnameString, plateConfigs.spellname_text_size)
			Plater.SetAnchor (spellnameString, plateConfigs.spellname_text_anchor)
		end

		--update spell cast time
		if (plateConfigs.spellpercent_text_enabled) then
			spellPercentString:Show()
			plateFrame.unitFrame.castBar.Settings.ShowCastTime = true
			if (needReset) then
				DF:SetFontColor (spellPercentString, plateConfigs.spellpercent_text_color)
				DF:SetFontSize (spellPercentString, plateConfigs.spellpercent_text_size)
				
				--DF:SetFontOutline (spellPercentString, plateConfigs.spellpercent_text_shadow)
				Plater.SetFontOutlineAndShadow (spellPercentString, plateConfigs.spellpercent_text_outline, plateConfigs.spellpercent_text_shadow_color, plateConfigs.spellpercent_text_shadow_color_offset[1], plateConfigs.spellpercent_text_shadow_color_offset[2])
				
				DF:SetFontFace (spellPercentString, plateConfigs.spellpercent_text_font)
				Plater.SetAnchor (spellPercentString, plateConfigs.spellpercent_text_anchor)
			end
		else
			plateFrame.unitFrame.castBar.Settings.ShowCastTime = false
			spellPercentString:Hide()
		end
		
	
		-- updates for special frames
		if (plateFrame.IsSelf) then
		
			--return
			--needReset = true
			
		elseif (plateFrame.IsFriendlyPlayerWithoutHealthBar) then --not critical code
			--when the option to show only the player name is enabled
			--special string to show the player name
			local nameFontString = plateFrame.ActorNameSpecial
			nameFontString:Show()
			
			--set the name in the string
			plateFrame.CurrentUnitNameString = nameFontString
			Plater.UpdateUnitName (plateFrame)
			
			DF:SetFontSize (nameFontString, plateConfigs.actorname_text_size)
			DF:SetFontFace (nameFontString, plateConfigs.actorname_text_font)
			
			--DF:SetFontOutline (nameFontString, plateConfigs.actorname_text_shadow)
			Plater.SetFontOutlineAndShadow (nameFontString, plateConfigs.actorname_text_outline, plateConfigs.actorname_text_shadow_color, plateConfigs.actorname_text_shadow_color_offset[1], plateConfigs.actorname_text_shadow_color_offset[2])
			
			--check if the player has a guild, this check is done when the nameplate is added
			if (plateFrame.playerGuildName) then
				if (plateConfigs.show_guild_name) then
					Plater.AddGuildNameToPlayerName (plateFrame)
				end
			end
			
			--set the point of the name and guild texts
			nameFontString:ClearAllPoints()
			PixelUtil.SetPoint (plateFrame.ActorNameSpecial, "center", plateFrame.unitFrame, "center", 0, 10)
			
			--format the color if is the same guild, a friend from friends list or color by player class
			if (Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_use_guild_color and plateFrame.playerGuildName == Plater.PlayerGuildName) then
				--is a guild friend?
				DF:SetFontColor (nameFontString, unpack(Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_guild_color))
				plateFrame.isFriend = true
				
			elseif (Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_use_friends_color and Plater.FriendsCache [plateFrame.unitNameInternal]) then
				--is regular friend
				DF:SetFontColor (nameFontString, unpack(Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_friend_color))
				--DF:SetFontOutline (nameFontString, plateConfigs.actorname_text_shadow)
				Plater.SetFontOutlineAndShadow (nameFontString, plateConfigs.actorname_text_outline, plateConfigs.actorname_text_shadow_color, plateConfigs.actorname_text_shadow_color_offset[1], plateConfigs.actorname_text_shadow_color_offset[2])
				plateFrame.isFriend = true
				
			else
				--isn't friend, check if is showing only the name and if is showing class colors
				if (plateConfigs.actorname_use_class_color) then
					local _, unitClass = UnitClass (plateFrame.unitFrame [MEMBER_UNITID])
					if (unitClass) then
						local color = DB_CLASS_COLORS [unitClass]
						DF:SetFontColor (nameFontString, color.r, color.g, color.b)
					else
						DF:SetFontColor (nameFontString, plateConfigs.actorname_text_color)
					end
				else
					DF:SetFontColor (nameFontString, plateConfigs.actorname_text_color)
				end

				plateFrame.isFriend = nil
			end
			
			return
		
		elseif (plateFrame.IsNpcWithoutHealthBar) then --not critical code
		
			--reset points for special units
			plateFrame.ActorNameSpecial:ClearAllPoints()
			plateFrame.ActorTitleSpecial:ClearAllPoints()
			
			--hide for good measure as reset
			plateFrame.ActorNameSpecial:Hide()
			plateFrame.ActorTitleSpecial:Hide()
			
			PixelUtil.SetPoint (plateFrame.ActorNameSpecial, "center", plateFrame.unitFrame, "center", 0, 10)
			PixelUtil.SetPoint (plateFrame.ActorTitleSpecial, "top", plateFrame.ActorNameSpecial, "bottom", 0, -2)

			--there's two ways of showing this for friendly npcs (selected from the options panel): show all names or only npcs with profession names
			--enemy npcs always show all
			if (plateConfigs.all_names or (plateFrame.isSoftInteract and Plater.db.profile.show_healthbars_on_softinteract)) then
				if not plateFrame.isObject or (plateFrame.isObject and not Plater.db.profile.hide_name_on_game_objects) then
					plateFrame.ActorNameSpecial:Show()
				else
					plateFrame.ActorNameSpecial:Hide()
				end
				plateFrame.CurrentUnitNameString = plateFrame.ActorNameSpecial
				Plater.UpdateUnitName (plateFrame)
				
				--if this is an enemy or neutral npc
				if (plateFrame [MEMBER_REACTION] <= 4) then
				
					local r, g, b, a = UnitSelectionColor(plateFrame.unitFrame [MEMBER_UNITID]) -- use this as default.
					
					--get the quest color if this npcs is a quest npc
					if (plateFrame [MEMBER_QUEST] and DB_PLATE_CONFIG [plateFrame.unitFrame.ActorType].quest_color_enabled) then
						if (plateFrame [MEMBER_REACTION] == Plater.UnitReaction.UNITREACTION_NEUTRAL) then
							r, g, b, a = unpack (plateConfigs.quest_color_neutral)
						else
							r, g, b, a = unpack (plateConfigs.quest_color_enemy)
							g = g + 0.1
							b = b + 0.1
						end
					elseif plateFrame.isObject then
						r, g, b, a = unpack (Plater.db.profile.name_on_game_object_color)
					elseif not r then -- use UnitSelectionColor instead, if available. fallback otherwise
						r, g, b, a = 1, 1, 0, 1 --neutral
						if (plateFrame [MEMBER_REACTION] <= 3) then
							r, g, b, a = 1, .05, .05, 1
						end
					end
					
					plateFrame.ActorNameSpecial:SetTextColor (r, g, b, a)
					DF:SetFontSize (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_size)
					DF:SetFontFace (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_font)
					
					--DF:SetFontOutline (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_shadow)
					Plater.SetFontOutlineAndShadow (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_outline, plateConfigs.big_actorname_text_shadow_color, plateConfigs.big_actorname_text_shadow_color_offset[1], plateConfigs.big_actorname_text_shadow_color_offset[2])
					
					--npc title
					local subTitle = Plater.GetActorSubName (plateFrame)
					if (subTitle and subTitle ~= "") then
						plateFrame.ActorTitleSpecial:Show()
						--subTitle = DF:RemoveRealmName (subTitle) -- why are removing real names on npc titles? e.g. <T-Shirt Scalper> Skin-Me-Own-Coat-Dibblefur gets broken to <T>.
						plateFrame.ActorTitleSpecial:SetText ("<" .. subTitle .. ">")
						plateFrame.ActorTitleSpecial:ClearAllPoints()
						PixelUtil.SetPoint (plateFrame.ActorTitleSpecial, "top", plateFrame.ActorNameSpecial, "bottom", 0, -2)
						
						plateFrame.ActorTitleSpecial:SetTextColor (r, g, b, a)
						DF:SetFontSize (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_size)
						DF:SetFontFace (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_font)
						
						--DF:SetFontOutline (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_shadow)
						Plater.SetFontOutlineAndShadow (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_outline, plateConfigs.big_actortitle_text_shadow_color, plateConfigs.big_actortitle_text_shadow_color_offset[1], plateConfigs.big_actortitle_text_shadow_color_offset[2])
					else
						plateFrame.ActorTitleSpecial:Hide()
					end
					
				else
					--it's a friendly npc
					plateFrame.ActorNameSpecial:SetTextColor (unpack (plateConfigs.big_actorname_text_color))
					DF:SetFontSize (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_size)
					DF:SetFontFace (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_font)
					
					--DF:SetFontOutline (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_shadow)
					Plater.SetFontOutlineAndShadow (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_outline, plateConfigs.big_actorname_text_shadow_color, plateConfigs.big_actorname_text_shadow_color_offset[1], plateConfigs.big_actorname_text_shadow_color_offset[2])
					
					--profession (title)
					local subTitle = Plater.GetActorSubName (plateFrame)
					if (subTitle and subTitle ~= "") then
						plateFrame.ActorTitleSpecial:Show()
						--subTitle = DF:RemoveRealmName (subTitle)
						plateFrame.ActorTitleSpecial:SetText ("<" .. subTitle .. ">")
						plateFrame.ActorTitleSpecial:ClearAllPoints()
						PixelUtil.SetPoint (plateFrame.ActorTitleSpecial, "top", plateFrame.ActorNameSpecial, "bottom", 0, -2)
						
						plateFrame.ActorTitleSpecial:SetTextColor (unpack (plateConfigs.big_actortitle_text_color))
						DF:SetFontSize (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_size)
						DF:SetFontFace (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_font)
						
						--DF:SetFontOutline (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_shadow)
						Plater.SetFontOutlineAndShadow (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_outline, plateConfigs.big_actortitle_text_shadow_color, plateConfigs.big_actortitle_text_shadow_color_offset[1], plateConfigs.big_actortitle_text_shadow_color_offset[2])
					end
				end
			else
				--scan tooltip to check if there's an title for this npc
				local subTitle = Plater.GetActorSubName (plateFrame)
				if (subTitle and subTitle ~= "" and not Plater.IsNpcInIgnoreList (plateFrame, true)) then
				
					plateFrame.ActorTitleSpecial:Show()
					--subTitle = DF:RemoveRealmName (subTitle)
					plateFrame.ActorTitleSpecial:SetText ("<" .. subTitle .. ">")
					plateFrame.ActorTitleSpecial:ClearAllPoints()
					PixelUtil.SetPoint (plateFrame.ActorTitleSpecial, "top", plateFrame.ActorNameSpecial, "bottom", 0, -2)
					
					plateFrame.ActorTitleSpecial:SetTextColor (unpack (plateConfigs.big_actortitle_text_color))
					plateFrame.ActorNameSpecial:SetTextColor (unpack (plateConfigs.big_actorname_text_color))
					
					DF:SetFontSize (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_size)
					DF:SetFontFace (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_font)
					
					--DF:SetFontOutline (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_shadow)
					Plater.SetFontOutlineAndShadow (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_outline, plateConfigs.big_actortitle_text_shadow_color, plateConfigs.big_actortitle_text_shadow_color_offset[1], plateConfigs.big_actortitle_text_shadow_color_offset[2])
					
					--npc name
					plateFrame.ActorNameSpecial:Show()

					plateFrame.CurrentUnitNameString = plateFrame.ActorNameSpecial
					Plater.UpdateUnitName (plateFrame)

					DF:SetFontSize (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_size)
					DF:SetFontFace (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_font)
					
					--DF:SetFontOutline (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_shadow)
					Plater.SetFontOutlineAndShadow (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_outline, plateConfigs.big_actorname_text_shadow_color, plateConfigs.big_actorname_text_shadow_color_offset[1], plateConfigs.big_actorname_text_shadow_color_offset[2])
				end
			end
			
			return
		end
		
		--critical code
		--the nameplate is showing the health bar
		--cache the strings for performance
		local nameString = plateFrame.unitFrame.healthBar.unitName	
		local guildString = plateFrame.ActorTitleSpecial
		local levelString = plateFrame.unitFrame.healthBar.actorLevel
		local lifeString = plateFrame.unitFrame.healthBar.lifePercent
		
		--update the unit name
		plateFrame.CurrentUnitNameString = nameString
		Plater.UpdateUnitName (plateFrame)
		
		--update unit name text
		if (needReset) then
			DF:SetFontSize (nameString, plateConfigs.actorname_text_size)
			DF:SetFontFace (nameString, plateConfigs.actorname_text_font)
			
			Plater.SetFontOutlineAndShadow (nameString, plateConfigs.actorname_text_outline, plateConfigs.actorname_text_shadow_color, plateConfigs.actorname_text_shadow_color_offset[1], plateConfigs.actorname_text_shadow_color_offset[2])

			Plater.SetAnchor (nameString, plateConfigs.actorname_text_anchor)
			--PixelUtil.SetHeight (nameString, nameString:GetLineHeight())
		end
		
		if (plateFrame.playerGuildName) then
			if (plateConfigs.show_guild_name) then
				Plater.AddGuildNameToPlayerName (plateFrame)
			end
		end
		
		if (Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_use_guild_color and plateFrame.playerGuildName == Plater.PlayerGuildName) then
			--is a guild friend?
			DF:SetFontColor (nameString, unpack(Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_guild_color))
			DF:SetFontColor (guildString, unpack(Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_guild_color))
			plateFrame.isFriend = true
		
		elseif (Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_use_friends_color and Plater.FriendsCache [plateFrame.unitNameInternal]) then
			--is regular friend
			DF:SetFontColor (nameString, unpack(Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_friend_color))
			DF:SetFontColor (guildString, unpack(Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_friend_color))
			plateFrame.isFriend = true		

		elseif (plateFrame.actorType == ACTORTYPE_FRIENDLY_PLAYER and Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_use_class_color) then
			--class colors should be used, if possible, because this is enabled
			plateFrame.isFriend = nil
			
			local _, unitClass = UnitClass (plateFrame.unitFrame [MEMBER_UNITID])
			if (unitClass) then
				local color = DB_CLASS_COLORS [unitClass]
				DF:SetFontColor (nameString, color.r, color.g, color.b)
				DF:SetFontColor (guildString, color.r, color.g, color.b)
			else
				DF:SetFontColor (nameString, plateConfigs.actorname_text_color)
				DF:SetFontColor (guildString, plateConfigs.actorname_text_color)
			end
		
		elseif (plateFrame.actorType == ACTORTYPE_ENEMY_PLAYER and Plater.db.profile.plate_config [ACTORTYPE_ENEMY_PLAYER].actorname_use_class_color) then
			--class colors should be used, if possible, because this is enabled
			plateFrame.isFriend = nil
			
			local _, unitClass = UnitClass (plateFrame.unitFrame [MEMBER_UNITID])
			if (unitClass) then
				local color = DB_CLASS_COLORS [unitClass]
				DF:SetFontColor (nameString, color.r, color.g, color.b)
				DF:SetFontColor (guildString, color.r, color.g, color.b)
			else
				DF:SetFontColor (nameString, plateConfigs.actorname_text_color)
				DF:SetFontColor (guildString, plateConfigs.actorname_text_color)
			end
		
		else
			DF:SetFontColor (nameString, plateConfigs.actorname_text_color)
			DF:SetFontColor (guildString, plateConfigs.actorname_text_color)
			plateFrame.isFriend = nil
		end
		
		--update unit level text
		if (plateConfigs.level_text_enabled) then
			levelString:Show()
			if (needReset) then
				DF:SetFontSize (levelString, plateConfigs.level_text_size)
				DF:SetFontFace (levelString, plateConfigs.level_text_font)

				Plater.SetFontOutlineAndShadow (levelString, plateConfigs.level_text_outline, plateConfigs.level_text_shadow_color, plateConfigs.level_text_shadow_color_offset[1], plateConfigs.level_text_shadow_color_offset[2])
				
				Plater.SetAnchor (levelString, plateConfigs.level_text_anchor)
				Plater.UpdateLevelTextAndColor (levelString, plateFrame.unitFrame [MEMBER_UNITID])
				levelString:SetAlpha (plateConfigs.level_text_alpha)
			else
				Plater.UpdateLevelTextAndColor (levelString, plateFrame.unitFrame [MEMBER_UNITID])
				levelString:SetAlpha (plateConfigs.level_text_alpha)
			end
		else
			levelString:Hide()
		end

		--update health amount text
		if (plateConfigs.percent_text_enabled) then
			if (needReset) then
				DF:SetFontSize (lifeString, plateConfigs.percent_text_size)
				DF:SetFontFace (lifeString, plateConfigs.percent_text_font)

				Plater.SetFontOutlineAndShadow (lifeString, plateConfigs.percent_text_outline, plateConfigs.percent_text_shadow_color, plateConfigs.percent_text_shadow_color_offset[1], plateConfigs.percent_text_shadow_color_offset[2])
				
				DF:SetFontColor (lifeString, plateConfigs.percent_text_color)

				Plater.SetAnchor (lifeString, plateConfigs.percent_text_anchor)
				PixelUtil.SetHeight (lifeString, lifeString:GetLineHeight())
				
				lifeString:SetAlpha (plateConfigs.percent_text_alpha)
			end
			
			Plater.UpdateLifePercentText (plateFrame.unitFrame.healthBar, plateFrame.unitFrame [MEMBER_UNITID], plateConfigs.percent_show_health, plateConfigs.percent_show_percent, plateConfigs.percent_text_show_decimals)
		else
			lifeString:Hide()
		end
		
		--name isn't shown in the personal bar
		if (plateFrame.IsSelf) then
			plateFrame.unitFrame.healthBar.unitName:SetText ("")
		end
		
		return true
	end
	
	--check if the life percent should be showing for this nameplate
	--self is plateFrame
	function Plater.UpdateLifePercentVisibility (self)
		local plateConfigs = Plater.GetSettings (self)
		
		if (plateConfigs.percent_text_enabled) then
			--can show out of combat? or if the player is in combat
			if ((Plater.db.profile.use_player_combat_state and PLAYER_IN_COMBAT or self.unitFrame.InCombat) or plateConfigs.percent_text_ooc) then
				self.unitFrame.healthBar.lifePercent:Show()
			else
				self.unitFrame.healthBar.lifePercent:Hide()
			end
		else
			self.unitFrame.healthBar.lifePercent:Hide()
		end
	end

	--return the nameplate config directly from the profile
	--this is the actual table from the db, any changes will affect the profile
	--self is plateFrame
	function Plater.GetSettings (self) --private
		return self.PlateConfig
	end
	
	function Plater.UpdateLifePercentText (healthBar, unitId, showHealthAmount, showPercentAmount, showDecimals) -- ~health
		
		--get the cached health amount for performance
		local currentHealth, maxHealth = healthBar.CurrentHealth, healthBar.CurrentHealthMax
		
		if (showHealthAmount and showPercentAmount) then
			local percent = maxHealth == 0 and 100 or (currentHealth / maxHealth * 100)
			
			if (showDecimals) then
				if (percent < 10) then
					healthBar.lifePercent:SetText (Plater.FormatNumber (currentHealth) .. format (" (%.2f%%)", percent))
					
				elseif (percent < 99.9) then
					healthBar.lifePercent:SetText (Plater.FormatNumber (currentHealth) .. format (" (%.1f%%)", percent))
				else
					healthBar.lifePercent:SetText (Plater.FormatNumber (currentHealth) .. " (100%)")
				end
			else
				healthBar.lifePercent:SetText (Plater.FormatNumber (currentHealth) ..  format (" (%d%%)", percent))
			end
			
		elseif (showHealthAmount) then
			healthBar.lifePercent:SetText (Plater.FormatNumber (currentHealth))
		
		elseif (showPercentAmount) then
			local percent = maxHealth == 0 and 100 or (currentHealth / maxHealth * 100)
			
			if (showDecimals) then
				if (percent < 10) then
					healthBar.lifePercent:SetText (format ("%.2f%%", percent))
					
				elseif (percent < 99.9) then
					healthBar.lifePercent:SetText (format ("%.1f%%", percent))
				else
					healthBar.lifePercent:SetText ("100%")
				end
			else
				healthBar.lifePercent:SetText (format ("%d%%", percent))
			end
		
		else
			healthBar.lifePercent:SetText ("")
		end
	end

	-- this test if the percent life text can updated
	function Plater.CheckLifePercentText (unitFrame) --private
		if (not unitFrame.actorType) then
			return
		end
		
		local actorTypeDBConfig = DB_PLATE_CONFIG [unitFrame.actorType]
		if (Plater.db.profile.use_player_combat_state and PLAYER_IN_COMBAT or unitFrame.InCombat) then
			if (actorTypeDBConfig.percent_text_enabled) then
				Plater.UpdateLifePercentText (unitFrame.healthBar, unitFrame.unit, actorTypeDBConfig.percent_show_health, actorTypeDBConfig.percent_show_percent, actorTypeDBConfig.percent_text_show_decimals)
			else
				unitFrame.healthBar.lifePercent:Hide()
			end
		else
			--if not in combat, check if can show the percent health out of combat
			if (actorTypeDBConfig.percent_text_enabled and actorTypeDBConfig.percent_text_ooc) then
				Plater.UpdateLifePercentText (unitFrame.healthBar, unitFrame.unit, actorTypeDBConfig.percent_show_health, actorTypeDBConfig.percent_show_percent, actorTypeDBConfig.percent_text_show_decimals)
			else
				unitFrame.healthBar.lifePercent:Hide()
			end
		end
	end
		
	function Plater.UpdateAllNames() --private
		for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
			if (plateFrame.actorType == ACTORTYPE_PLAYER) then
				plateFrame.NameAnchor = 0
				
			elseif (plateFrame.actorType == Plater.UnitReaction.UNITREACTION_FRIENDLY) then
				plateFrame.NameAnchor = DB_NAME_PLAYERFRIENDLY_ANCHOR
				
			elseif (plateFrame.actorType == ACTORTYPE_ENEMY_PLAYER) then
				plateFrame.NameAnchor = DB_NAME_PLAYERENEMY_ANCHOR
				
			elseif (plateFrame.actorType == ACTORTYPE_FRIENDLY_NPC) then
				plateFrame.NameAnchor = DB_NAME_NPCFRIENDLY_ANCHOR
				
			elseif (plateFrame.actorType == ACTORTYPE_ENEMY_NPC) then
				plateFrame.NameAnchor = DB_NAME_NPCENEMY_ANCHOR
				
			end
		
			Plater.UpdateUnitName (plateFrame)
		end
	end

	function Plater.UpdateSpellNameSize (nameString, actorType, cutOff, inCombat)
		local spellName = nameString:GetText()
		
		if not spellName or spellName == "" then
			return
		end
		
		local maxLength = Plater.MaxCastBarTextLength or 500
		cutOff = cutOff or 40
		actorType = actorType or "enemynpc"
		
		if (not Plater.db.profile.no_spellname_length_limit) then
			local castKey = Plater.GetHashKey(inCombat)
			if Plater.db.profile.plate_config[actorType][castKey] then
				maxLength = Plater.db.profile.plate_config[actorType][castKey][1] - cutOff
			end
		end
		
		while (nameString:GetStringWidth() > maxLength) do
			spellName = strsub (spellName, 1, #spellName - 1)
			nameString:SetText (spellName)
			if (string.len (spellName) <= 1) then
				break
			end
		end
		
		-- cleanup utf8...
		spellName = DF:CleanTruncateUTF8String(spellName)
		nameString:SetText (spellName)
		
	end

	function Plater.AddGuildNameToPlayerName (plateFrame)
		local currentText = plateFrame.CurrentUnitNameString:GetText()
		if (not currentText:find ("<")) then
			plateFrame.CurrentUnitNameString:SetText (currentText .. "\n" .. "<" .. plateFrame.playerGuildName .. ">")
		end
	end
	
	function Plater.UpdateUnitName (plateFrame)
		local nameString = plateFrame.CurrentUnitNameString

		if ( not (plateFrame.IsFriendlyPlayerWithoutHealthBar or plateFrame.IsNpcWithoutHealthBar) and plateFrame.NameAnchor >= 9) then
			--remove some character from the unit name if the name is placed inside the nameplate
			Plater.UpdateUnitNameTextSize (plateFrame, nameString)
		else
			nameString:SetText (plateFrame [MEMBER_NAME] or plateFrame.unitFrame [MEMBER_NAME] or "")
		end
		
		--check if the player has a guild, this check is done when the nameplate is added
		if (plateFrame.playerGuildName) then
			if (plateFrame.PlateConfig.show_guild_name) then
				Plater.AddGuildNameToPlayerName (plateFrame)
			end
		end
	end

	function Plater.UpdateUnitNameTextSize (plateFrame, nameString, maxWidth)
		local stringSize = maxWidth or max (plateFrame.unitFrame.healthBar:GetWidth() - 6, 44)
		local name = plateFrame [MEMBER_NAME] or plateFrame.unitFrame [MEMBER_NAME] or ""
		
		nameString:SetText (name)
		
		if not name or name == "" then
			return
		end
		
		while (nameString:GetStringWidth() > stringSize) do
			name = strsub (name, 1, #name-1)
			nameString:SetText (name)
			if (string.len (name) <= 1) then
				break
			end
		end
		
		-- cleanup utf8...
		name = DF:CleanTruncateUTF8String(name)
		nameString:SetText (name)
	end

	--updates the level text and the color
	function Plater.UpdateLevelTextAndColor (levelString, unitId)
		--level text
		local level = UnitLevel (unitId)
		if (not level) then
			levelString:SetText ("")
			
		elseif (level == -1) then
			levelString:SetText ("??")
			
		else
			levelString:SetText (level)
		end
		
		--level color
		local color = GetRelativeDifficultyColor (UnitLevel ("player") or 120, UnitLevel (unitId) or 120) or Plater.DefaultLevelColor
		levelString:SetTextColor (color.r, color.g, color.b, Plater.db.profile.level_text_alpha)
	end	

	--units can be rename on the Npc Colors tab, this function run from nameplate_added and UpdatePlateFrame() (usually from UpdateAllPlates() are called)
	function Plater.UpdateNameOnRenamedUnit(plateFrame)
		--set the npc name if the unit has a custom name
		local newNpcName = Plater.db.profile.npcs_renamed[plateFrame[MEMBER_NPCID]]
		local unitFrame = plateFrame.unitFrame
		if (newNpcName) then
			plateFrame [MEMBER_NAME] = newNpcName
			plateFrame [MEMBER_NAMELOWER] = lower (newNpcName)
			unitFrame [MEMBER_NAME] = newNpcName
			unitFrame [MEMBER_NAMELOWER] = plateFrame [MEMBER_NAMELOWER]
			unitFrame.unitName:SetText(newNpcName)
			unitFrame.unitName.isRenamed = true
		else
			if (unitFrame.unitName.isRenamed) then
				newNpcName = UnitName(plateFrame[MEMBER_UNITID])
				plateFrame [MEMBER_NAME] = newNpcName
				plateFrame [MEMBER_NAMELOWER] = lower (newNpcName)
				unitFrame [MEMBER_NAME] = newNpcName
				unitFrame [MEMBER_NAMELOWER] = plateFrame [MEMBER_NAMELOWER]
				unitFrame.unitName:SetText(newNpcName)
				unitFrame.unitName.isRenamed = nil
			end
		end
	end
	
	--Blizzard default font settings
	function Plater.UpdateBlizzardNameplateFonts()
		local profile = Plater.db.profile
		if profile.blizzard_nameplate_font_override_enabled then
			DF:SetFontFace (_G.SystemFont_NamePlate, profile.blizzard_nameplate_font)
			DF:SetFontOutline (_G.SystemFont_NamePlate, profile.blizzard_nameplate_font_outline)
			DF:SetFontSize (_G.SystemFont_NamePlate, profile.blizzard_nameplate_font_size)
			
			DF:SetFontFace (_G.SystemFont_NamePlateFixed, profile.blizzard_nameplate_font)
			DF:SetFontOutline (_G.SystemFont_NamePlateFixed, profile.blizzard_nameplate_font_outline)
			DF:SetFontSize (_G.SystemFont_NamePlateFixed, profile.blizzard_nameplate_font_size)
			
			DF:SetFontFace (_G.SystemFont_LargeNamePlate, profile.blizzard_nameplate_large_font)
			DF:SetFontOutline (_G.SystemFont_LargeNamePlate, profile.blizzard_nameplate_large_font_outline)
			DF:SetFontSize (_G.SystemFont_LargeNamePlate, profile.blizzard_nameplate_large_font_size)
			
			DF:SetFontFace (_G.SystemFont_LargeNamePlateFixed, profile.blizzard_nameplate_large_font)
			DF:SetFontOutline (_G.SystemFont_LargeNamePlateFixed, profile.blizzard_nameplate_large_font_outline)
			DF:SetFontSize (_G.SystemFont_LargeNamePlateFixed, profile.blizzard_nameplate_large_font_size)
		end
	end

	-- ~updateplate ~update ~updatenameplate
	function Plater.UpdatePlateFrame (plateFrame, actorType, forceUpdate, justAdded, regenDisabled)
		Plater.StartLogPerformanceCore("Plater-Core", "Update", "UpdatePlateFrame")
		
		actorType = actorType or plateFrame.actorType
		
		if (not actorType or not plateFrame.unitFrame.PlaterOnScreen) then
			Plater.EndLogPerformanceCore("Plater-Core", "Update", "UpdatePlateFrame")
			return
		end
		
		local unitFrame = plateFrame.unitFrame
		local healthBar = unitFrame.healthBar
		local castBar = unitFrame.castBar
		local buffFrame = unitFrame.BuffFrame
		local buffFrame2 = unitFrame.BuffFrame2
		local nameFrame = unitFrame.healthBar.unitName
		
		castBar.Settings.FillOnInterrupt = Plater.db.profile.cast_statusbar_spark_filloninterrupt
		castBar.Settings.HideSparkOnInterrupt = Plater.db.profile.cast_statusbar_spark_hideoninterrupt

		plateFrame.actorType = actorType
		unitFrame.actorType = actorType
		unitFrame.ActorType = actorType --exposed to scripts
		
		local shouldForceRefresh = justAdded or forceUpdate
		if (plateFrame.IsNpcWithoutHealthBar or plateFrame.IsFriendlyPlayerWithoutHealthBar) then
			shouldForceRefresh = true
			
		end

		healthBar.BorderIsAggroIndicator = nil
		
		local wasQuestPlate = plateFrame [MEMBER_QUEST]
		plateFrame [MEMBER_QUEST] = false
		unitFrame [MEMBER_QUEST] = false
		plateFrame.QuestInfo = {}
		unitFrame.QuestInfo = {}
		
		plateFrame.ActorNameSpecial:Hide()
		plateFrame.ActorTitleSpecial:Hide()
		plateFrame.Top3DFrame:Hide()
		plateFrame.RaidTarget:Hide()
		
		--clear aggro glow
		unitFrame.aggroGlowUpper:Hide()
		unitFrame.aggroGlowLower:Hide()
		
		--check for quest color
		if (IS_IN_OPEN_WORLD and actorType == ACTORTYPE_ENEMY_NPC and DB_PLATE_CONFIG [actorType].quest_enabled) then --actorType == ACTORTYPE_FRIENDLY_NPC or 
			local isQuestMob = Plater.IsQuestObjective (plateFrame)
			if (isQuestMob and DB_PLATE_CONFIG [actorType].quest_color_enabled and not Plater.IsUnitTapDenied (plateFrame.unitFrame.unit)) then
				if (plateFrame [MEMBER_REACTION] == Plater.UnitReaction.UNITREACTION_NEUTRAL) then
					Plater.ChangeHealthBarColor_Internal (healthBar, unpack (DB_PLATE_CONFIG [actorType].quest_color_neutral))
					
				else
					Plater.ChangeHealthBarColor_Internal (healthBar, unpack (DB_PLATE_CONFIG [actorType].quest_color_enemy))
				end
			else
				if (wasQuestPlate) then
					Plater.FindAndSetNameplateColor (unitFrame)
				end
			end
		else
			if (wasQuestPlate) then
				Plater.FindAndSetNameplateColor (unitFrame)
			end
		end
		
		--if the nameplate is for a friendly npc
		if (actorType == ACTORTYPE_FRIENDLY_NPC) then
			plateFrame.IsFriendlyPlayerWithoutHealthBar = false
			
			local subTitleExists = false
			local subTitle = Plater.GetActorSubName (plateFrame)
			if (subTitle and subTitle ~= "" and not Plater.IsNpcInIgnoreList (plateFrame, true)) then
				subTitleExists = true
			end
		
			Plater.ForceFindPetOwner (plateFrame [MEMBER_GUID])
		
			-- handle own pets separately, including nazjatar guardians
			if (Plater.PlayerPetCache [unitFrame [MEMBER_GUID]] and not plateFrame.isBattlePet) then
				if (DB_PLATE_CONFIG [actorType].only_names) then
					healthBar:Hide()
					buffFrame:Hide()
					buffFrame2:Hide()
					nameFrame:Hide()
					plateFrame.IsNpcWithoutHealthBar = true
				
				else
					healthBar:Show()
					buffFrame:Show()
					buffFrame2:Show()
					nameFrame:Show()
					plateFrame.IsNpcWithoutHealthBar = false
				end
			
			elseif (DB_PLATE_CONFIG [actorType].follow_blizzard_npc_option and not (unitFrame.isSoftInteract or plateFrame [MEMBER_TARGET]) and not UnitShouldDisplayName(plateFrame [MEMBER_UNITID])) then
				-- hide if following blizzard naming
				healthBar:Hide()
				buffFrame:Hide()
				buffFrame2:Hide()
				nameFrame:Hide()
				plateFrame.IsNpcWithoutHealthBar = false
				
			elseif (IS_IN_OPEN_WORLD and DB_PLATE_CONFIG [actorType].quest_enabled and Plater.IsQuestObjective (plateFrame)) then
				if (DB_PLATE_CONFIG [actorType].quest_color_enabled) then
					Plater.ChangeHealthBarColor_Internal (healthBar, unpack (DB_PLATE_CONFIG [actorType].quest_color))
				end

				healthBar:Show()
				buffFrame:Show()
				buffFrame2:Show()
				nameFrame:Show()
				plateFrame.IsNpcWithoutHealthBar = false
				
				--these twoseettings make the healthing dummy show the healthbar
				--				Plater.db.profile.plate_config.friendlynpc.only_names = false
				--				Plater.db.profile.plate_config.friendlynpc.all_names = false
			
			elseif (DB_PLATE_CONFIG [actorType].only_names) then
				--show only the npc name without the health bar

				healthBar:Hide()
				buffFrame:Hide()
				buffFrame2:Hide()
				nameFrame:Hide()
				plateFrame.IsNpcWithoutHealthBar = true
				
			elseif (not subTitleExists and not DB_PLATE_CONFIG [actorType].all_names) then
				-- show only if a title is present
				healthBar:Hide()
				buffFrame:Hide()
				buffFrame2:Hide()
				nameFrame:Hide()
				plateFrame.IsNpcWithoutHealthBar = true
			
			else
				healthBar:Show()
				buffFrame:Show()
				buffFrame2:Show()
				nameFrame:Show()
				plateFrame.IsNpcWithoutHealthBar = false
			end

		elseif (actorType == ACTORTYPE_FRIENDLY_PLAYER) then
			plateFrame.IsNpcWithoutHealthBar = false
			Plater.ParseHealthSettingForPlayer (plateFrame, justAdded)
			
				--change the player health bar color to either class color or users choice
			if (not Plater.db.profile.use_playerclass_color) then
				Plater.ChangeHealthBarColor_Internal (healthBar, unpack(DB_PLATE_CONFIG [actorType].fixed_class_color))
			else
				local _, class = UnitClass (unitFrame [MEMBER_UNITID])
				if (class) then		
					local color = DB_CLASS_COLORS [class]
					Plater.ChangeHealthBarColor_Internal (healthBar, color.r, color.g, color.b, color.a)
				else
					Plater.ChangeHealthBarColor_Internal (healthBar, 1, 1, 1, 1)
				end
			end
			
		elseif (actorType == ACTORTYPE_ENEMY_PLAYER) then
			plateFrame.IsNpcWithoutHealthBar = false
			
			if (plateFrame.PlayerCannotAttack and not DB_SHOW_HEALTHBARS_FOR_NOT_ATTACKABLE) then
				healthBar:Hide()
				buffFrame:Hide()
				buffFrame2:Hide()
				nameFrame:Hide()
				plateFrame.IsFriendlyPlayerWithoutHealthBar = true
				
			else
				healthBar:Show()
				buffFrame:Show()
				buffFrame2:Show()
				nameFrame:Show()
				plateFrame.IsFriendlyPlayerWithoutHealthBar = false
				
				if (DB_PLATE_CONFIG [actorType].use_playerclass_color) then
					local _, class = UnitClass (unitFrame [MEMBER_UNITID])
					if (class) then		
						local color = DB_CLASS_COLORS [class]
						Plater.ChangeHealthBarColor_Internal (healthBar, color.r, color.g, color.b, color.a)
					else
						Plater.ChangeHealthBarColor_Internal (healthBar, unpack (DB_PLATE_CONFIG [actorType].fixed_class_color))
					end
				else
					Plater.ChangeHealthBarColor_Internal (healthBar, unpack (DB_PLATE_CONFIG [actorType].fixed_class_color))
				end
			end
			
		else --> ENEMY NPC pass throught here
			plateFrame.IsFriendlyPlayerWithoutHealthBar = false
			
			--check if this is an enemy npc but the player cannot attack it
			if (plateFrame.PlayerCannotAttack and not DB_SHOW_HEALTHBARS_FOR_NOT_ATTACKABLE and not unitFrame.IsSelf) or unitFrame.isSoftInteractObject then
				healthBar:Hide()
				buffFrame:Hide()
				buffFrame2:Hide()
				nameFrame:Hide()
				plateFrame.IsNpcWithoutHealthBar = true
				
			else
				healthBar:Show()
				buffFrame:Show()
				buffFrame2:Show()
				plateFrame.IsNpcWithoutHealthBar = false
				
				if unitFrame.IsSelf then
					--refresh color
					if (plateFrame.PlateConfig.healthbar_color_by_hp) then
						local currentHealth = healthBar.currentHealth
						local currentHealthMax = healthBar.currentHealthMax
						local originalColor = plateFrame.PlateConfig.healthbar_color
						local r, g, b = DF:LerpLinearColor (abs (currentHealth / currentHealthMax - 1), 1, originalColor[1], originalColor[2], originalColor[3], 1, .4, 0)
						Plater.ChangeHealthBarColor_Internal (healthBar, r, g, b, (originalColor[4] or 1), true)
					else
						Plater.ChangeHealthBarColor_Internal (healthBar, unpack (DB_PLATE_CONFIG [actorType].healthbar_color))
					end
				else
					nameFrame:Show()
					-- could be a pet
					Plater.ForceFindPetOwner (plateFrame [MEMBER_GUID])
				end
			end
		end
		
		castBar:SetStatusBarTexture (DB_TEXTURE_CASTBAR)
		
		--update all texts in the nameplate
		Plater.UpdatePlateText (plateFrame, DB_PLATE_CONFIG [actorType], shouldForceRefresh or plateFrame.PreviousUnitType ~= actorType or unitFrame.RefreshID < PLATER_REFRESH_ID)

		if (unitFrame.RefreshID < PLATER_REFRESH_ID or shouldForceRefresh) then
			unitFrame.RefreshID = PLATER_REFRESH_ID

			local profile = Plater.db.profile
			
			--update highlight texture
			unitFrame.HighlightFrame.HighlightTexture:SetTexture (DB_TEXTURE_HEALTHBAR)
			unitFrame.HighlightFrame.HighlightTexture:SetBlendMode ("ADD")
			unitFrame.HighlightFrame.HighlightTexture:SetAlpha (profile.hover_highlight_alpha)
			
			--click area is shown?
			if (profile.click_space_always_show) then
				Plater.SetPlateBackground (plateFrame)
			else
				plateFrame.unitFrame:SetBackdrop (nil)
			end
			
			--setup cast bar
			castBar.background:SetTexture (DB_TEXTURE_CASTBAR_BG)
			castBar.extraBackground:SetTexture (DB_TEXTURE_CASTBAR_BG)
			castBar.background:SetVertexColor (unpack (profile.cast_statusbar_bgcolor))
			castBar.extraBackground:SetVertexColor (unpack (profile.cast_statusbar_bgcolor))
			castBar.flashTexture:SetTexture (DB_TEXTURE_CASTBAR)
			castBar.Icon:SetTexCoord (0.078125, 0.921875, 0.078125, 0.921875)
			
			local colors = castBar.Settings.Colors
			colors.Casting:SetColor (profile.cast_statusbar_color)
			colors.Channeling:SetColor (profile.cast_statusbar_color_channeling)
			colors.NonInterruptible:SetColor (profile.cast_statusbar_color_nointerrupt)
			colors.Interrupted:SetColor (profile.cast_statusbar_color_interrupted)
			colors.Finished:SetColor (profile.cast_statusbar_color_finished)
			
			castBar.Settings.FadeInTime = profile.cast_statusbar_fadein_time
			castBar.Settings.FadeOutTime = profile.cast_statusbar_fadeout_time
			castBar.fadeOutAnimation.alpha1:SetDuration (castBar.Settings.FadeOutTime)
			castBar.fadeInAnimation.alpha1:SetDuration (castBar.Settings.FadeInTime)
			castBar.Settings.NoFadeEffects = not profile.cast_statusbar_use_fade_effects
			castBar.Settings.SparkTexture = profile.cast_statusbar_spark_texture
			castBar.Spark:SetTexture (castBar.Settings.SparkTexture)
			castBar.Spark:SetVertexColor (unpack (profile.cast_statusbar_spark_color))
			castBar.Spark:SetAlpha (profile.cast_statusbar_spark_alpha)
			
			if (profile.cast_statusbar_spark_half) then
				castBar.Spark:SetTexCoord (0, 0.5, 0, 1)
			else
				castBar.Spark:SetTexCoord (0, 1, 0, 1)
			end

			castBar.castColorTexture:Hide()
			if (profile.cast_color_settings.enabled) then
				castBar.castColorTexture:SetWidth(profile.cast_color_settings.width)
				castBar.castColorTexture:SetAlpha(profile.cast_color_settings.alpha)
				castBar.castColorTexture:SetDrawLayer(profile.cast_color_settings.layer, -6)
				Plater.SetAnchor(castBar.castColorTexture, profile.cast_color_settings.anchor)
			end

			castBar.Settings.SparkOffset = profile.cast_statusbar_spark_offset
			
			--setup power bar
			unitFrame.powerBar:SetTexture (DB_TEXTURE_HEALTHBAR)
			
			--setup health bar~
			healthBar:SetTexture (DB_TEXTURE_HEALTHBAR)
			healthBar.background:SetTexture (DB_TEXTURE_HEALTHBAR_BG)
			healthBar.background:SetVertexColor (unpack (profile.health_statusbar_bgcolor))
			
			--update border
			Plater.UpdatePlateBorders (plateFrame)
			
			--target overlay texture
			local targetedOverlayTexture = LibSharedMedia:Fetch ("statusbar", profile.health_selection_overlay)
			unitFrame.targetOverlayTexture:SetTexture (targetedOverlayTexture)
			unitFrame.targetOverlayTexture:SetVertexColor (unpack (Plater.db.profile.health_selection_overlay_color))
			unitFrame.targetOverlayTexture:SetAlpha (profile.health_selection_overlay_alpha)
			
			--heal prediction
			unitFrame.healthBar.Settings.ShowHealingPrediction = Plater.db.profile.show_health_prediction
			unitFrame.healthBar.Settings.ShowShields = Plater.db.profile.show_shield_prediction
			if (unitFrame.healthBar.unit) then
				unitFrame.healthBar:UNIT_HEALTH()
			end
			
			if IS_WOW_PROJECT_MAINLINE and unitFrame.WidgetContainer then
				Plater.SetAnchor (unitFrame.WidgetContainer, profile.widget_bar_anchor, unitFrame)
				plateFrame.unitFrame.WidgetContainer:SetScale(Plater.db.profile.widget_bar_scale)
			end
		end
		
		--update the plate size for this unit
		Plater.UpdatePlateSize (plateFrame)
		
		--raid marker
		Plater.UpdatePlateRaidMarker (plateFrame)
		
		--indicators for the unit
		Plater.UpdateIndicators (plateFrame, actorType, regenDisabled)
		
		--update the visibility of the health text
		Plater.UpdateLifePercentVisibility (plateFrame)
		--update the health text
		Plater.CheckLifePercentText (unitFrame)
		
		--target indicator
		Plater.UpdateTarget (plateFrame)
		
		--personal player bar
		if (plateFrame.IsSelf) then
			Plater.UpdatePersonalBar (NamePlateDriverFrame)
			if (not DB_PLATE_CONFIG [actorType].healthbar_color_by_hp) then
				Plater.ChangeHealthBarColor_Internal (healthBar, unpack (DB_PLATE_CONFIG [actorType].healthbar_color))
			end
		end
		
		Plater.UpdateCustomDesign (unitFrame)

		Plater.UpdateNameOnRenamedUnit(plateFrame)

		--update options in the extra icons row frame
		if (unitFrame.ExtraIconFrame.RefreshID < PLATER_REFRESH_ID) then
			Plater.SetAnchor (unitFrame.ExtraIconFrame, Plater.db.profile.extra_icon_anchor)
			unitFrame.ExtraIconFrame:SetOption ("anchor", Plater.db.profile.extra_icon_anchor)
			unitFrame.ExtraIconFrame:SetOption ("show_text", Plater.db.profile.extra_icon_show_timer)
			unitFrame.ExtraIconFrame:SetOption ("text_font", Plater.db.profile.extra_icon_timer_font)
			unitFrame.ExtraIconFrame:SetOption ("text_size", Plater.db.profile.extra_icon_timer_size)
			unitFrame.ExtraIconFrame:SetOption ("text_outline", Plater.db.profile.extra_icon_timer_outline)
			unitFrame.ExtraIconFrame:SetOption ("grow_direction", unitFrame.ExtraIconFrame:GetIconGrowDirection())
			unitFrame.ExtraIconFrame:SetOption ("icon_width", Plater.db.profile.extra_icon_width)
			unitFrame.ExtraIconFrame:SetOption ("icon_height", Plater.db.profile.extra_icon_height)
			unitFrame.ExtraIconFrame:SetOption ("texcoord", Plater.db.profile.extra_icon_wide_icon and Plater.WideIconCoords or Plater.BorderLessIconCoords)
			unitFrame.ExtraIconFrame:SetOption ("desc_text", Plater.db.profile.extra_icon_caster_name)
			unitFrame.ExtraIconFrame:SetOption ("desc_text_font", Plater.db.profile.extra_icon_caster_font)
			unitFrame.ExtraIconFrame:SetOption ("desc_text_size", Plater.db.profile.extra_icon_caster_size)
			unitFrame.ExtraIconFrame:SetOption ("desc_text_outline", Plater.db.profile.extra_icon_caster_outline)
			unitFrame.ExtraIconFrame:SetOption ("stack_text", Plater.db.profile.extra_icon_show_stacks)
			unitFrame.ExtraIconFrame:SetOption ("stack_text_font", Plater.db.profile.extra_icon_stack_font)
			unitFrame.ExtraIconFrame:SetOption ("stack_text_size", Plater.db.profile.extra_icon_stack_size)
			unitFrame.ExtraIconFrame:SetOption ("stack_text_outline", Plater.db.profile.extra_icon_stack_outline)
			unitFrame.ExtraIconFrame:SetOption ("surpress_tulla_omni_cc", Plater.db.profile.disable_omnicc_on_auras)
			unitFrame.ExtraIconFrame:SetOption ("surpress_blizzard_cd_timer", true)
			unitFrame.ExtraIconFrame:SetOption ("decimal_timer", Plater.db.profile.extra_icon_timer_decimals)
			unitFrame.ExtraIconFrame:SetOption ("cooldown_reverse", Plater.db.profile.extra_icon_cooldown_reverse)
			unitFrame.ExtraIconFrame:SetOption ("cooldown_swipe_enabled", Plater.db.profile.extra_icon_show_swipe)
			unitFrame.ExtraIconFrame:SetOption ("cooldown_edge_texture", Plater.db.profile.extra_icon_cooldown_edge_texture)
			
			--> update refresh ID
			unitFrame.ExtraIconFrame.RefreshID = PLATER_REFRESH_ID
		end
		
		--update options in the boss mods icons frame
		Plater.UpdateBossModAuraFrameSettings(unitFrame, PLATER_REFRESH_ID)
		
		--> details! integration
		if (IS_USING_DETAILS_INTEGRATION) then
			local detailsPlaterConfig = Details.plater

			if (detailsPlaterConfig.realtime_dps_enabled) then
				local textString = healthBar.DetailsRealTime
				Plater.SetAnchor (textString, detailsPlaterConfig.realtime_dps_anchor)
				DF:SetFontSize (textString, detailsPlaterConfig.realtime_dps_size)
				DF:SetFontOutline (textString, detailsPlaterConfig.realtime_dps_shadow)
				DF:SetFontColor (textString, detailsPlaterConfig.realtime_dps_color)
			end
			
			if (detailsPlaterConfig.realtime_dps_player_enabled) then
				local textString = healthBar.DetailsRealTimeFromPlayer
				Plater.SetAnchor (textString, detailsPlaterConfig.realtime_dps_player_anchor)
				DF:SetFontSize (textString, detailsPlaterConfig.realtime_dps_player_size)
				DF:SetFontOutline (textString, detailsPlaterConfig.realtime_dps_player_shadow)
				DF:SetFontColor (textString, detailsPlaterConfig.realtime_dps_player_color)
			end
			
			if (detailsPlaterConfig.damage_taken_enabled) then
				local textString = healthBar.DetailsDamageTaken
				Plater.SetAnchor (textString, detailsPlaterConfig.damage_taken_anchor)
				DF:SetFontSize (textString, detailsPlaterConfig.damage_taken_size)
				DF:SetFontOutline (textString, detailsPlaterConfig.damage_taken_shadow)
				DF:SetFontColor (textString, detailsPlaterConfig.damage_taken_color)
			end
			
			--reset all labels used by details!
			healthBar.DetailsRealTime:SetText ("")
			healthBar.DetailsRealTimeFromPlayer:SetText ("")
			healthBar.DetailsDamageTaken:SetText ("")
		end
		
		if (plateFrame.OnTickFrame.actorType == actorType and plateFrame.OnTickFrame.unit == unitFrame [MEMBER_UNITID]) then
			Plater.NameplateTick (plateFrame.OnTickFrame, 10)
		end
		
		Plater.EndLogPerformanceCore("Plater-Core", "Update", "UpdatePlateFrame")
	end

	-- ~border
	--changes the border color, this call is used internally on Plater
	--see Plater.SetBorderColor for scripting calls
	--currently this is called for threat color changes (if enabled at the options panel)
	function Plater.ForceChangeBorderColor (self, r, g, b) --private --self = unitFrame
		--this call is from the retail game, file: blizzard_nameplates.lua
		if (not self.customBorderColor) then
			self.healthBar.border:SetVertexColor (r, g, b)
			self.BorderIsAggroIndicator = true
		end
	end
	
	--> update the border color respecting custom colors set by scripts
	function Plater.UpdateBorderColor (self) --self is unitFrame
		--set the border color
		if (not self.customBorderColor) then
			self.healthBar.border:SetVertexColor (DB_BORDER_COLOR_R, DB_BORDER_COLOR_G, DB_BORDER_COLOR_B, DB_BORDER_COLOR_A)
			self.powerBar.border:SetVertexColor (DB_BORDER_COLOR_R, DB_BORDER_COLOR_G, DB_BORDER_COLOR_B, DB_BORDER_COLOR_A)
		else
			self.healthBar.border:SetVertexColor (unpack (self.customBorderColor))
			self.powerBar.border:SetVertexColor (unpack (self.customBorderColor))
		end
	end

	function Plater.UpdatePlateBorders (plateFrame) --private
		--if didn't pass a plate to update, update all frames
		if (not plateFrame) then
			for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
				Plater.UpdatePlateBorders (plateFrame)
			end
			return
		end
		
		Plater.UpdatePlateBorderThickness (plateFrame)
		
		--ignore if the border is being used internally as threat indicator
		if (plateFrame.unitFrame.healthBar.BorderIsAggroIndicator) then
			return
		end
		
		Plater.UpdateBorderColor (plateFrame.unitFrame)
	end

	function Plater.UpdatePlateBorderThickness (plateFrame)
		if (not plateFrame) then
			for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
				Plater.UpdatePlateBorderThickness (plateFrame)
			end
			return
		end
		
		plateFrame.unitFrame.healthBar.border:SetBorderSizes (DB_BORDER_THICKNESS, DB_BORDER_THICKNESS, DB_BORDER_THICKNESS, DB_BORDER_THICKNESS)
		plateFrame.unitFrame.healthBar.border:UpdateSizes()
		
		plateFrame.unitFrame.powerBar.border:SetBorderSizes (DB_BORDER_THICKNESS, DB_BORDER_THICKNESS, DB_BORDER_THICKNESS, DB_BORDER_THICKNESS)
		plateFrame.unitFrame.powerBar.border:UpdateSizes()
	end

	-- ~raidmarker ~raidtarget
	function Plater.UpdatePlateRaidMarker (plateFrame)
		local index = GetRaidTargetIndex (plateFrame.unitFrame [MEMBER_UNITID])

		if (index and not plateFrame.IsSelf) then
			local icon = plateFrame.unitFrame.PlaterRaidTargetFrame.RaidTargetIcon
			SetRaidTargetIconTexture (icon, index)
			icon:Show()
			
			if (not icon.IsShowning) then
				--play animations
				icon.IsShowning = true
				icon.ShowAnimation:Play()
			end
			
			--adjust scale and anchor
			plateFrame.unitFrame.PlaterRaidTargetFrame:SetScale (Plater.db.profile.indicator_raidmark_scale)
			Plater.SetAnchor (plateFrame.unitFrame.PlaterRaidTargetFrame, Plater.db.profile.indicator_raidmark_anchor)
			
			--adjust frame level:
			plateFrame.unitFrame.PlaterRaidTargetFrame:SetFrameStrata(plateFrame.unitFrame.healthBar:GetFrameStrata())
			plateFrame.unitFrame.PlaterRaidTargetFrame:SetFrameLevel(plateFrame.unitFrame.healthBar:GetFrameLevel() + 25)
			
			--mini mark inside the nameplate
			if (Plater.db.profile.indicator_extra_raidmark) then
				plateFrame.RaidTarget:Show()
				plateFrame.RaidTarget:SetTexture (icon:GetTexture())
				plateFrame.RaidTarget:SetTexCoord (icon:GetTexCoord())
				
				local height = plateFrame.unitFrame.healthBar:GetHeight() - 2
				plateFrame.RaidTarget:SetSize (height, height)
				plateFrame.RaidTarget:SetAlpha (.4)
			end
		else
			plateFrame.unitFrame.PlaterRaidTargetFrame.RaidTargetIcon.IsShowning = nil
			plateFrame.unitFrame.PlaterRaidTargetFrame.RaidTargetIcon:Hide()
			--hide the extra raid target inside the nameplate
			plateFrame.RaidTarget:Hide()
		end
	end

	--iterate among all nameplates and update the raid target icon
	function Plater.UpdateRaidMarkersOnAllNameplates() --private
		for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
			if plateFrame.unitFrame.PlaterOnScreen then
				Plater.UpdatePlateRaidMarker (plateFrame)
				
				--hooks
				if (HOOK_RAID_TARGET.ScriptAmount > 0) then
					for i = 1, HOOK_RAID_TARGET.ScriptAmount do
						local globalScriptObject = HOOK_RAID_TARGET [i]
						local unitFrame = plateFrame.unitFrame
						local scriptContainer = unitFrame:ScriptGetContainer()
						local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Raid Target")
						--run
						unitFrame:ScriptRunHook (scriptInfo, "Raid Target")
					end
				end
			end
		end
	end

	-- ~indicators
	function Plater.UpdateIndicators (plateFrame, actorType, regenDisabled)
		--limpa os indicadores
		Plater.ClearIndicators (plateFrame)
		local config = Plater.db.profile
		
		if (actorType == ACTORTYPE_ENEMY_PLAYER) then
			if (config.indicator_faction) then
				--don't show faction icon on arena of battleground, it's kinda useless (terciob july 2022)
				if (Plater.ZoneInstanceType ~= "pvp" and Plater.ZoneInstanceType ~= "arena") then
					Plater.AddIndicator (plateFrame, UnitFactionGroup (plateFrame.unitFrame [MEMBER_UNITID]))
				end
			end

			if (config.indicator_enemyclass) then
				Plater.AddIndicator (plateFrame, "classicon")
			end

			--don't show spec icon during combat, it occupies a valuable space (terciob july 2022)
			if (config.indicator_spec and (config.indicator_spec_always or (not InCombatLockdown() and not regenDisabled))) then
				-- use BG info if available
				local texture, L, R, T, B = Plater.GetSpecIconForUnitFromBG(plateFrame.unitFrame [MEMBER_UNITID])
				if texture then
					Plater.AddIndicator (plateFrame, "specicon", texture, L, R, T, B)
				else
					--> check if the user is using details
					if (Details and Details.realversion >= 134) then
						local spec = Details:GetSpecByGUID (plateFrame [MEMBER_GUID])
						if (spec) then
							local texture, L, R, T, B = Details:GetSpecIcon (spec)
							Plater.AddIndicator (plateFrame, "specicon", texture, L, R, T, B)
						end
					end
				end
			end
		
		elseif (actorType == ACTORTYPE_FRIENDLY_PLAYER) then
			if (config.indicator_friendlyfaction) then
				Plater.AddIndicator (plateFrame, UnitFactionGroup (plateFrame.unitFrame [MEMBER_UNITID]))
			end
			if (config.indicator_friendlyclass) then
				Plater.AddIndicator (plateFrame, "classicon")
			end
			if (config.indicator_friendlyspec) then
				-- use BG info if available
				local texture, L, R, T, B = Plater.GetSpecIconForUnitFromBG(plateFrame [MEMBER_UNITID])
				if texture then
					Plater.AddIndicator (plateFrame, "specicon", texture, L, R, T, B)
				else
					--> check if the user is using details
					if (Details and Details.realversion >= 134) then
						local spec = Details:GetSpecByGUID (plateFrame [MEMBER_GUID])
						if (spec) then
							local texture, L, R, T, B = Details:GetSpecIcon (spec)
							Plater.AddIndicator (plateFrame, "specicon", texture, L, R, T, B)
						end
					end
				end
			end
			
		elseif (actorType == ACTORTYPE_ENEMY_NPC) then
		
			--is a pet
			if (PET_CACHE [plateFrame [MEMBER_GUID]]) then
				if (config.indicator_pet) then
					Plater.AddIndicator (plateFrame, "pet")
				end
			end

			--classification
			local unitClassification = UnitClassification (plateFrame.unitFrame [MEMBER_UNITID]) --elite minus normal rare rareelite worldboss
			if (unitClassification == "worldboss" and config.indicator_worldboss) then
				Plater.AddIndicator (plateFrame, "worldboss")
				
			elseif (unitClassification == "rareelite" and (config.indicator_rare or config.indicator_elite)) then
				Plater.AddIndicator (plateFrame, "elite")
				Plater.AddIndicator (plateFrame, "rare")
			else
				if (unitClassification == "elite" and config.indicator_elite) then
					Plater.AddIndicator (plateFrame, "elite")
				end
				if (unitClassification == "rare" and config.indicator_rare) then
					Plater.AddIndicator (plateFrame, "rare")
				end
			end
			
			--quest boss
			local isQuestBoss = (IS_WOW_PROJECT_MAINLINE) and UnitIsQuestBoss (plateFrame.unitFrame [MEMBER_UNITID]) or false --true false
			if (isQuestBoss and config.indicator_quest) then
				Plater.AddIndicator (plateFrame, "quest")
			end
		
		elseif (actorType == ACTORTYPE_FRIENDLY_NPC and config.indicator_quest) then
			if (plateFrame [MEMBER_QUEST]) then
				Plater.AddIndicator (plateFrame, "quest")
			end
		end
		
		--custom indicators from scripts
		for i = 1, #plateFrame.unitFrame.CustomIndicators do
			Plater.AddIndicator (plateFrame, "custom", unpack (plateFrame.unitFrame.CustomIndicators [i]))
		end
	end

	function Plater.AddIndicator (plateFrame, indicator, ...)

		local thisIndicator = plateFrame.IconIndicators [plateFrame.IconIndicators.Next]
		
		if (not thisIndicator) then
			local newIndicator = plateFrame.unitFrame.healthBar:CreateTexture (nil, "overlay")
			newIndicator:SetDrawLayer("OVERLAY", 7)
			newIndicator:SetSize (10, 10)
			tinsert (plateFrame.IconIndicators, newIndicator)
			thisIndicator = newIndicator
		end

		thisIndicator:Show()
		thisIndicator:SetTexCoord (0, 1, 0, 1)
		thisIndicator:SetVertexColor (1, 1, 1)
		thisIndicator:SetDesaturated (false)
		thisIndicator:SetSize (10, 10)
		thisIndicator:SetScale (Plater.db.profile.indicator_scale)
		
		-- ~icons
		if (indicator == "pet") then
			thisIndicator:SetTexture ([[Interface\AddOns\Plater\images\peticon]])
			
		elseif (indicator == "Horde") then
			thisIndicator:SetTexture ([[Interface\PVPFrame\PVP-Currency-Horde]])
			thisIndicator:SetSize (12, 12)

		elseif (indicator == "Alliance") then
			thisIndicator:SetTexture ([[Interface\PVPFrame\PVP-Currency-Alliance]])
			thisIndicator:SetTexCoord (4/32, 29/32, 2/32, 30/32)
			thisIndicator:SetSize (12, 12)
			
		elseif (indicator == "elite") then
			thisIndicator:SetTexture ([[Interface\GLUES\CharacterSelect\Glues-AddOn-Icons]])
			thisIndicator:SetTexCoord (0.75, 1, 0, 1)
			thisIndicator:SetVertexColor (1, .8, 0)
			thisIndicator:SetSize (12, 12)
			
		elseif (indicator == "rare") then
			thisIndicator:SetTexture ([[Interface\GLUES\CharacterSelect\Glues-AddOn-Icons]])
			thisIndicator:SetTexCoord (0.75, 1, 0, 1)
			thisIndicator:SetSize (12, 12)
			thisIndicator:SetDesaturated (true)
			
		elseif (indicator == "quest") then
			thisIndicator:SetTexture ([[Interface\TARGETINGFRAME\PortraitQuestBadge]])
			thisIndicator:SetTexCoord (2/32, 26/32, 1/32, 31/32)
			
		elseif (indicator == "classicon") then
			local _, class = UnitClass (plateFrame.unitFrame [MEMBER_UNITID])
			if (class) then
				thisIndicator:SetTexture ([[Interface\GLUES\CHARACTERCREATE\UI-CharacterCreate-Classes]])
				thisIndicator:SetTexCoord (unpack (CLASS_ICON_TCOORDS [class]))
			end
		
		elseif (indicator == "specicon") then
			local texture, L, R, T, B = ...
			thisIndicator:SetTexture (texture)
			thisIndicator:SetTexCoord (L, R, T, B)
		
		elseif (indicator == "worldboss") then
			thisIndicator:SetTexture ([[Interface\Scenarios\ScenarioIcon-Boss]])
		
		elseif (indicator == "custom") then
			local texture, width, height, color, L, R, T, B = ...
			thisIndicator:SetTexture (texture)
			thisIndicator:SetSize (width, height)
			thisIndicator:SetTexCoord (L, R, T, B)
			local r, g, b = DF:ParseColors (color)
			thisIndicator:SetVertexColor (r, g, b)
		end
		
		if (plateFrame.IconIndicators.Next == 1) then
			Plater.SetAnchor (thisIndicator, Plater.db.profile.indicator_anchor)
		else
			local attachTo = plateFrame.IconIndicators [plateFrame.IconIndicators.Next - 1]
			--se for menor que 4 ele deve crescer para o lado da esquerda, nos outros casos vai para a direita
			if (Plater.db.profile.indicator_anchor.side < 4) then
				PixelUtil.SetPoint (thisIndicator, "right", attachTo, "left", -2, 0)
			else
				PixelUtil.SetPoint (thisIndicator, "left", attachTo, "right", 1, 0)
			end
		end
		
		plateFrame.IconIndicators.Next = plateFrame.IconIndicators.Next + 1
	end

	function Plater.ClearIndicators (plateFrame)
		for _, indicator in ipairs (plateFrame.IconIndicators) do
			indicator:Hide()
			indicator:ClearAllPoints()
		end
		plateFrame.IconIndicators.Next = 1
	end
	


--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> misc stuff - general functions ~misc

	--auto toggle the show friendly players, and other stuff.
	function Plater.RefreshAutoToggle(combat, leavingCombat) --private

		if Plater.HasRefreshAutoToggleScheduled and combat == nil then
			return
		elseif leavingCombat then
			if Plater.HasRefreshAutoToggleScheduled then
				Plater.HasRefreshAutoToggleScheduled:Cancel()
			end
			
			Plater.HasRefreshAutoToggleScheduled = C_Timer.NewTimer (1.5, function()
					Plater.HasRefreshAutoToggleScheduled = nil
					Plater.RefreshAutoToggle(false) 
				end) --schedule
			return
			
		elseif not leavingCombat and Plater.HasRefreshAutoToggleScheduled then
			if combat then
				Plater.HasRefreshAutoToggleScheduled:Cancel()
				Plater.HasRefreshAutoToggleScheduled = nil
			else
				return
			end
		end
		
		if ((combat == nil) and InCombatLockdown()) then
			C_Timer.After (0.5, function() Plater.RefreshAutoToggle() end)
			return
		end
		
		local zoneName, zoneType = GetInstanceInfo()
		local profile = Plater.db.profile
		
		-- combat toggle
		if (profile.auto_toggle_combat_enabled and (combat ~= nil)) then
			local onlyNamesEnabled = GetCVarBool("nameplateShowOnlyNames")
			local onlyNamesEnabledRaw = GetCVar("nameplateShowOnlyNames")
			
			--NamePlateDriverFrame:UnregisterEvent("CVAR_UPDATE")
			if combat or InCombatLockdown() then -- update this separately and only if needed
				if onlyNamesEnabled ~= profile.auto_toggle_combat.blizz_healthbar_ic then
					SetCVar("nameplateShowOnlyNames", profile.auto_toggle_combat.blizz_healthbar_ic and CVAR_ENABLED or CVAR_DISABLED)
					Plater.UpdateBaseNameplateOptions()
				end
			else
				if onlyNamesEnabled ~= profile.auto_toggle_combat.blizz_healthbar_ooc then
					SetCVar("nameplateShowOnlyNames", profile.auto_toggle_combat.blizz_healthbar_ooc and CVAR_ENABLED or CVAR_DISABLED)
					--Plater.UpdateBaseNameplateOptions()
				end
			end
			--NamePlateDriverFrame:RegisterEvent("CVAR_UPDATE")
			
			if combat then
				SetCVar("nameplateShowFriends", profile.auto_toggle_combat.friendly_ic and CVAR_ENABLED or CVAR_DISABLED)
				SetCVar("nameplateShowEnemies", profile.auto_toggle_combat.enemy_ic and CVAR_ENABLED or CVAR_DISABLED)
				return
			else
				SetCVar("nameplateShowFriends", profile.auto_toggle_combat.friendly_ooc and CVAR_ENABLED or CVAR_DISABLED)
				SetCVar("nameplateShowEnemies", profile.auto_toggle_combat.enemy_ooc and CVAR_ENABLED or CVAR_DISABLED)
			end
		end

		-- dungeon/raid toggle pets/totems
		if not combat then -- just do this out of combat to counter some weird errors
			if (profile.auto_inside_raid_dungeon.hide_enemy_player_pets) then
				local showEnemyPets = GetCVarBool("nameplateShowEnemyPets")
				if (zoneType == "party" or zoneType == "raid") then
					if showEnemyPets ~= CVAR_DISABLED then
						SetCVar("nameplateShowEnemyPets", CVAR_DISABLED)
					end
				else
					if showEnemyPets ~= CVAR_ENABLED then
						SetCVar("nameplateShowEnemyPets", CVAR_ENABLED)
					end
				end
			end
			if (profile.auto_inside_raid_dungeon.hide_enemy_player_totems) then
				local showEnemyTotems = GetCVarBool("nameplateShowEnemyTotems")
				if (zoneType == "party" or zoneType == "raid") then
					if showEnemyTotems ~= CVAR_DISABLED then
						SetCVar("nameplateShowEnemyTotems", CVAR_DISABLED)
					end
				else
					if showEnemyTotems ~= CVAR_ENABLED then
						SetCVar("nameplateShowEnemyTotems", CVAR_ENABLED)
					end
				end
			end
		end
		
		--stacking toggle
		if (profile.auto_toggle_stacking_enabled and profile.stacking_nameplates_enabled) then
			--discover which is the map type the player is in
			if (zoneType == "party") then
				SetCVar ("nameplateMotion", profile.auto_toggle_stacking ["party"] and CVAR_ENABLED or CVAR_DISABLED)
				
			elseif (zoneType == "raid") then
				SetCVar ("nameplateMotion", profile.auto_toggle_stacking ["raid"] and CVAR_ENABLED or CVAR_DISABLED)
				
			elseif (zoneType == "arena" or zoneType == "pvp") then
				SetCVar ("nameplateMotion", profile.auto_toggle_stacking ["arena"] and CVAR_ENABLED or CVAR_DISABLED)
				
			else
				--if the player is resting, consider inside a major city
				if (IsResting()) then
					SetCVar ("nameplateMotion", profile.auto_toggle_stacking ["cities"] and CVAR_ENABLED or CVAR_DISABLED)
				else
					SetCVar ("nameplateMotion", profile.auto_toggle_stacking ["world"] and CVAR_ENABLED or CVAR_DISABLED)
				end
			end
		end
		
		if combat then return end

		--friendly nameplate toggle
		if (profile.auto_toggle_friendly_enabled) then
			--discover which is the map type the player is in
			if (zoneType == "party") then
				SetCVar ("nameplateShowFriends", profile.auto_toggle_friendly ["party"] and CVAR_ENABLED or CVAR_DISABLED)
				
			elseif (zoneType == "raid") then
				SetCVar ("nameplateShowFriends", profile.auto_toggle_friendly ["raid"] and CVAR_ENABLED or CVAR_DISABLED)
				
			elseif (zoneType == "arena" or zoneType == "pvp") then
				SetCVar ("nameplateShowFriends", profile.auto_toggle_friendly ["arena"] and CVAR_ENABLED or CVAR_DISABLED)
				
			else
				--if the player is resting, consider inside a major city
				if (IsResting()) then
					SetCVar ("nameplateShowFriends", profile.auto_toggle_friendly ["cities"] and CVAR_ENABLED or CVAR_DISABLED)
				else
					SetCVar ("nameplateShowFriends", profile.auto_toggle_friendly ["world"] and CVAR_ENABLED or CVAR_DISABLED)
				end
			end
		end
		
		--enemy nameplate toggle
		if (profile.auto_toggle_enemy_enabled) then
			--discover which is the map type the player is in
			if (zoneType == "party") then
				SetCVar ("nameplateShowEnemies", profile.auto_toggle_enemy ["party"] and CVAR_ENABLED or CVAR_DISABLED)
				
			elseif (zoneType == "raid") then
				SetCVar ("nameplateShowEnemies", profile.auto_toggle_enemy ["raid"] and CVAR_ENABLED or CVAR_DISABLED)
				
			elseif (zoneType == "arena" or zoneType == "pvp") then
				SetCVar ("nameplateShowEnemies", profile.auto_toggle_enemy ["arena"] and CVAR_ENABLED or CVAR_DISABLED)
				
			else
				--if the player is resting, consider inside a major city
				if (IsResting()) then
					SetCVar ("nameplateShowEnemies", profile.auto_toggle_enemy ["cities"] and CVAR_ENABLED or CVAR_DISABLED)
				else
					SetCVar ("nameplateShowEnemies", profile.auto_toggle_enemy ["world"] and CVAR_ENABLED or CVAR_DISABLED)
				end
			end
		end
	end

	local anchor_functions = {
		function (widget, config, attachTo, centered)--1 topleft
			widget:ClearAllPoints()
			local widgetRelative = centered and "bottom" or "bottomleft"
			PixelUtil.SetPoint (widget, widgetRelative, attachTo, "topleft", config.x, config.y, 0, 0)
		end,
		function (widget, config, attachTo, centered)--2 left
			widget:ClearAllPoints()
			local widgetRelative = centered and "center" or "right"
			PixelUtil.SetPoint (widget, widgetRelative, attachTo, "left", config.x, config.y, 0, 0)
		end,
		function (widget, config, attachTo, centered)--3 bottomleft
			widget:ClearAllPoints()
			local widgetRelative = centered and "top" or "topleft"
			PixelUtil.SetPoint (widget, widgetRelative, attachTo, "bottomleft", config.x, config.y, 0, 0)
		end,
		function (widget, config, attachTo, centered)--4 bottom
			widget:ClearAllPoints()
			PixelUtil.SetPoint (widget, "top", attachTo, "bottom", config.x, config.y, 0, 0)
		end,
		function (widget, config, attachTo, centered)--5 bottomright
			widget:ClearAllPoints()
			local widgetRelative = centered and "top" or "topright"
			PixelUtil.SetPoint (widget, widgetRelative, attachTo, "bottomright", config.x, config.y, 0, 0)
		end,
		function (widget, config, attachTo, centered)--6 right
			widget:ClearAllPoints()
			local widgetRelative = centered and "center" or "left"
			PixelUtil.SetPoint (widget, widgetRelative, attachTo, "right", config.x, config.y, 0, 0)
		end,
		function (widget, config, attachTo, centered)--7 topright
			widget:ClearAllPoints()
			local widgetRelative = centered and "bottom" or "bottomright"
			PixelUtil.SetPoint (widget, widgetRelative, attachTo, "topright", config.x, config.y, 0, 0)
		end,
		function (widget, config, attachTo, centered)--8 top
			widget:ClearAllPoints()
			PixelUtil.SetPoint (widget, "bottom", attachTo, "top", config.x, config.y, 0, 0)
		end,
		function (widget, config, attachTo, centered)--9 center
			widget:ClearAllPoints()
			PixelUtil.SetPoint (widget, "center", attachTo, "center", config.x, config.y, 0, 0)
		end,
		function (widget, config, attachTo, centered)--10 inner left
			widget:ClearAllPoints()
			PixelUtil.SetPoint (widget, "left", attachTo, "left", config.x, config.y, 0, 0)
		end,
		function (widget, config, attachTo, centered)--11 inner right
			widget:ClearAllPoints()
			PixelUtil.SetPoint (widget, "right", attachTo, "right", config.x, config.y, 0, 0)
		end,
		function (widget, config, attachTo, centered)--12 inner top
			widget:ClearAllPoints()
			PixelUtil.SetPoint (widget, "top", attachTo, "top", config.x, config.y, 0, 0)
		end,
		function (widget, config, attachTo, centered)--13 inner bottom
			widget:ClearAllPoints()
			PixelUtil.SetPoint (widget, "bottom", attachTo, "bottom", config.x, config.y, 0, 0)
		end
	}

	--auto set the point based on the table from the config, if attachTo isn't received, it'll use its parent
	function Plater.SetAnchor (widget, config, attachTo, centered) --private
		attachTo = attachTo or widget:GetParent()
		anchor_functions [config.side] (widget, config, attachTo, centered)
	end
	
	-- anchor sides as comprehensive table.
	Plater.AnchorSides = {
        TOP_LEFT = 1,
        LEFT = 2,
        BOTTOM_LEFT = 3,
        BOTTOM = 4,
        BOTTOM_RIGHT = 5,
        RIGHT = 6,
        TOP_RIGHT = 7,
        TOP = 8,
        CENTER = 9,
        INNER_LEFT = 10,
        INNER_RIGHT = 11,
        INNER_TOP = 12,
        INNER_BOTTOM = 13,
    }

	--check the setting 'only_damaged' and 'only_thename' for player characters. not critical code, can run slow
	function Plater.ParseHealthSettingForPlayer (plateFrame, force) --private
		local isFriendlyPlayerWithoutHealthBar = plateFrame.IsFriendlyPlayerWithoutHealthBar
		if (DB_PLATE_CONFIG [ACTORTYPE_FRIENDLY_PLAYER].only_thename and not DB_PLATE_CONFIG [ACTORTYPE_FRIENDLY_PLAYER].only_damaged) then
			if (not isFriendlyPlayerWithoutHealthBar) or force then
				Plater.HideHealthBar (plateFrame.unitFrame, true)
			end
			
		elseif (DB_PLATE_CONFIG [ACTORTYPE_FRIENDLY_PLAYER].only_damaged) then
			local healthBar = plateFrame.unitFrame.healthBar
			if ((healthBar.currentHealth or 1) < (healthBar.currentHealthMax or 1)) then
				if isFriendlyPlayerWithoutHealthBar or force then
					Plater.ShowHealthBar (plateFrame.unitFrame)
				end
				
			elseif (not isFriendlyPlayerWithoutHealthBar) or force then
				Plater.HideHealthBar (plateFrame.unitFrame, true)
			end
			
		elseif isFriendlyPlayerWithoutHealthBar or force then
			Plater.ShowHealthBar (plateFrame.unitFrame)
		end
	end

	function Plater.GetPlateAlpha (plateFrame)
		return plateFrame and plateFrame.unitFrame and plateFrame.unitFrame:GetAlpha() or -1
	end

	local widget_set_alpha = function (widget, value)
		if (widget.FadeAnimation:IsPlaying()) then
			widget.FadeAnimation:Stop()
		end
		widget.FadeAnimation.Animation:SetFromAlpha (widget:GetAlpha())
		widget.FadeAnimation.Animation:SetToAlpha (value)
		widget.FadeAnimation:Play()
	end	

	local on_finished_fade_animation = function (fadeAnimation)
		local widget = fadeAnimation:GetParent()
		widget:SetAlpha(fadeAnimation.Animation:GetToAlpha())
	end

	Plater.CreateAlphaAnimation = function (plateFrame)
		local unitFrame = plateFrame.unitFrame
		local healthBar = plateFrame.unitFrame.healthBar
		local castBar = plateFrame.unitFrame.castBar
		local powerBar = plateFrame.unitFrame.powerBar
		local buffFrame = plateFrame.unitFrame.BuffFrame
		local buffFrame2 = plateFrame.unitFrame.BuffFrame2

		local allWidgets = {
			unitFrame, healthBar, castBar, buffFrame, buffFrame2, powerBar
		}

		for i = 1, #allWidgets do
			local widget = allWidgets[i]
			widget.FadeAnimation = widget:CreateAnimationGroup()
			--widget.FadeAnimation:SetScript ("OnPlay", on_play_fade_animation)
			widget.FadeAnimation:SetScript ("OnFinished", on_finished_fade_animation)
			widget.FadeAnimation.Animation = widget.FadeAnimation:CreateAnimation ("Alpha")
			widget.FadeAnimation.Animation:SetOrder (1)
			widget.FadeAnimation.Animation:SetDuration (0.15)
			widget.SetAlphaTo = widget_set_alpha
		end
	end

	function Plater.CheckHighlight (self)
		if (UnitIsUnit ("mouseover", self.unit)) then
			self.HighlightTexture:Show()
		else
			self.HighlightTexture:Hide()
		end
	end
	
	--create a new frame for the highlight (when the mouse passes over the nameplate)
	function Plater.CreateHighlightNameplate (plateFrame) --private
		local highlightOverlay = CreateFrame ("frame", "$parentHighlightOverlay", plateFrame.unitFrame.healthBar, BackdropTemplateMixin and "BackdropTemplate") --why this was parented to UIParent (question mark)
		highlightOverlay:EnableMouse (false)
		highlightOverlay:SetAllPoints()
		highlightOverlay:SetScript ("OnUpdate", Plater.CheckHighlight)
		highlightOverlay:Hide()
		--highlightOverlay:SetFrameStrata ("TOOLTIP") --it'll use the same strata as the health bar now
		
		highlightOverlay.HighlightTexture = plateFrame.unitFrame.healthBar:CreateTexture (nil, "overlay")
		highlightOverlay.HighlightTexture:SetAllPoints()
		highlightOverlay.HighlightTexture:SetColorTexture (1, 1, 1, 1)
		highlightOverlay.HighlightTexture:SetAlpha (1)
		highlightOverlay.HighlightTexture:Hide()
		
		plateFrame.unitFrame.HighlightFrame = highlightOverlay
	end
	
	function Plater.EnableHighlight (unitFrame)
		unitFrame.HighlightFrame:Show()
		unitFrame.HighlightFrame.HighlightTexture:Show()

		unitFrame.HighlightFrame.unit = unitFrame [MEMBER_UNITID]
		unitFrame.HighlightFrame:SetScript ("OnUpdate", Plater.CheckHighlight)
	end
	
	function Plater.DisableHighlight (unitFrame)
		unitFrame.HighlightFrame:SetScript ("OnUpdate", nil)
		unitFrame.HighlightFrame:Hide()
		unitFrame.HighlightFrame.HighlightTexture:Hide()
	end
	
	function Plater.CreateHealthFlashFrame (plateFrame) --private
		local f_anim = CreateFrame ("frame", nil, plateFrame.unitFrame.healthBar, BackdropTemplateMixin and "BackdropTemplate")
		f_anim:SetFrameLevel (plateFrame.unitFrame.healthBar:GetFrameLevel()-1)
		f_anim:SetPoint ("topleft", plateFrame.unitFrame.healthBar, "topleft", -2, 2)
		f_anim:SetPoint ("bottomright", plateFrame.unitFrame.healthBar, "bottomright", 2, -2)
		plateFrame.unitFrame.healthBar.canHealthFlash = true
		
		local t = f_anim:CreateTexture (nil, "artwork")
		t:SetColorTexture (1, 1, 1, 1)
		t:SetAllPoints()
		t:SetBlendMode ("ADD")
		f_anim.texture = t
		
		local animation = t:CreateAnimationGroup()
		local anim1 = animation:CreateAnimation ("Alpha")
		local anim2 = animation:CreateAnimation ("Alpha")
		local anim3 = animation:CreateAnimation ("Alpha")
		anim1:SetOrder (1)
		anim1:SetFromAlpha (0)
		anim1:SetToAlpha (1)
		anim1:SetDuration (0.1)
		anim2:SetOrder (2)
		anim2:SetFromAlpha (1)
		anim2:SetToAlpha (0)
		anim2:SetDuration (0.1)
		anim3:SetOrder (3)
		anim3:SetFromAlpha (0)
		anim3:SetToAlpha (1)
		anim3:SetDuration (0.1)
		f_anim.animation = animation

		animation:SetScript ("OnFinished", function (self)
			f_anim:Hide()
		end)
		animation:SetScript ("OnPlay", function (self)
			f_anim:Show()
		end)

		local do_flash_anim = function (duration)
			if (not plateFrame.unitFrame.healthBar.canHealthFlash) then
				return
			end
			plateFrame.unitFrame.healthBar.canHealthFlash = false
			
			duration = duration or 0.1
			
			anim1:SetDuration (duration)
			anim2:SetDuration (duration)
			anim3:SetDuration (duration)
			
			f_anim:Show()
			animation:Play()
		end
		
		f_anim:Hide()
		plateFrame.unitFrame.healthBar.HealthFlashFrame = f_anim
		plateFrame.unitFrame.healthBar.PlayHealthFlash = do_flash_anim
	end

	function Plater.CreateAggroFlashFrame (plateFrame) --private

		local f_anim = CreateFrame ("frame", nil, plateFrame.unitFrame.healthBar, BackdropTemplateMixin and "BackdropTemplate")
		--local f_anim = CreateFrame ("frame", nil, plateFrame, BackdropTemplateMixin and "BackdropTemplate")
		f_anim:SetFrameLevel (plateFrame.unitFrame.healthBar:GetFrameLevel()+3)
		f_anim:SetPoint ("topleft", plateFrame.unitFrame.healthBar, "topleft")
		f_anim:SetPoint ("bottomright", plateFrame.unitFrame.healthBar, "bottomright")
		
		local t = f_anim:CreateTexture (nil, "artwork")
		--t:SetTexCoord (0, 0.78125, 0, 0.66796875)
		--t:SetTexture ([[Interface\AchievementFrame\UI-Achievement-Alert-Glow]])
		t:SetColorTexture (1, 1, 1, 1)
		t:SetAllPoints()
		t:SetBlendMode ("ADD")
		f_anim.texture = t
		local s = f_anim:CreateFontString (nil, "overlay", "GameFontNormal")
		s:SetText ("-AGGRO-")
		s:SetTextColor (.70, .70, .70)
		s:SetPoint ("center", t, "center")
		f_anim.text = s
		
		local animation = t:CreateAnimationGroup()
		local anim1 = animation:CreateAnimation ("Alpha")
		local anim2 = animation:CreateAnimation ("Alpha")
		anim1:SetOrder (1)
		anim1:SetFromAlpha (0)
		anim1:SetToAlpha (1)
		anim1:SetDuration (0.2)
		anim2:SetOrder (2)
		anim2:SetFromAlpha (1)
		anim2:SetToAlpha (0)
		anim2:SetDuration (0.2)
		f_anim.animation = animation
		
		animation:SetScript ("OnFinished", function (self)
			f_anim:Hide()
		end)
		animation:SetScript ("OnPlay", function (self)
			f_anim:Show()
		end)

		local do_flash_anim = function (text, duration, ignoreCooldown)
			if (not ignoreCooldown and Plater.CombatTime+5 > GetTime()) then
				return
			end
			
			text = text or ""
			duration = duration or 0.2
			
			anim1:SetDuration (duration)
			anim2:SetDuration (duration)
			
			s:SetText (text)
			f_anim:Show()
			animation:Play()
		end
		
		f_anim:Hide()
		plateFrame.unitFrame.BodyFlashFrame = f_anim
		plateFrame.PlayBodyFlash = do_flash_anim
	end	
	

	--> animation with acceleration ~animation ~healthbaranimation
	function Plater.AnimateLeftWithAccel (self, deltaTime)
		local distance = (self.AnimationStart - self.AnimationEnd) / self.CurrentHealthMax * 100	--scale 1 - 100
		local minTravel = min (distance / 10, 3) -- 10 = trigger distance to max speed 3 = speed scale on max travel
		local maxTravel = max (minTravel, 0.45) -- 0.45 = min scale speed on low travel speed
		local calcAnimationSpeed = (self.CurrentHealthMax * (deltaTime * DB_ANIMATION_TIME_DILATATION)) * maxTravel --re-scale back to unit health, scale with delta time and scale with the travel speed
		
		self.AnimationStart = self.CurrentHealthMax == 0 and 1 or self.AnimationStart - calcAnimationSpeed
		self:SetValue (self.AnimationStart)
		self.CurrentHealth = self.AnimationStart
		
		if (self.Spark) then
			self.Spark:SetPoint ("center", self, "left", self.AnimationStart / self.CurrentHealthMax * self:GetWidth(), 0)
			self.Spark:Show()
		end
		
		if (self.AnimationStart-1 <= self.AnimationEnd) then
			self:SetValue (self.AnimationEnd)
			self.CurrentHealth = self.AnimationEnd
			self.IsAnimating = false
			if (self.Spark) then
				self.Spark:Hide()
			end
		end
	end

	function Plater.AnimateRightWithAccel (self, deltaTime)
		local distance = (self.AnimationEnd - self.AnimationStart) / self.CurrentHealthMax * 100	--scale 1 - 100 basis
		local minTravel = min (distance / 10, 3) -- 10 = trigger distance to max speed 3 = speed scale on max travel
		local maxTravel = max (minTravel, 0.45) -- 0.45 = min scale speed on low travel speed
		local calcAnimationSpeed = (self.CurrentHealthMax * (deltaTime * DB_ANIMATION_TIME_DILATATION)) * maxTravel --re-scale back to unit health, scale with delta time and scale with the travel speed
		
		self.AnimationStart = self.AnimationStart + (calcAnimationSpeed)
		self:SetValue (self.AnimationStart)
		self.CurrentHealth = self.AnimationStart
		
		if (self.AnimationStart+1 >= self.AnimationEnd) then
			self:SetValue (self.AnimationEnd)
			self.CurrentHealth = self.AnimationEnd
			self.IsAnimating = false
		end
	end	

	function Plater.CreateScaleAnimation (plateFrame) --private
		--animation table
		plateFrame.SpellAnimations = {}
		
		--scale animation
		local duration = 0.05
		local animationHub = DF:CreateAnimationHub (plateFrame.unitFrame)
		animationHub.ScaleUp = DF:CreateAnimation (animationHub, "scale", 1, duration,	1, 	1, 	1.2, 	1.2)
		animationHub.ScaleDown = DF:CreateAnimation (animationHub, "scale", 2, duration,	1, 	1, 	0.8, 	0.8)
		
		plateFrame.SpellAnimations ["scale"] = animationHub
	end

	function Plater.DoNameplateAnimation (plateFrame, frameAnimations, spellName, isCritical) --private
		Plater.StartLogPerformanceCore("Plater-Core", "Update", "DoNameplateAnimation")
		for animationIndex, animationTable in ipairs (frameAnimations) do
			if ((animationTable.animationCooldown [plateFrame] or 0) < GetTime()) then
				--animation "scale" is pre constructed when the nameplate frame is created
				if (animationTable.animation_type == "scale") then
					--get the animation
					local animationHub = plateFrame.SpellAnimations ["scale"]

					--duration
					animationHub.ScaleUp:SetDuration (animationTable.duration)
					animationHub.ScaleDown:SetDuration (animationTable.duration)
					
					local scaleUpX, scaleUpY = animationTable.scale_upX, animationTable.scale_upY
					local scaleDownX, scaleDownY = animationTable.scale_downX, animationTable.scale_downY;
					
					(animationHub.ScaleUp.SetFromScale or animationHub.ScaleUp.SetScaleFrom) (animationHub.ScaleUp, 1, 1);
					(animationHub.ScaleUp.SetToScale or animationHub.ScaleUp.SetScaleTo) (animationHub.ScaleUp, scaleUpX, scaleUpY);
					(animationHub.ScaleDown.SetFromScale or animationHub.ScaleDown.SetScaleFrom) (animationHub.ScaleDown, 1, 1);
					(animationHub.ScaleDown.SetToScale or animationHub.ScaleDown.SetScaleTo) (animationHub.ScaleDown, scaleDownX, scaleDownY)
					
					--play it
					animationHub:Play()
					animationTable.animationCooldown [plateFrame] = GetTime() + animationTable.cooldown
					
				elseif (animationTable.animation_type == "frameshake") then
					--get the animation
					local frameShake = plateFrame.SpellAnimations ["frameshake" .. spellName]
					local shakeTargetFrame = plateFrame.unitFrame
					
					if (not frameShake) then
			
						--[=[ 8.2 GetPoint isn't more possible in nameplate childs
						for i = 1, shakeTargetFrame:GetNumPoints() do --shakeTargetFrame = unitFrame from Plater
							local p1, p2, p3, p4, p5 = shakeTargetFrame:GetPoint (i)
							points [#points+1] = {p1, p2, p3, p4, p5}
						end
						--]=]
						
						local points = Plater.GetPoints (plateFrame.unitFrame)
						
						frameShake = DF:CreateFrameShake (shakeTargetFrame, animationTable.duration, animationTable.amplitude, animationTable.frequency, animationTable.absolute_sineX, animationTable.absolute_sineY, animationTable.scaleX, animationTable.scaleY, animationTable.fade_in, animationTable.fade_out, points)
						plateFrame.SpellAnimations ["frameshake" .. spellName] = frameShake
					end
					
					local animationScale = Plater.db.profile.spell_animations_scale
					
					if (IS_EDITING_SPELL_ANIMATIONS) then
						shakeTargetFrame:SetFrameShakeSettings (frameShake, animationTable.duration, animationTable.amplitude, animationTable.frequency, animationTable.absolute_sineX, animationTable.absolute_sineY, animationTable.scaleX, animationTable.scaleY, animationTable.fade_in, animationTable.fade_out)
					end
					
					if (isCritical and animationTable.critical_scale) then
						animationScale = animationScale * animationTable.critical_scale
						shakeTargetFrame:PlayFrameShake (frameShake, animationScale, animationScale, animationScale, DF:Clamp (0.75, 1.75, animationScale)) --, animationScale
					else
						--scaleDirection, scaleAmplitude, scaleFrequency, scaleDuration
						shakeTargetFrame:PlayFrameShake (frameShake, animationScale, animationScale, animationScale, DF:Clamp (0.75, 1.75, animationScale)) --, animationScale
					end
					
					animationTable.animationCooldown [plateFrame] = GetTime() + animationTable.cooldown
				end
			end
		end
		Plater.EndLogPerformanceCore("Plater-Core", "Update", "DoNameplateAnimation")
	end

	function Plater.RefreshIsEditingAnimations (state) --private
		IS_EDITING_SPELL_ANIMATIONS = state
	end

	--black list npcs to not show when showing friendly npcs
	local ignored_npcs_when_profession = {
		[32751] = true, --warp huntress pet - Dalaran
		[110571] = 1, --delas mooonfang - Dalaran
		[113199] = true, --delas's pet - Dalaran
		[110018] = 1, --gazrix gearlock - Dalaran
		[107622] = true, --glutonia - Dalaran
		[106263] = 1, --earthen ring shaman - Dalaran
		[106262] = 1, --earthen ring shaman - Dalaran
		[97141] = true, --koraud - Dalaran
	}

	function Plater.IsNpcInIgnoreList (plateFrame, onlyProfession) --private
		if (onlyProfession) then
			local npcId = plateFrame [MEMBER_NPCID]
			if (not npcId) then
				return
			end
			if (ignored_npcs_when_profession [npcId]) then
				return true
			end
		end
	end
	
	function Plater.UpdateMaxCastbarTextLength(newGlobalSize)
		Plater.MaxCastBarTextLength = newGlobalSize
	end

	function Plater.GetNpcIDFromGUID (guid) --private
		local npcID = select (6, strsplit ("-", guid))
		return tonumber (npcID or "0") or 0
	end

	function Plater.GetNpcID (plateFrame) --private
		local npcId = plateFrame [MEMBER_GUID]
		if (npcId and npcId ~= "") then
			npcId = select (6, strsplit ("-", npcId))
			if (npcId) then
				npcId = tonumber (npcId)
			else
				return
			end
		else
			return
		end
		
		if (not npcId) then
			return
		end
		
		plateFrame [MEMBER_NPCID] = npcId
		plateFrame.unitFrame [MEMBER_NPCID] = npcId
		
		return npcId
	end
	
	function Plater.GetUnitType (plateFrame)
		if (plateFrame ["namePlateClassification"] == "minus") then
			return "minus"
			
		elseif (PET_CACHE [plateFrame [MEMBER_GUID]]) then
			return "pet"
			
		end
		
		return "normal"
	end
	
	--returns isPet, isPlayerPet, PET_CACHE-entry (if existing)
	function Plater.IsUnitPet (unitFrame) 
		if not unitFrame then return false, false, nil end
		local entry = PET_CACHE [unitFrame.PlateFrame [MEMBER_GUID]]
		if (entry) then
			return true, Plater.PlayerPetCache [unitFrame.PlateFrame [MEMBER_GUID]] and true or false, entry
		end
		return false, false, nil
	end

	function Plater.CanChangePlateSize() --private
		return not InCombatLockdown()
	end
	
	function Plater.RefreshOmniCCGroup (fromInit) --private
		if (OmniCC) then
			local platerThemeName = "Plater Nameplates Theme"
			local platerRuleName = "Plater Nameplates Rule"
			
			--cleanup old data...
			OmniCC:RemoveRule("PlaterNameplates Blacklist")
			OmniCC:RemoveTheme("PlaterNameplates Blacklist")
			
			--attempt to get the plater theme
			local platerTheme = OmniCC:GetTheme(platerThemeName)
			
			--check if the plater theme exists, if it doesn't but the call is from Initialization or Profile Refresh, just quit
			if (not platerTheme and fromInit) then
				return
			end
			
			--if doesn't exists and isn't from init (the call came from the options panel by the user clicking in the checkbox)
			if (not platerTheme) then
				platerTheme = OmniCC:AddTheme(platerThemeName)
				platerTheme.enableText = false
			end
			
			local platerRules = OmniCC:AddRule(platerRuleName, platerThemeName)
			if not platerRules then
				-- rule already exists, get it properly...
				for _, rule in OmniCC:GetRulesets() do
					if rule.id == platerRuleName then
						platerRules = rule
						break
					end
				end
			end
			
			DF.table.addunique (platerRules.patterns, "PlaterMainAuraIcon")
			DF.table.addunique (platerRules.patterns, "PlaterSecondaryAuraIcon")
			DF.table.addunique (platerRules.patterns, "ExtraIconRowIcon")
			
			if (Plater.db.profile.disable_omnicc_on_auras) then
				platerTheme.enableText = false
				platerRules.enabled = true
			else
				platerTheme.enableText = true
			end
			
			OmniCC:OnProfileChanged()
			OmniCC.Cooldown:ForAll("Refresh", true)
		end
	end
	
	-- tanslate the npc cache entries if needed, do so. can translate names only, but not zones.
	function Plater.TranslateNPCCache()
		if not Plater.db.profile.auto_translate_npc_names then return end
		if Plater.TranslateNPCCacheIsRunning then return end
		Plater.TranslateNPCCacheIsRunning = true
		local maxPerFrame = 10
		local translateTimer = 0.1
		
		local function GetCreatureNameFromID(npcID)
			if C_TooltipInfo then
				local info = C_TooltipInfo.GetHyperlink(("unit:Creature-0-0-0-0-%d"):format(npcID))
				local leftText = info and info.lines and info.lines[1] and info.lines[1].leftText
				if leftText and leftText ~= _G.UNKNOWN then
					return leftText
				end
			else
				local tooltipFrame = GetCreatureNameFromIDFinderTooltip or CreateFrame ("GameTooltip", "GetCreatureNameFromIDFinderTooltip", nil, "GameTooltipTemplate")
				tooltipFrame:SetOwner (WorldFrame, "ANCHOR_NONE")
				tooltipFrame:SetHyperlink (("unit:Creature-0-0-0-0-%d"):format(npcID))
				local npcNameLine = _G ["GetCreatureNameFromIDFinderTooltipTextLeft1"]
				return npcNameLine and npcNameLine:GetText()
			end
		end
		
		local translate_npc_cache
		translate_npc_cache	= function()
			if not Plater.db.profile.auto_translate_npc_names then return end
			if PLAYER_IN_COMBAT then --or not IS_IN_OPEN_WORLD then
				C_Timer.After(5, translate_npc_cache)
				return
			end
			
			local count = 0
			local leftOvers = false
			for id, entry in pairs(DB_NPCIDS_CACHE) do
				
				if entry[3] ~= Plater.Locale then
					local npcName = GetCreatureNameFromID(id)
					if npcName then
						--DevTool:AddData(npcName, "translated")
						entry[1] = npcName
						entry[3] = Plater.Locale
						count = count + 1
					else
						--DevTool:AddData(id .. " - " .. entry[1], "not translated")
					end
				end
				
				if count >= maxPerFrame then
					leftOvers = true
					break
				end
			end
			
			if leftOvers and Plater.TranslateNPCCacheIsRunning then
				C_Timer.After(translateTimer, translate_npc_cache)
			else
				Plater.TranslateNPCCacheIsRunning = false
			end
		end
		translate_npc_cache()
	end

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> combat log reader  ~combatlog ~cleu


	local PlaterCLEUParser = CreateFrame ("frame", "PlaterCLEUParserFrame", UIParent, BackdropTemplateMixin and "BackdropTemplate")

	-- defined local above
	parserFunctions = {
		--todo: if animations are disabled, SPELL_DAMAGE doesn't need to be read
		SPELL_DAMAGE = function (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical)
			if (SPELL_WITH_ANIMATIONS [spellName] and sourceGUID == Plater.PlayerGUID) then
				for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
					if (plateFrame [MEMBER_GUID] == targetGUID and plateFrame.unitFrame.PlaterOnScreen) then
						Plater.DoNameplateAnimation (plateFrame, SPELL_WITH_ANIMATIONS [spellName], spellName, isCritical)
					end
				end
			end
		end,
		
		--~summon
		SPELL_SUMMON = function (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical)
		--[=[ --some actors are not having the pet flag 0x3000, so we are directly adding all target summons into the cache
			print ("Summon", targetFlag, bit.band (targetFlag, 0x00003000) ~= 0)
			
			if (sourceFlag and bit.band (sourceFlag, 0x00003000) ~= 0) then
				print ("new pet", sourceName, targetName)
				PET_CACHE [sourceGUID] = time
				
			elseif (targetFlag and bit.band (targetFlag, 0x00003000) ~= 0) then
				print ("new pet", sourceName, targetName)
				PET_CACHE [targetGUID] = time
			end
		--]=]

			---@type plater_petinfo
			local entry = {
				ownerGUID = sourceGUID,
				ownerName = sourceName,
				petName = targetName,
				time = time
			}
			PET_CACHE[targetGUID] = entry
			
			if (sourceGUID == Plater.PlayerGUID) then
				Plater.PlayerPetCache [targetGUID] = entry
			end

			--check if the summoner has friendly affiliation, if it is friendly, add it to the friendly affiliation cache
			if ((sourceFlag and bit.band(sourceFlag, 0x10) ~= 0) or (targetFlag and bit.band(targetFlag, 0x10) ~= 0)) then --0x10 = affiliation friendly
				platerInternal.HasFriendlyAffiliation[targetGUID] = true
			end
		end,
		
		SPELL_INTERRUPT = function (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical)
			if (IS_IN_INSTANCE) then
				PlaterDB.InterruptableSpells[spellID] = true
			end

			if (not Plater.db.profile.show_interrupt_author) then
				return
			end

			--~interrupt
			local name = sourceName
			for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
				local unitFrame = plateFrame.unitFrame
				local castBar = unitFrame.castBar
				if (unitFrame.PlaterOnScreen and castBar:IsShown()) then
					if (plateFrame [MEMBER_GUID] == targetGUID) then
						--get owner name if a pet interrupted
						local petOwnerTable = PET_CACHE[sourceGUID]
						if (petOwnerTable) then
							name = petOwnerTable.ownerName or name
							sourceGUID = petOwnerTable.ownerGUID or sourceGUID
						end
						if DB_USE_NAME_TRANSLIT then
							name = LibTranslit:Transliterate(name, TRANSLIT_MARK)
						end
						castBar.Text:SetText (INTERRUPTED .. " [" .. Plater.SetTextColorByClass (sourceGUID, name) .. "]")
						castBar.IsInterrupted = true
						castBar.InterruptSourceName = sourceName
						castBar.InterruptSourceGUID = sourceGUID
						
						--interrupt animation
						if (Plater.db.profile.cast_statusbar_interrupt_anim) then
							local interruptAnim = castBar._interruptAnim
							if (not interruptAnim) then
								--scale animation
								local duration = 0.05
								local animationHub = DF:CreateAnimationHub(castBar)
								animationHub.ScaleUp = DF:CreateAnimation(animationHub, "scale", 1, duration,	1, 	1, 	1, 	1.05)
								animationHub.ScaleDown = DF:CreateAnimation(animationHub, "scale", 2, duration,	1, 	1.05, 	1, 	0.95)

								--shake animattion
								local duration = 0.36
								local amplitude = 0.58
								local frequency = 3.39
								local absolute_sineX = false
								local absolute_sineY = false
								local scaleX = 0
								local scaleY = 1.2
								local fade_out = 0.33
								local fade_in = 0.001
								local cooldown = 0.5

								local points = castBar._points
								local frameShakeObject = DF:CreateFrameShake(castBar, duration, amplitude, frequency, absolute_sineX, absolute_sineY, scaleX, scaleY, fade_in, fade_out, points)

								castBar._interruptAnim = {
									Scale = animationHub,
									Shake = frameShakeObject
								}

								interruptAnim = castBar._interruptAnim
							end

							interruptAnim.Scale:Play()
							castBar:PlayFrameShake(interruptAnim.Shake)
						end

						castBar.FrameOverlay.TargetName:Hide() -- hide the target immediately
						--> check and stop the casting script if any
						castBar:OnHideWidget()
					end
				end
			end
		end,
		
		SPELL_CAST_SUCCESS = function (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical)
			if ((tonumber(spellID) or 0) > 0 and (not DB_CAPTURED_SPELLS[spellID] or DB_CAPTURED_SPELLS[spellID].isChanneled == nil)) then -- check isChanneled to ensure update of already existing data
				if (not platerInternal.HasFriendlyAffiliation[sourceGUID]) then
					if (not sourceFlag or bit.band(sourceFlag, 0x60) ~= 0) then --is neutral or hostile
						local npcId = Plater:GetNpcIdFromGuid(sourceGUID or "")
						local isChanneled = false
						if sourceGUID and UnitTokenFromGUID then -- this is the only proper way to check for channeled spells...
							local unit = UnitTokenFromGUID(sourceGUID)
							if unit and UnitChannelInfo(unit) then
								isChanneled = true
							end 
						end

						if (npcId and npcId ~= 0) then
							---@type plater_spelldata
							local spellData = {
								event = token,
								source = sourceName,
								npcID = npcId,
								encounterID = Plater.CurrentEncounterID,
								encounterName = Plater.CurrentEncounterName,
								isChanneled = isChanneled
							}
							--print("added DB_CAPTURED_SPELLS 1:", sourceName, spellID, spellName)
							DB_CAPTURED_SPELLS[spellID] = spellData

							if isChanneled and not DB_CAPTURED_CASTS[spellID] then
								---@type plater_spelldata
								local spellData = {
									event = token,
									source = sourceName,
									npcID = npcId,
									encounterID = Plater.CurrentEncounterID,
									encounterName = Plater.CurrentEncounterName,
									isChanneled = isChanneled
								}
								DB_CAPTURED_CASTS[spellID] = spellData
							end
						end
					end
				end
			end
		end,
		
		SPELL_CAST_START = function (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical)
			if (not DB_CAPTURED_CASTS[spellID] and not platerInternal.HasFriendlyAffiliation[sourceGUID]) then
				if (not sourceFlag or bit.band(sourceFlag, 0x60) ~= 0) then --is neutral or hostile
					local npcId = Plater:GetNpcIdFromGuid(sourceGUID or "")
					if (npcId and npcId ~= 0) then
						---@type plater_spelldata
						local spellData = {
							event = token,
							source = sourceName,
							npcID = npcId,
							encounterID = Plater.CurrentEncounterID,
							encounterName = Plater.CurrentEncounterName
						}
						DB_CAPTURED_CASTS[spellID] = spellData
					end
				end
			end

			if (spellName) then
				Plater.LastCombat.spellNames[spellName] = true
			end

			platerInternal.Audio.PlaySoundForCastStart(spellID)
		end,

		SPELL_AURA_APPLIED = function (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, auraType, overKill, school, resisted, blocked, absorbed, isCritical)
			if (not DB_CAPTURED_SPELLS[spellID] and not platerInternal.HasFriendlyAffiliation[sourceGUID]) then
				if (not sourceFlag or bit.band(sourceFlag, 0x60) ~= 0) then --is neutral or hostile
					local npcId = Plater:GetNpcIdFromGuid(sourceGUID or "")
					if (npcId and npcId ~= 0) then
						---@type plater_spelldata
						local spellData = {
							event = token,
							source = sourceName,
							type = auraType,
							npcID = npcId,
							encounterID = Plater.CurrentEncounterID,
							encounterName = Plater.CurrentEncounterName
						}
						--print("added DB_CAPTURED_SPELLS 2:", sourceName, spellID, spellName, sourceFlag)
						DB_CAPTURED_SPELLS[spellID] = spellData
					end
				end
			end
			
			if IS_WOW_PROJECT_NOT_MAINLINE then
				-- paladin tank buff tracking
				local playerGUID = Plater.PlayerGUID
				if sourceGUID == playerGUID and targetGUID == playerGUID then
					local spellId = select(7, GetSpellInfo(spellName))
					if spellId == 25780 or spellId == 407627 then
						UpdatePlayerTankState(true)
						--Plater.RefreshTankCache()
					end
				end
			end
		end,

		UNIT_DIED = function(time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID)
			--targetGUID is the GUID of the unit which died
			platerInternal.ExtraAuras.RemoveGUIDFromUnitFrameCache(targetGUID)
		end,
	}
	
	if IS_WOW_PROJECT_NOT_MAINLINE then
		tinsert(parserFunctions, {
			SPELL_AURA_REMOVED = function (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical)
				-- paladin tank buff tracking
				local playerGUID = Plater.PlayerGUID
				if sourceGUID == playerGUID and targetGUID == playerGUID then
					local spellId = select(7, GetSpellInfo(spellName))
					if spellId == 25780 or spellId == 407627 then
						UpdatePlayerTankState(false)
						--Plater.RefreshTankCache()
					end
				end
			end,
		})
	end

	PlaterCLEUParser.Parser = function (self)
		local time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical = CombatLogGetCurrentEventInfo()
		local func = parserFunctions [token]
		if (func) then
			Plater.StartLogPerformanceCore("Plater-Core", "Events", token)
			func (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical)
			if (targetName) then
				Plater.LastCombat.npcNames[targetName] = true
			end
			Plater.EndLogPerformanceCore("Plater-Core", "Events", token)
		end
	end

	PlaterCLEUParser:SetScript ("OnEvent", PlaterCLEUParser.Parser)
	PlaterCLEUParser:RegisterEvent ("COMBAT_LOG_EVENT_UNFILTERED")

	C_Timer.NewTicker (600, function()
		local now = time()
		for guid, entry in pairs (PET_CACHE) do
			local time = entry.time
			if (time+600 < now) then
				PET_CACHE [guid] = nil
			end
		end
		
		for guid, entry in pairs (Plater.PlayerPetCache) do
			local time = entry.time
			if (time + 3600 < now) then
				Plater.PlayerPetCache [guid] = nil
			end
		end
	end)

	Plater.NpcBlackList = {} 
	function Plater.ForceFindPetOwner (serial) --private
		Plater.StartLogPerformanceCore("Plater-Core", "Update", "ForceFindPetOwner")
		
		local petName,text1
		local cbMode = tonumber(GetCVar("colorblindMode")) or 0
		if IS_WOW_PROJECT_MAINLINE then
			local tooltipData = C_TooltipInfo.GetHyperlink ("unit:" .. serial or "")
			if tooltipData then
				local lines = tooltipData.lines
				
				petName = lines and lines[1] and lines[1].leftText
				text1 = lines and lines[2 + cbMode] and lines[2 + cbMode].leftText
			end
			
		else
			local tooltipFrame = PlaterPetOwnerFinder or CreateFrame ("GameTooltip", "PlaterPetOwnerFinder", nil, "GameTooltipTemplate")
			tooltipFrame:SetOwner (WorldFrame, "ANCHOR_NONE")
			tooltipFrame:SetHyperlink ("unit:" .. serial or "")
			
			local petNameLine = _G ["PlaterPetOwnerFinderTextLeft1"]
			petName = petNameLine and petNameLine:GetText()
			local line1 = _G ["PlaterPetOwnerFinderTextLeft" .. (2 + cbMode)]
			text1 = line1 and line1:GetText()
		end
		
		local isPlayerPet = false
		local isOtherPet = false
		local ownerName = ""
		
		
		if (text1 and text1 ~= "") then
			local pName = GetUnitName ("player", true)
			local playerName = pName:gsub ("%-.*", "") --remove realm name
			if (text1:find (playerName)) then
				isPlayerPet = true
				ownerName = playerName
			else
				ownerName = (string.match(text1, string.gsub(UNITNAME_TITLE_PET, "%%s", "(%.*)")) or string.match(text1, string.gsub(UNITNAME_TITLE_MINION, "%%s", "(%.*)")) or string.match(text1, string.gsub(UNITNAME_TITLE_GUARDIAN, "%%s", "(%.*)")))
				if ownerName then
					isOtherPet = true
				end
			end
		end
		
		if (isPlayerPet or isOtherPet) and petName then
			local entry = {ownerGUID = UnitGUID(ownerName), ownerName = ownerName, petName = petName, time = time()}
			
			if (isPlayerPet) then
				PET_CACHE [serial] = entry
				Plater.PlayerPetCache [serial] = entry
			elseif (isOtherPet) then
				--ViragDevTool_AddData({serial = serial, entry = entry, tooltipFrame = tooltipFrame}, "pet")
				PET_CACHE [serial] = entry
			end
		else
			Plater.NpcBlackList [serial] = true
		end
		
		Plater.EndLogPerformanceCore("Plater-Core", "Update", "ForceFindPetOwner")
	end
	
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> cvars - ~cvars
	
function Plater.CreatePlaterButtonAtInterfaceOptions()
	if not InterfaceOptionsFrame then return end --DF does not has this anymore TODO
	local f = CreateFrame ("frame", nil, InterfaceOptionsNamesPanel, BackdropTemplateMixin and "BackdropTemplate")
	f:SetSize (300, 200)
	f:SetPoint ("topleft", InterfaceOptionsNamesPanel, "topleft", 10, -440)
	
	local open_options = function()
		InterfaceOptionsFrame:Hide()
		HideUIPanel(GameMenuFrame)
		Plater.OpenOptionsPanel()
	end
	
	local Button = DF:CreateButton (f, open_options, 100, 22, "", -1, nil, nil, nil, nil, nil, DF:GetTemplate ("button", "OPTIONS_BUTTON_TEMPLATE"))
	Button:SetPoint ("topleft", f, "topleft", 10, 0)
	Button:SetText ("Advanced Nameplate Options")
	Button:SetIcon ([[Interface\BUTTONS\UI-OptionsButton]], 18, 18, "overlay", {0, 1, 0, 1})
end

--elseof
function Plater.SetCVarsOnFirstRun()

	if (InCombatLockdown()) then
		C_Timer.After (1, function() Plater.SetCVarsOnFirstRun() end)
		return
	end
	
	canSaveCVars = false -- ensure to not overwrite profile

	--> these are the cvars set for each character when they logon
	
	--disabled:
		--SetCVar ("nameplateShowSelf", CVAR_DISABLED)
		--SetCVar ("nameplateShowFriends", CVAR_ENABLED)
	--> location of the personal bar
	--	SetCVar ("nameplateSelfBottomInset", 20 / 100)
	--	SetCVar ("nameplateSelfTopInset", abs (20 - 99) / 100)
	
	--> set the stacking to true
	--SetCVar ("nameplateMotion", CVAR_ENABLED) --March 08, 2019 don't change the stacking type when installing plater
	--> distance between each nameplate when using stacking
	--SetCVar ("nameplateOverlapV", 1.25)
	
	--> make nameplates always shown and down't show minions
	SetCVar ("nameplateShowAll", CVAR_ENABLED)
	if IS_WOW_PROJECT_MAINLINE then
		SetCVar ("ShowNamePlateLoseAggroFlash", CVAR_ENABLED) --blizzard flash
	end
	
	--scale when it is too far away from the camera
	SetCVar ("nameplateMinScale", 1)
	--scale of the nameplate for important units, default is 1.2 which makes the nameplate be too big with the 1.15 target scale
	SetCVar ("nameplateLargerScale", 1.10)
	
	--enable enemy minus nameplates
	SetCVar ("nameplateShowEnemyMinions", CVAR_ENABLED)
	SetCVar ("nameplateShowEnemyMinus", CVAR_ENABLED)
	
	--don't show friendly npcs
	SetCVar ("nameplateShowFriendlyNPCs", 0)
	--disable friendly minius nameplates
	SetCVar ("nameplateShowFriendlyGuardians", CVAR_DISABLED)
	SetCVar ("nameplateShowFriendlyPets", CVAR_DISABLED)
	SetCVar ("nameplateShowFriendlyTotems", CVAR_DISABLED)
	SetCVar ("nameplateShowFriendlyMinions", CVAR_DISABLED)
	
	--> make it show the class color of players
	SetCVar ("ShowClassColorInNameplate", CVAR_ENABLED)
	
	--> lock nameplates to screen
	SetCVar ("nameplateOtherTopInset", "0.085")
	SetCVar ("nameplateLargeTopInset", "0.085")
	SetCVar ("clampTargetNameplateToScreen", "1")
	SetCVar ("nameplateTargetRadialPosition", "1")
	SetCVar ("nameplateTargetBehindMaxDistance", "30")

	--> reset the horizontal and vertical scale
	SetCVar ("NamePlateHorizontalScale", CVAR_ENABLED)
	SetCVar ("NamePlateVerticalScale", CVAR_ENABLED)
	if IS_WOW_PROJECT_MAINLINE then
		SetCVar ("NamePlateClassificationScale", CVAR_ENABLED)
	end
	
	--> make the selection be a little bigger
	SetCVar ("nameplateSelectedScale", "1.15")

	--> movement speed of nameplates when using stacking, going above 0.5 this isn't recommended
	SetCVar ("nameplateMotionSpeed", "0.025")

	--> make the personal bar hide very fast
	SetCVar ("nameplatePersonalHideDelaySeconds", 0.2)
	
	--> don't show debuffs on blizzard healthbars
	SetCVar ("nameplateShowDebuffsOnFriendly", CVAR_DISABLED)

	--> view distance
	if IS_WOW_PROJECT_MAINLINE then
		SetCVar ("nameplateMaxDistance", 60)
	else
		SetCVar ("nameplateMaxDistance", 41)
	end
	
	--> ensure resource on target consistency:
	if IS_WOW_PROJECT_MAINLINE then
		PlaterDBChr.resources_on_target = GetCVar ("nameplateResourceOnTarget") == CVAR_ENABLED
		SetCVar ("nameplateResourceOnTarget", CVAR_DISABLED)
	end
	
	PlaterDBChr.first_run3 [UnitGUID ("player")] = true
	Plater.db.profile.first_run3 = true
	
	Plater.RunFunctionForEvent ("ZONE_CHANGED_NEW_AREA")
	
	--Plater:Msg ("Plater has been successfully installed on this character.")
	
	Plater.RestoreProfileCVars() -- restore profile, if existing
	
	canSaveCVars = true -- save cvars again

end

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> quest log stuff ~quest
	
	function Plater.GetActorSubName (plateFrame) --private
		local cbMode = tonumber(GetCVar("colorblindMode")) or 0
		local subTitle = ""
		if IS_WOW_PROJECT_MAINLINE then
			local tooltipData = C_TooltipInfo.GetHyperlink("unit:" .. (plateFrame [MEMBER_GUID] or ""))
			if tooltipData then
				local line = tooltipData.lines and tooltipData.lines[2 + cbMode]
				subTitle = line and line.leftText or ""
			end
		else
			local GameTooltipFrame = PlaterScanTooltip or CreateFrame ("GameTooltip", "PlaterScanTooltip", nil, "GameTooltipTemplate")
			GameTooltipFrame:SetOwner (WorldFrame, "ANCHOR_NONE")
			GameTooltipFrame:SetHyperlink ("unit:" .. (plateFrame [MEMBER_GUID] or ''))
			
			local GameTooltipFrameTextLeft = _G ["PlaterScanTooltipTextLeft" .. (2 + cbMode)]
			subTitle = GameTooltipFrameTextLeft and GameTooltipFrameTextLeft:GetText() or ""
		end
		if subTitle ~= "" and not subTitle:lower():match (string.gsub(UNIT_LEVEL_TEMPLATE:lower(), "%%d", "(%.*)")) then
			return subTitle
		else
			return nil
		end
	end

	function Plater.IsQuestObjective (plateFrame)
		if (not plateFrame [MEMBER_GUID]) then --platerFrame.actorType == "friendlynpc"
			return
		end
		
		Plater.StartLogPerformanceCore("Plater-Core", "Update", "IsQuestObjective")
		
		-- reset quest amount
		plateFrame.QuestAmountCurrent = nil
		plateFrame.QuestAmountTotal = nil
		plateFrame.QuestText = nil
		plateFrame.QuestName = nil
		plateFrame.QuestIsCampaign = nil
		plateFrame.unitFrame.QuestAmountCurrent = nil
		plateFrame.unitFrame.QuestAmountTotal = nil
		plateFrame.unitFrame.QuestText = nil
		plateFrame.unitFrame.QuestName = nil
		plateFrame.unitFrame.QuestIsCampaign = nil
		
		local ScanQuestTextCache = {}
		local useQuestie = false
		local QuestieTooltips = QuestieLoader and QuestieLoader._modules["QuestieTooltips"]
		if QuestieTooltips then
			ScanQuestTextCache = QuestieTooltips.GetTooltip("m_"..plateFrame [MEMBER_NPCID])
			if not ScanQuestTextCache then
				ScanQuestTextCache = {}
			end
			useQuestie = true
		else
			if IS_WOW_PROJECT_MAINLINE then
				local tooltipData = C_TooltipInfo.GetHyperlink ("unit:" .. plateFrame [MEMBER_GUID])
				if tooltipData then
					for _, line in ipairs(tooltipData.lines or {}) do
						if line.type == Enum.TooltipDataLineType.QuestObjective or line.type == Enum.TooltipDataLineType.QuestTitle or line.type == Enum.TooltipDataLineType.QuestPlayer then
							--only add actual quest tooltip lines
							ScanQuestTextCache [#ScanQuestTextCache + 1] = line.leftText or ""
						end
					end
				end
			else
				local GameTooltipScanQuest = PlaterScanQuestTooltip or CreateFrame ("GameTooltip", "PlaterScanQuestTooltip", nil, "GameTooltipTemplate")
				GameTooltipScanQuest:SetOwner (WorldFrame, "ANCHOR_NONE")
				GameTooltipScanQuest:SetHyperlink ("unit:" .. plateFrame [MEMBER_GUID])
				
				for i = 1, GameTooltipScanQuest:NumLines() do
					ScanQuestTextCache [i] = _G ["PlaterScanQuestTooltipTextLeft" .. i]:GetText() or ""
				end
			end
			
		end
		
		local playerName = UnitName("player")
		local isInGroup = IsInGroup()
		local unitQuestData = {}
		
		local isQuestUnit = false
		local atLeastOneQuestUnfinished = false
		for i = 1, #ScanQuestTextCache do
			local text = ScanQuestTextCache [i]
			if useQuestie then
				text = gsub(text,"|c........","") -- remove coloring begin
				text = gsub(text,"|r","") -- remove color end
				text = gsub(text,"%[.*%] ","") -- remove level text
				text = gsub(text," %(%d+%)","") -- remove quest-id
			end
			
			if (Plater.QuestCache [text]) then
				--unit belongs to a quest
				isQuestUnit = true
				
				local isCampaignQuest = Plater.QuestCacheCampaign[text]
				local isGroupQuest, yourQuest = nil, nil

				---@type questdata
				local questData = {
					questName = text,
					questText = "",
					finished = true,
					groupQuest = false,
					groupFinished = true,
					amount = 0,
					groupAmount = 0,
					total = 0,
					yourQuest = false,
					isCampaignQuest = isCampaignQuest,
				}

				local amount1, amount2, questText = nil, nil, nil
				local amountSet = false
				local j = i
				while (ScanQuestTextCache [j+1]) do
					--check if the unit objective isn't already done
					local nextLineText = ScanQuestTextCache [j+1]
					if useQuestie then
						nextLineText = gsub(nextLineText,"|c........","") -- remove coloring begin
						nextLineText = gsub(nextLineText,"|r","") -- remove color end
					end
					
					if (nextLineText) then
						if useQuestie then
							local isQuestieOwn = nextLineText:match ("%(("..playerName..")%)%s*$") and true or false
							local isQuestieGroup = nextLineText:match ("%((%w+)%)%s*$") and isInGroup and true or false
							yourQuest = isQuestieOwn or not isQuestieGroup
							isGroupQuest = isQuestieGroup
							questData.yourQuest = yourQuest
							questData.groupQuest = isGroupQuest
						end
						if not useQuestie and isInGroup and nextLineText == playerName then
							yourQuest = true
							isGroupQuest = true
							questData.yourQuest = true
							questData.groupQuest = true
						elseif not nextLineText:match(THREAT_TOOLTIP) then
							local p1, p2 = nextLineText:match ("(%d+)/(%d+)")
							if (not p1) then
								-- check for % based quests
								p1 = nextLineText:match ("(%d+%%)")
								if p1 then
									-- remove the % sign for consistency
									p1 = string.gsub(p1,"%%", '')
								end
							end
							
							if (p1 and p2 and not (p1 == p2)) or (p1 and not p2 and not (p1 == "100")) then
								-- quest not completed
								atLeastOneQuestUnfinished = true
								amount1, amount2 = p1, p2
								if not amountSet or ((tonumber(p1) or 0) < (tonumber(questData.groupAmount) or 0)) then
									questData.groupAmount = amount1
								end
								
								questData.total = amount2
								if yourQuest ~= false then
									yourQuest = false -- already set on data
									questData.finished = false
									questData.amount = amount1
								end
								questData.groupFinished = false
								questData.questText = nextLineText
								questText = nextLineText
								amountSet = true
							elseif yourQuest and (p1 and p2 and (p1 == p2)) or (p1 and not p2 and (p1 == "100")) then
								yourQuest = false -- already set on data
								questData.finished = true
								questData.amount = p1
							end
						else
							j = 99 --safely break here, as we saw threat% -> quest text is done
						end
					end
					j = j + 1
				end

				if (amount1 and atLeastOneQuestUnfinished) then
					plateFrame.QuestAmountCurrent = questData.groupAmount
					plateFrame.QuestAmountTotal = amount2
					plateFrame.QuestText = questText
					plateFrame.QuestName = text
					plateFrame.QuestIsCampaign = isCampaignQuest
					
					--expose to scripts
					plateFrame.unitFrame.QuestAmountCurrent = questData.groupAmount
					plateFrame.unitFrame.QuestAmountTotal = amount2
					plateFrame.unitFrame.QuestText = questText
					plateFrame.unitFrame.QuestName = text
					plateFrame.unitFrame.QuestIsCampaign = isCampaignQuest
				end
				
				if not isGroupQuest then
					questData.yourQuest = true
				end
				
				tinsert(unitQuestData, questData)
			end
		end
		
		plateFrame.QuestInfo = unitQuestData
		plateFrame.unitFrame.QuestInfo = unitQuestData
		
		local namePlateIsQuestObjective = isQuestUnit and atLeastOneQuestUnfinished
		plateFrame [MEMBER_QUEST] = namePlateIsQuestObjective
		plateFrame.unitFrame [MEMBER_QUEST] = namePlateIsQuestObjective
		
		Plater.EndLogPerformanceCore("Plater-Core", "Update", "IsQuestObjective")
		
		return namePlateIsQuestObjective
	end

	local update_quest_cache = function()

		--clear the quest cache
		wipe (Plater.QuestCache)
		wipe (Plater.QuestCacheCampaign)

		--do not update if is inside an instance
		local isInInstance = IsInInstance()
		if (isInInstance) then
			return
		end
		
		--update the quest cache
		local numEntries, numQuests = C_QuestLog.GetNumQuestLogEntries and C_QuestLog.GetNumQuestLogEntries() or GetNumQuestLogEntries()
		for questLogId = 1, numEntries do
			if IS_WOW_PROJECT_MAINLINE then
				local questDetails = C_QuestLog.GetInfo(questLogId)
				--any chance to track via quest objective? no unit IDs given there...
				--ViragDevTool_AddData({questDetails = questDetails, QuestObjectives = C_QuestLog.GetQuestObjectives(questDetails.questID), Title = C_QuestLog.GetTitleForLogIndex(questLogId)}, "QuestUpdate - " .. questLogId)
				if (questDetails and not questDetails.isHeader and questDetails.title and type (questDetails.questID) == "number" and questDetails.questID > 0) then
					Plater.QuestCache [questDetails.title] = true
					if (questDetails.campaignID) then
						Plater.QuestCacheCampaign[questDetails.title] = true
					end
				end
			else
				local title, level, suggestedGroup, isHeader, isCollapsed, isComplete, frequency, questId, startEvent, displayQuestID, isOnMap, hasLocalPOI, isTask, isStory = GetQuestLogTitle (questLogId)
				if (type (questId) == "number" and questId > 0) then -- and not isComplete
					Plater.QuestCache [title] = true
				end
			end
		end
		
		if IS_WOW_PROJECT_MAINLINE then
			local mapId = C_Map.GetBestMapForUnit ("player")
			if (mapId) then
				local worldQuests = C_TaskQuest.GetQuestsForPlayerByMapID (mapId)
				if (type (worldQuests) == "table") then
					for i, questTable in ipairs (worldQuests) do
						local x, y, floor, numObjectives, questId, inProgress = questTable.x, questTable.y, questTable.floor, questTable.numObjectives, questTable.questId, questTable.inProgress
						if (type (questId) == "number" and questId > 0) then
							local questName = C_TaskQuest.GetQuestInfoByQuestID (questId)
							if (questName) then
								Plater.QuestCache [questName] = true
							end
						end
					end
				end
			end
		end
		
		Plater.UpdateAllPlates()
	end

	function Plater.QuestLogUpdated() --private
		if (Plater.UpdateQuestCacheThrottle and not Plater.UpdateQuestCacheThrottle._cancelled) then
			Plater.UpdateQuestCacheThrottle:Cancel()
		end
		Plater.UpdateQuestCacheThrottle = C_Timer.NewTimer(1, update_quest_cache)
	end



--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> API ~API

	--attempt to get the role of the unit shown in the nameplate
	function Plater.GetUnitRole (unitFrame)
		if IS_WOW_PROJECT_MAINLINE then
			local assignedRole = UnitGroupRolesAssigned (unitFrame.unit)
			if (assignedRole and assignedRole ~= "NONE") then
				return assignedRole
			end
		
			if (Plater.ZoneInstanceType == "arena") then
				local oponentes = GetNumArenaOpponentSpecs()
				for i = 1, oponentes do
					local unitGUID = UnitGUID ("arena" .. i)
					if (unitGUID == unitFrame [MEMBER_GUID]) then
						local spec = GetArenaOpponentSpec (i)
						if (spec) then
							local id, name, description, icon, role, class = GetSpecializationInfoByID (spec)
							if (role and role ~= "NONE") then
								return role
							end
						end
					end
				end
				
			elseif (Plater.ZoneInstanceType == "pvp") then
				if (Details) then
					local actor = Details:GetActor ("current", DETAILS_ATTRIBUTE_DAMAGE, GetUnitName (unitFrame.unit, true))
					if (actor) then
						local spec = actor.spec
						if (spec) then
							local id, name, description, icon, role, class = GetSpecializationInfoByID (spec)
							if (role and role ~= "NONE") then
								return role
							end
						end
					end
				end
			end
			
			return assignedRole
			
		else
			if IS_WOW_PROJECT_CLASSIC_WRATH then
				local assignedRole = UnitGroupRolesAssigned (unitFrame.unit)
				if (assignedRole and assignedRole ~= "NONE") then
					return assignedRole
				end
			end
			if GetPartyAssignment("MAINTANK", unitFrame.unit) then
				return "MAINTANK"
			elseif GetPartyAssignment("MAINASSIST", unitFrame.unit) then
				return "MAINASSIST"
			end
		end
	end
	
	
	local BG_PLAYER_CACHE = {}
	function Plater.UpdateBgPlayerRoleCache()
		wipe(BG_PLAYER_CACHE)
	
		if IS_WOW_PROJECT_MAINLINE then
			if Plater.ZoneInstanceType == "pvp" then
				local curNumScores = GetNumBattlefieldScores()
				for i = 1, curNumScores do
					local info = C_PvP.GetScoreInfo(i)
					if info then
						local name, faction, race, class, classToken, talentSpec = info.name, info.faction, info.raceName, info.className, info.classToken, info.talentSpec
						if name then
							BG_PLAYER_CACHE[name] = {faction = faction, race = race, class = class, classToken = classToken, talentSpec = talentSpec, specID = (CLASS_INFO_CACHE[classToken] and CLASS_INFO_CACHE[classToken][talentSpec] and CLASS_INFO_CACHE[classToken][talentSpec].specID), name = name}
						end
					end
				end
			elseif Plater.ZoneInstanceType == "arena" then
				local numOpps = GetNumArenaOpponentSpecs();
				for i=1, numOpps do
					local specID, gender = GetArenaOpponentSpec(i);
					if (specID > 0) then
						local name = GetUnitName ("arena"..i, true)
						if name then
							local id, talentSpec, _, _, _, class = GetSpecializationInfoByID(specID, gender);
							local class, classToken = UnitClass("arena"..i);
							local race = UnitRace("arena"..i);
							BG_PLAYER_CACHE[name] = {faction = nil, race = race, class = class, classToken = classToken, talentSpec = talentSpec, specID = specID, name = name}
						end
					end
				end
			end
			
		else
			--TODO: Does this really work in BG/Arena or is it just score screen?
			if Plater.ZoneInstanceType == "pvp" or Plater.ZoneInstanceType == "arena" then
				local curNumScores = GetNumBattlefieldScores()
				for i = 1, curNumScores do
					local name, _, _, _, _, faction, _, race, class, classToken = GetBattlefieldScore(i);
					if name then
						BG_PLAYER_CACHE[name] = {faction = faction, race = race, class = class, classToken = classToken, talentSpec = "UNKNOWN", specID = nil, name = name}
					end
				end
			end
		end
	end

	function Plater.GetUnitBGInfo(unit)

		if (not UnitIsPlayer(unit)) then
			return nil
		end
		
		if (not Plater.ZoneInstanceType == "pvp" and not Plater.ZoneInstanceType == "arena") then
			return nil
		end

		local name = GetUnitName(unit, true)
		if not BG_PLAYER_CACHE[name] then
			Plater.UpdateBgPlayerRoleCache()
		end

		return BG_PLAYER_CACHE[name]
	end
	
	function Plater.GetSpecIconForUnitFromBG(unit)
		
		local cache = Plater.GetUnitBGInfo(unit)
		if cache and cache.specID then
			return Plater.GetSpecIcon(cache.specID)
		end
		return nil
	end
	
	function Plater.GetSpecIcon(spec)
		if (spec) then
			if (not class_specs_coords[spec]) then -- default to holy paladin if spec not supported
				spec = 65
			end
			if (useAlpha) then
				return [[Interface\AddOns\Plater\images\spec_icons_normal_alpha]], unpack (class_specs_coords [spec])
			else
				return [[Interface\AddOns\Plater\images\spec_icons_normal]], unpack (class_specs_coords [spec])
			end
		end
	end
	
	--similar to Plater.GetSettings, but can be called from scripts
	--is is also safe because it passes a read-only table with copied values
	function Plater.GetConfig (unitFrame)
		return Plater.ActorTypeSettingsCache [unitFrame.ActorType]
	end
	
	--return true if the player is in open world (not inside dungeons, etc)
	function Plater.IsInOpenWorld()
		return IS_IN_OPEN_WORLD
	end
	
	--refresh the frame strata and frame level when using UIParent as the parent
	function Plater.RefreshNameplateStrata (unitFrame)
		return Plater.UpdateUIParentLevels (unitFrame)
	end
	
	--add an extra indicator
	function Plater.ShowIndicator (unitFrame, texture, width, height, color, L, R, T, B)
		tinsert (unitFrame.CustomIndicators, {texture or "", width or 12, height or 12, color or "white", L or 0, R or 1, T or 0, B or 1})
		Plater.UpdateIndicators (unitFrame.PlateFrame, unitFrame.ActorType)
	end
	
	--allow scripts to perform safe cvars changes with backup of default values
	--PostponeCVarChange stores cvars scheduled to be changed after the combat lockdown drops
	Plater.PostponeSetCVar = {}
	local postpone_set_cvar = function (timerObject)
		local variableName, value = timerObject.variableName, timerObject.value
		Plater.PostponeSetCVar [variableName] = nil
		Plater.SafeSetCVar (variableName, value)
	end
	
	function Plater.SafeSetCVar (variableName, value)
		--check if is a valid cvar
		if (GetCVar (variableName) == nil) then
			Plater:Msg ("invalid cvar for Plater.SafeSetCVar()")
			return
		end
		
		--check if there's a scheduled change for this cvar and cancel it
		if (Plater.PostponeSetCVar [variableName]) then
			Plater.PostponeSetCVar [variableName]:Cancel()
			Plater.PostponeSetCVar [variableName] = nil
		end
		
		--check if is in combat, if is, schedule to change this cvar after the lockdown drop
		if (InCombatLockdown()) then
			local timerObject = C_Timer.NewTimer (0.5, postpone_set_cvar)
			timerObject.variableName = variableName
			timerObject.value = value
			Plater.PostponeSetCVar [variableName] = timerObject
			return true
		end
		
		--store the default value if there's no default value set yet
		local cvarCache = Plater.db.profile.cvar_default_cache
		if (cvarCache [variableName] == nil) then
			cvarCache [variableName] = GetCVar (variableName)
		end
		
		SetCVar (variableName, value)
		return true
	end
	
	Plater.PostponeRestoreCVar = {}
	function Plater.PostponeCVarRestauration (timerObject)
		local variableName = timerObject.variableName
		Plater.PostponeRestoreCVar [variableName] = nil
		Plater.RestoreCVar (variableName)
	end
	
	function Plater.RestoreCVar (variableName)
		--check if is a valid cvar
		if (GetCVar (variableName) == nil) then
			Plater:Msg ("invalid cvar for Plater.SafeSetCVar()")
			return
		end
		
		--check if there's a scheduled change for this cvar and cancel it
		if (Plater.PostponeRestoreCVar [variableName]) then
			Plater.PostponeRestoreCVar [variableName]:Cancel()
			Plater.PostponeRestoreCVar [variableName] = nil
		end
		
		--check if is in combat, if is, schedule to change this cvar after the lockdown drop
		if (InCombatLockdown()) then
			local timerObject = C_Timer.NewTimer (0.5, Plater.PostponeCVarRestauration)
			timerObject.variableName = variableName
			Plater.PostponeRestoreCVar [variableName] = timerObject
			return true
		end

		--restore the value
		local cvarCache = Plater.db.profile.cvar_default_cache
		if (cvarCache [variableName]) then
			SetCVar (variableName, cvarCache [variableName])
			cvarCache [variableName] = nil
			return true
		end
	end
	
	--return if the unit is in the friends list
	function Plater.IsUnitInFriendsList (unitFrame)
		return Plater.FriendsCache [unitFrame.unitNameInternal]
	end
	
	--> api version of the tap denied function
	function Plater.IsUnitTapped (unitFrame)
		return Plater.IsUnitTapDenied (unitFrame.unit)
	end
	
	--set if Plater will check for the execute range and what percent of life is require to enter in the execute range
	--healthAmount is a floor com zero to one, example: 25% is 0.25
	function Plater.SetExecuteRange (isExecuteEnabled, healthAmountLower, healthAmountUpper)
		DB_USE_HEALTHCUTOFF = isExecuteEnabled
		DB_HEALTHCUTOFF_AT = tonumber (healthAmountLower) or -0.1
		DB_HEALTHCUTOFF_AT_UPPER = tonumber (healthAmountUpper) or 1.1
	end
	
	--return the name of the unit guild
	function Plater.GetUnitGuildName (unitFrame)
		return unitFrame.PlateFrame.playerGuildName
	end
	
	--return if the nameplate is showing an aura
	function Plater.NameplateHasAura (unitFrame, aura)
		return unitFrame.BuffFrame.AuraCache [aura] or unitFrame.BuffFrame2.AuraCache [aura] or unitFrame.ExtraIconFrame.AuraCache [aura]
	end
	
	--return if the unit has a specific aura
	function Plater.UnitHasAura (unitFrame, aura)
		return unitFrame and unitFrame.AuraCache and aura and unitFrame.AuraCache [aura]
	end
	
	--return if the unit has an enrage effect
	function Plater.UnitHasEnrage (unitFrame)
		return unitFrame and unitFrame.AuraCache and unitFrame.AuraCache.hasEnrage
	end
	
	--return if the unit has a dispellable effect
	function Plater.UnitHasDispellable (unitFrame)
		return unitFrame and unitFrame.AuraCache and unitFrame.AuraCache.canStealOrPurge
	end
	
	--get npc color set in the colors tab, it is getting the color set by the user with or without the "scripts only checked"
	function Plater.GetNpcColor(unitFrame)
		local npcId = unitFrame[MEMBER_NPCID]
		if (npcId) then
			return DB_UNITCOLOR_CACHE[npcId] or DB_UNITCOLOR_SCRIPT_CACHE[npcId]
		end
	end

	--pass some colors and return the first valid color
    function Plater.GetColorByPriority(unitFrame, color1, color2, color3)
        if (unitFrame) then
			--from the Npc Colors and Names
            local npcColor = Plater.GetNpcColor(unitFrame)
            if (npcColor) then
                return npcColor
            end
        end
        
        if (color1) then
            return color1
        end
        
        if (color2) then
            return color2
        end

		if (color3) then
            return color3
        end
    end

	--return which raid mark the namepalte has
	function Plater.GetRaidMark (unitFrame)
		if (unitFrame.IsSelf) then
			return false
		end
		return GetRaidTargetIndex (unitFrame.unit)
	end

	--limit the text size of a font string
	function Plater.LimitTextSize (fontString, maxWidth)
		if (not fontString) then
			Plater:Msg ("Plater.LimitTextSize() with not fontString")
			return
		end
		
		maxWidth = max (maxWidth or 0, 10)
		local text = fontString:GetText()
		
		if not text or text == "" then
			return
		end
		
		while (fontString:GetStringWidth() > maxWidth) do
			text = strsub (text, 1, #text - 1)
			fontString:SetText (text)
			if (string.len (text) <= 1) then
				break
			end
		end	
		
		-- cleanup utf8...
		text = DF:CleanTruncateUTF8String(text)
		fontString:SetText (text)
		
	end
	
	--create a custom aura checking, this reset the currently shown auras and only check for auras the script passed
	--this is only called from scripts
	--@buffList: a table with aura names as keys and true as the value, example: ["aura name"] = true
	--@debuffList: same as above
	--@noSpecialAuras: won't check special auras
	function Plater.CheckAuras (self, buffList, debuffList, noSpecialAuras)
		local buffFrame = self.BuffFrame
		local buffFrame2 = self.BuffFrame2
		
		Plater.ResetAuraContainer (buffFrame)
		
		Plater.TrackSpecificAuras (buffFrame, self.unit, true, buffList, self.IsSelf, noSpecialAuras)
		Plater.TrackSpecificAuras (buffFrame, self.unit, false, debuffList, self.IsSelf, noSpecialAuras)
		
		Plater.HideNonUsedAuraIcons (buffFrame)
		
		--update the buff layout and alpha
		buffFrame.unit = self.unit
		Plater.AlignAuraFrames (buffFrame)
		--buffFrame:SetAlpha (DB_AURA_ALPHA)
		
		if (DB_AURA_SEPARATE_BUFFS) then
			buffFrame2.unit = self.unit
			Plater.AlignAuraFrames (buffFrame2)
			--buffFrame2:SetAlpha (DB_AURA_ALPHA)
		end
		Plater.RunScriptTriggersForAuraIcons (unitFrame)
	end
	
	--return the health bar and the unitname text
	function Plater.GetHealthBar (unitFrame)
		--check if the plateFrame has been passed instead
		if (unitFrame.unitFrame) then
			unitFrame = unitFrame.unitFrame
		end
		return unitFrame.healthBar, unitFrame.healthBar.unitName
	end
	--return the cast bar and the spellname text
	function Plater.GetCastBar (unitFrame)
		--check if the plateFrame has been passed instead
		if (unitFrame.unitFrame) then
			unitFrame = unitFrame.unitFrame
		end
		return unitFrame.castBar, unitFrame.castBar.Text
	end
	
	--create a glow around the frame using LibCustomGlow - defaults to "button" glow
	--[[ options can be used to create different glow types, see https://www.curseforge.com/wow/addons/libcustomglow
		--type "pixel"
		options = {
			glowType = "pixel",
			color = "white", -- all plater color types accepted, from lib: {r,g,b,a}, color of lines and opacity, from 0 to 1. Defaul value is {0.95, 0.95, 0.32, 1}
			N = 8, -- number of lines. Defaul value is 8;
			frequency = 0.25, -- frequency, set to negative to inverse direction of rotation. Default value is 0.25;
			length = 4, -- length of lines. Default value depends on region size and number of lines;
			th = 2, -- thickness of lines. Default value is 2;
			xOffset = 0,
			yOffset = 0, -- offset of glow relative to region border;
			border = false, -- set to true to create border under lines;
			key = "", -- key of glow, allows for multiple glows on one frame;
		}
		
		-- type "ants"
		options = {
			glowType = "ants",
			color = "white", -- all plater color types accepted, from lib: {r,g,b,a}, color of lines and opacity, from 0 to 1. Defaul value is {0.95, 0.95, 0.32, 1}
			N = 4, -- number of particle groups. Each group contains 4 particles. Defaul value is 4;
			frequency = 0.125, -- frequency, set to negative to inverse direction of rotation. Default value is 0.125;
			scale = 1, -- scale of particles
			xOffset = 0,
			yOffset = 0, -- offset of glow relative to region border;
			key = "", -- key of glow, allows for multiple glows on one frame;
		}
		
		-- type "button"
		options = {
			glowType = "button",
			color = "white", -- all plater color types accepted, from lib: {r,g,b,a}, color of lines and opacity, from 0 to 1. Defaul value is {0.95, 0.95, 0.32, 1}
			frequency = 0.125, -- frequency, set to negative to inverse direction of rotation. Default value is 0.125;
		}
	--]]
	function Plater.StartGlow(frame, color, options, key)
		if not frame then return end
		
		if not color and (options and options.color) then
			color = options.color
		end
		if color then
			local r, g, b, a = DF:ParseColors (color)
			color = {r, g, b, a}
			options.color = color
		end
		
		if not options then
			options = {
				glowType = "button",
				color = color,
				key = key or "",
			}
		end
		
		if not options.glowType then
			options.glowType = "button"
		end
		
		if key then
			options.key = key
		end
		
		if (not frame.__PlaterGlowFrame) then
			frame.__PlaterGlowFrame = CreateFrame("Frame", nil, frame, BackdropTemplateMixin and "BackdropTemplate");
			frame.__PlaterGlowFrame:SetAllPoints(frame);
			frame.__PlaterGlowFrame:SetSize(frame:GetSize());
		end
		
		if options.glowType == "button" then
			LCG.ButtonGlow_Start(frame.__PlaterGlowFrame, options.color, options.frequency)
		elseif options.glowType == "pixel" then
			if not options.border then options.border = false end
			LCG.PixelGlow_Start(frame.__PlaterGlowFrame, options.color, options.N, options.frequency, options.length, options.th, options.xOffset, options.yOffset, options.border, options.key or "")
		elseif options.glowType == "ants" then
			LCG.AutoCastGlow_Start(frame.__PlaterGlowFrame, options.color, options.N, options.frequency, options.scale, options.xOffset, options.yOffset, options.key or "")
		end
	end
	
	-- creates a button glow effect
	function Plater.StartButtonGlow(frame, color, options, key)
		-- type "button"
		if not options then
			options = {
				glowType = "button",
				color = color, -- all plater color types accepted, from lib: {r,g,b,a}, color of lines and opacity, from 0 to 1. Defaul value is {0.95, 0.95, 0.32, 1}
				frequency = 0.125, -- frequency, set to negative to inverse direction of rotation. Default value is 0.125;
				key = key or "", -- key of glow, allows for multiple glows on one frame;
			}
		else
			options.glowType = "button"
		end
		
		Plater.StartGlow(frame, color or options.color, options, options.key)
	end
	
	-- creates an ants glow effect
	function Plater.StartAntsGlow(frame, color, options, key)
		-- type "ants"
		if not options then
			options = {
				glowType = "ants",
				color = color,
				N = 4, -- number of particle groups. Each group contains 4 particles. Defaul value is 4;
				frequency = 0.125, -- frequency, set to negative to inverse direction of rotation. Default value is 0.125;
				scale = 1, -- scale of particles
				xOffset = 0,
				yOffset = 0, -- offset of glow relative to region border;
				key = key or "", -- key of glow, allows for multiple glows on one frame;
			}
		else
			options.glowType = "ants"
		end
		
		Plater.StartGlow(frame, color or options.color, options, options.key)
	end
	
	-- creates a pixel glow effect
	function Plater.StartPixelGlow(frame, color, options, key)
		-- type "pixel"
		if not options then
			options = {
				glowType = "pixel",
				color = color, -- all plater color types accepted, from lib: {r,g,b,a}, color of lines and opacity, from 0 to 1. Defaul value is {0.95, 0.95, 0.32, 1}
				N = 8, -- number of lines. Defaul value is 8;
				frequency = 0.25, -- frequency, set to negative to inverse direction of rotation. Default value is 0.25;
				--length = 4, -- length of lines. Default value depends on region size and number of lines;
				th = 2, -- thickness of lines. Default value is 2;
				xOffset = 0,
				yOffset = 0, -- offset of glow relative to region border;
				border = false, -- set to true to create border under lines;
				key = key or "", -- key of glow, allows for multiple glows on one frame;
			}
		else
			options.glowType = "pixel"
		end
		
		Plater.StartGlow(frame, color or options.color, options, options.key)
	end
	
	-- stop LibCustomGlow effects on the frame, if existing
	-- if glowType (and key) are given, stop one glow. if not, stop all.
	function Plater.StopGlow(frame, glowType, key)
		if not frame then return end
		if not frame.__PlaterGlowFrame then return end
		
		if glowType then		
			if glowType == "button" then
				LCG.ButtonGlow_Stop(frame.__PlaterGlowFrame, key or "")
			elseif glowType == "pixel" then
				LCG.PixelGlow_Stop(frame.__PlaterGlowFrame, key or "")
			elseif glowType == "ants" then
				LCG.AutoCastGlow_Stop(frame.__PlaterGlowFrame, key or "")
			end
		else
			LCG.ButtonGlow_Stop(frame.__PlaterGlowFrame, key or "")
			LCG.PixelGlow_Stop(frame.__PlaterGlowFrame, key or "")
			LCG.AutoCastGlow_Stop(frame.__PlaterGlowFrame, key or "")
		end
	end
	
	-- stop a button glow
	function Plater.StopButtonGlow(frame, key)
		Plater.StopGlow(frame, "button", key)
	end
	
	-- stop a button glow
	function Plater.StopPixelGlow(frame, key)
		Plater.StopGlow(frame, "pixel", key)
	end
	
	-- stop an ants glow
	function Plater.StopAntsGlow(frame, key)
		Plater.StopGlow(frame, "ants", key)
	end

	--create a glow around an icon
	function Plater.CreateIconGlow (frame, color, color2, useShowAnimation)
		local f = Plater:CreateGlowOverlay (frame, color, color2 or color)
		if not useShowAnimation and IS_WOW_PROJECT_MAINLINE then
			f:SetScript("OnShow", nil) --reset
			
			local onShow = function(self)
				if (self.ProcStartAnim) then
					self.ProcStartAnim:Stop()
					self.ProcStartFlipbook:Hide()
					if (not self.ProcLoop:IsPlaying()) then
						self.ProcLoop:Play()
					end
				end
			end
			
			f:SetScript("OnShow", onShow)
		end
		return f
	end

	--create a glow around the healthbar or castbar frame
	function Plater.CreateNameplateGlow (frame, color, left, right, top, bottom)
		local antTable = {
			Throttle = 0.025,
			AmountParts = 15,
			TexturePartsWidth = 167.4,
			TexturePartsHeight = 83.6,
			TextureWidth = 512,
			TextureHeight = 512,
			BlendMode = "ADD",
			Color = color,
			Texture = [[Interface\AddOns\Plater\images\ants_rectangle]],
		}

		--> ants
		local f = DF:CreateAnts (frame, antTable, -27 + (left or 0), 25 + (right or 0), 5 + (top or 0), -7 + (bottom or 0))
		f:SetFrameLevel (frame:GetFrameLevel() + 1)
		f:SetAlpha (ALPHA_BLEND_AMOUNT - 0.249845)
		
		--> glow
		local glow = f:CreateTexture (nil, "background")
		glow:SetTexture ([[Interface\AddOns\Plater\images\nameplate_glow]])
		PixelUtil.SetPoint (glow, "center", frame, "center", 0, 0)
		glow:SetSize (frame:GetWidth() + frame:GetWidth()/2.3, 36)
		glow:SetBlendMode ("ADD")
		glow:SetVertexColor (DF:ParseColors (color or "white"))
		glow:SetAlpha (ALPHA_BLEND_AMOUNT)
		glow.GlowTexture = glow
		
		return f
	end

	function Plater.OnPlayCustomFlashAnimation (animationHub)
		animationHub:GetParent():Show()
		animationHub.Texture:Show()
		--animationHub.Texture:Show()
	end
	function Plater.OnStopCustomFlashAnimation (animationHub)
		animationHub:GetParent():Hide()
		animationHub.Texture:Hide()
	end
	function Plater.UpdateCustomFlashAnimation (animationHub, duration, r, g, b)
		for i = 1, #animationHub.AllAnimations do
			if (duration) then
				animationHub.AllAnimations [i]:SetDuration (duration)
			end
			if (r) then
				r, g, b = DF:ParseColors (r, g, b)
				animationHub.Texture:SetColorTexture (r, g, b)
			end
		end
	end

	--creates a flash, call returnedValue:Play() to flash
	function Plater.CreateFlash (frame, duration, amount, r, g, b, a)
		--defaults
		duration = duration or 0.25
		amount = amount or 1
		
		if (not r) then
			r, g, b, a = 1, 1, 1, 1
		else
			r, g, b, a = DF:ParseColors (r, g, b, a)
		end

		--create the flash frame
		local f = CreateFrame ("frame", "PlaterFlashAnimationFrame".. math.random (1, 100000000), frame, BackdropTemplateMixin and "BackdropTemplate")
		f:SetFrameLevel (frame:GetFrameLevel()+1)
		f:SetAllPoints()
		f:Hide()
		
		--create the flash texture
		local t = f:CreateTexture ("PlaterFlashAnimationTexture".. math.random (1, 100000000), "artwork")
		t:SetColorTexture (r, g, b, a)
		t:SetAllPoints()
		t:SetBlendMode ("ADD")
		t:Hide()
		
		--create the flash animation
		local animationHub = DF:CreateAnimationHub (f, Plater.OnPlayCustomFlashAnimation, Plater.OnStopCustomFlashAnimation)
		animationHub.AllAnimations = {}
		animationHub.Parent = f
		animationHub.Texture = t
		animationHub.Amount = amount
		animationHub.UpdateDurationAndColor = Plater.UpdateCustomFlashAnimation
		
		for i = 1, amount * 2, 2 do
			local fadeIn = DF:CreateAnimation (animationHub, "ALPHA", i, duration, 0, 1)
			local fadeOut = DF:CreateAnimation (animationHub, "ALPHA", i + 1, duration, 1, 0)
			tinsert (animationHub.AllAnimations, fadeIn)
			tinsert (animationHub.AllAnimations, fadeOut)
		end
		
		return animationHub
	end

	--called to undo a color modification
	function Plater.RefreshNameplateColor (unitFrame)
		if (unitFrame.unit) then
			if (Plater.IsUnitTapDenied (unitFrame.unit)) then
				Plater.ChangeHealthBarColor_Internal (unitFrame.healthBar, unpack (Plater.db.profile.tap_denied_color))
			elseif (DB_UNITCOLOR_CACHE [unitFrame [MEMBER_NPCID] or -1]) then
				Plater.ChangeHealthBarColor_Internal (unitFrame.healthBar, unpack (DB_UNITCOLOR_CACHE [unitFrame [MEMBER_NPCID]]))
				unitFrame.UsingCustomColor = true --exposed to scripts
			else
				if (InCombatLockdown()) then
					local unitReaction = unitFrame.PlateFrame [MEMBER_REACTION]
					if (unitReaction == 4 and not unitFrame.InCombat) then
						Plater.FindAndSetNameplateColor (unitFrame, true)
					elseif (DB_AGGRO_CHANGE_HEALTHBAR_COLOR and unitFrame.CanCheckAggro and unitReaction <= 4) then
						Plater.UpdateNameplateThread (unitFrame)
					else
						Plater.FindAndSetNameplateColor (unitFrame)
					end
				else
					Plater.FindAndSetNameplateColor (unitFrame)
				end
			end
		end
	end

	--modify the color of the health bar
	function Plater.SetNameplateColor (unitFrame, r, g, b, a)
		if (unitFrame.unit) then
			if (not r) then
				Plater.RefreshNameplateColor (unitFrame)
			else
				r, g, b, a = DF:ParseColors (r, g, b, a)
				return Plater.ChangeHealthBarColor_Internal (unitFrame.healthBar, r, g, b, a)
			end
		end
	end

	--set a temporarly size for the healthbar
	--this value is reset when the nameplate is added to the screen
	function Plater.SetNameplateSize (unitFrame, width, height)
		unitFrame.customHealthBarWidth = width
		unitFrame.customHealthBarHeight = height
		Plater.UpdatePlateSize (unitFrame.PlateFrame)
	end

	--modify the color of the cast bar
	function Plater.SetCastBarColor (unitFrame, r, g, b)
		if (unitFrame.unit) then
			if (not r) then
				--refresh the castbar color (the framework adjust the cast bar color)
				unitFrame.castBar:UpdateCastColor()
			else
				--framework accepts SetColor, it does parse the color within the call
				return unitFrame.castBar:SetColor (r, g, b)
			end
		end
	end
	
	--set a temporarly size for the castbar
	--this value is reset when the nameplate is added to the screen
	function Plater.SetCastBarSize (unitFrame, width, height)
		unitFrame.customCastBarWidth = width
		unitFrame.customCastBarHeight = height
		Plater.UpdatePlateSize (unitFrame.PlateFrame)
	end
	
	--same thing as the two above but for the power bar
	function Plater.SetPowerBarSize (unitFrame, width, height)
		unitFrame.customPowerBarWidth = width
		unitFrame.customPowerBarHeight = height
		Plater.UpdatePlateSize (unitFrame.PlateFrame)
	end
	
	---changes the border color, this call is for the API, can be called from external sources
	---@param self table unitFrame
	---@param r any
	---@param g number|nil
	---@param b number|nil
	---@param a number|nil
	function Plater.SetBorderColor(self, r, g, b, a)
		if (not r) then
			self.customBorderColor = nil
			Plater.UpdateBorderColor(self)
			return
		end
		
		r, g, b, a = DF:ParseColors(r, g, b, a)
		
		--UpdateBorderColor will use the value set on customBorderColor member if any
		self.customBorderColor = {r, g, b, a}
		
		Plater.UpdateBorderColor(self)
	end

	---flashes on the health bar border
	---@param unitFrame table unitFrame
	---@param duration number
	function Plater.FlashNameplateBorder(unitFrame, duration)
		if (not unitFrame.healthBar.PlayHealthFlash) then
			Plater.CreateHealthFlashFrame(unitFrame.PlateFrame)
		end
		unitFrame.healthBar.canHealthFlash = true
		unitFrame.healthBar.PlayHealthFlash(duration)
	end

	---flashes the unitFrame body showing a text in the middle of the flash texture, by default this call is use to show aggro alerts with the word "-AGGRO-"
	---@param unitFrame table unitFrame
	---@param text string
	---@param duration number
	function Plater.FlashNameplateBody(unitFrame, text, duration)
		--sending true to ignore cooldown
		unitFrame.PlateFrame.PlayBodyFlash(text, duration, true) --weird, there's no reference to the plateFrame
	end

	---return if the player is in combat
	---@return boolean bIsPlayerInCombat
	function Plater.IsInCombat()
		return InCombatLockdown() or PLAYER_IN_COMBAT
	end

	---return true if the unit is in the tank role
	---@param unitFrame table unitFrame
	---@return boolean bIsUnitInTankRole
	function Plater.IsUnitTank(unitFrame)
		return TANK_CACHE[unitFrame.unitNameInternal]
	end
	
	---check the player role and role specialization and return if it is in the tank role
	---@return boolean bIsPlayerInTankRole
	function Plater.IsPlayerTank()
		return IsPlayerEffectivelyTank()
	end
	
	---return the table where tanks is stored
	---has the unit name as the key and true as value
	---@return table<string, boolean> tankList
	function Plater.GetTanks()
		return TANK_CACHE
	end

	--change the color of the cast bar
	function Plater.SetCastBarBorderColor (castBar, r, g, b, a)
		--check if the frame passed was the unitFrame instead of the castbar it self
		if (castBar.castBar) then
			castBar = castBar.castBar
		end
		
		if (not r) then
			castBar.FrameOverlay:SetBackdropBorderColor (0, 0, 0, 0)
			return
		end
		
		r, g, b, a = DF:ParseColors (r, g, b, a)
		castBar.FrameOverlay:SetBackdropBorderColor (r, g, b, a)
	end

	--show the health bar, the health bar is shown by default, use to undo HideHealthBar() call
	function Plater.ShowHealthBar (unitFrame)
		unitFrame.healthBar:Show()
		unitFrame.BuffFrame:Show()
		unitFrame.BuffFrame2:Show()
		unitFrame.ExtraIconFrame:Show()
		unitFrame.healthBar.unitName:Show()
		
		unitFrame.PlateFrame.IsFriendlyPlayerWithoutHealthBar = false
		unitFrame.PlateFrame.IsNpcWithoutHealthBar = false
		
		unitFrame.ActorNameSpecial:Hide()
		unitFrame.ActorTitleSpecial:Hide()
		
		Plater.UpdatePlateText (unitFrame.PlateFrame, DB_PLATE_CONFIG [unitFrame.ActorType], true)
	end

	--hide the health bar and show the secondary unit name and title text strings
	function Plater.HideHealthBar (unitFrame, showPlayerName, showNameNpc)
		unitFrame.healthBar:Hide()
		unitFrame.BuffFrame:Hide()
		unitFrame.BuffFrame2:Hide()
		unitFrame.ExtraIconFrame:Hide()
		unitFrame.healthBar.unitName:Hide()
		
		showPlayerName = showPlayerName and (unitFrame.ActorType == ACTORTYPE_FRIENDLY_PLAYER or unitFrame.ActorType == ACTORTYPE_ENEMY_PLAYER)
		showNameNpc = showNameNpc and (unitFrame.ActorType == ACTORTYPE_FRIENDLY_NPC or unitFrame.ActorType == ACTORTYPE_ENEMY_NPC)
		
		unitFrame.PlateFrame.IsFriendlyPlayerWithoutHealthBar = showPlayerName
		unitFrame.PlateFrame.IsNpcWithoutHealthBar = showNameNpc
		
		if (showPlayerName) then
			Plater.UpdatePlateText (unitFrame.PlateFrame, DB_PLATE_CONFIG [unitFrame.ActorType], true)
			
		elseif (showNameNpc) then
			Plater.UpdatePlateText (unitFrame.PlateFrame, DB_PLATE_CONFIG [unitFrame.ActorType], true)
		end
	end
	
	--forces a range check regardless of the user options and only changes the member_range flag, no alpha changes
	--if the spell name is passed, it just return the result without modifying the nameplate attributes
	function Plater.NameplateInRange (unitFrame, spellName)
		if (spellName) then
			return C_Spell.IsSpellInRange (spellName, unitFrame [MEMBER_UNITID])

		else
			local rangeChecker
			if unitFrame [MEMBER_REACTION] < 5 then 
				rangeChecker = Plater.RangeCheckFunctionEnemy
			else
				rangeChecker = Plater.RangeCheckFunctionFriendly
			end
			if (rangeChecker and rangeChecker (unitFrame [MEMBER_UNITID])) then
				unitFrame [MEMBER_RANGE] = true
				return true
			else
				unitFrame [MEMBER_RANGE] = false
				return false
			end
		end
	end

	
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> scripting ~scripting
	
	--the mod/script error handler
	local errorContext = {}
	local prevErrors = {}
	local ErrorHandler = function(errorMessage)
		errorContext.message = errorContext.message or "Plater error: "
		local msg = errorContext.message .. errorMessage
		--throttle error messages a bit...
		local lastTime = prevErrors[msg]
		local curTime = GetTime()
		if lastTime and curTime - lastTime < 1 then
			return
		end
		prevErrors[msg] = curTime
		DevTool:AddData(errorContext)
		local modscriptInfo = errorContext.modscript and errorContext.modscript.url and ("Mod/Script URL: " .. errorContext.modscript.url .. "\n") or nil
		if BugGrabber then
			geterrorhandler()(errorContext.message .. "\n" .. (modscriptInfo or "") .. Plater.fullVersionInfo .. "\n" .. errorMessage)
		end
		Plater:Msg (msg .. (modscriptInfo and ("\n" .. modscriptInfo) or ""))
		errorContext = {}
		return errorMessage
	end
	local GetErrorHandler = function(contextMessage, contextModScript)
		errorContext.message = contextMessage
		errorContext.modscript = contextModScript
		return ErrorHandler
	end
	platerInternal.GetErrorHandler = GetErrorHandler


	--scripts mixin - these functions are mixed in with castbar, unitframe and aura icons
	Plater.ScriptMetaFunctions = {
		--get the table which stores all script information for the widget
		--self is the affected widget, e.g. icon frame, unitframe, castbar progressbar
		ScriptGetContainer = function(self)
			local infoTable = self.ScriptInfoTable
			if (not infoTable) then
				self.ScriptInfoTable = {}
				return self.ScriptInfoTable
			else
				return infoTable
			end
		end,
		
		--get the table which stores the information for a single script
		--run for hooks only
		HookGetInfo = function (self, globalScriptObject, scriptContainer)
			scriptContainer = scriptContainer or self:ScriptGetContainer()
			
			--using the memory address of the original scriptObject from db.profile as the map key
			local scriptInfo = scriptContainer[globalScriptObject.DBScriptObject.scriptId]

			if (
				(not scriptInfo) or 
				(scriptInfo.GlobalScriptObject.NeedHotReload) or 
				(scriptInfo.GlobalScriptObject.Build and scriptInfo.GlobalScriptObject.Build < PLATER_HOOK_BUILD)
			) then
				local forceHotReload = scriptInfo and scriptInfo.GlobalScriptObject.NeedHotReload
			
				--keep script info and update as needed
				scriptInfo = scriptInfo or {
					GlobalScriptObject = globalScriptObject, 
					HotReload = -1, 
					Env = {}, 
					IsActive = false
				}
				scriptInfo.GlobalScriptObject = globalScriptObject
				scriptInfo.GlobalScriptObject.Build = PLATER_HOOK_BUILD
				scriptInfo.GlobalScriptObject.NeedHotReload = false

				if (globalScriptObject.HasConstructor and (not scriptInfo.Initialized or forceHotReload)) then
					local modName = scriptInfo.GlobalScriptObject.DBScriptObject.Name
					Plater.StartLogPerformance("Mod-RunHooks", modName, "Constructor")
					local okay, errortext = xpcall (globalScriptObject.Constructor, GetErrorHandler("Plater Mod |cFFAAAA22" .. modName .. "|r Constructor error: ", globalScriptObject.DBScriptObject), self, self.displayedUnit or self.unit or self:GetParent()[MEMBER_UNITID], self, scriptInfo.Env, PLATER_GLOBAL_MOD_ENV [scriptInfo.GlobalScriptObject.DBScriptObject.scriptId])
					Plater.EndLogPerformance("Mod-RunHooks", modName, "Constructor")
					if (not okay) then
						--handled via error handler
						--Plater:Msg ("Plater Mod |cFFAAAA22" .. modName .. "|r Constructor error: " .. errortext)
					end
					scriptInfo.Initialized = true
				end
				
				scriptContainer [globalScriptObject.DBScriptObject.scriptId] = scriptInfo
			end
			
			--always overwriting the globalScriptObject fixes the issue for not updating the script after saving it but only for OnShow OnUpdate and OnHide
			scriptInfo.GlobalScriptObject = globalScriptObject
			return scriptInfo
		end,

		--get the table which stores the information for a single script
		--run for scripts only
		ScriptGetInfo = function (self, globalScriptObject, scriptContainer)
			scriptContainer = scriptContainer or self:ScriptGetContainer()
			
			--using the memory address of the original scriptObject from db.profile as the map key
			local scriptInfo = scriptContainer[globalScriptObject.DBScriptObject.scriptId]

			local lastUpdateTime = globalScriptObject.LastUpdateTime or 0
			if (not scriptInfo or scriptInfo.LastUpdateTime <= lastUpdateTime) then
				--create script info
				if (not scriptInfo) then
					scriptInfo = {
						--GlobalScriptObject = globalScriptObject, --is set below
						HotReload = -1, --deprecated
						Env = {}, 
						IsActive = false
					}
				end

				scriptInfo.LastUpdateTime = GetTime()
				--scriptInfo.GlobalScriptObject = globalScriptObject
				
				scriptContainer [globalScriptObject.DBScriptObject.scriptId] = scriptInfo
			end
			
			--always overwriting the globalScriptObject fixes the issue for not updating the script after saving it but only for OnShow OnUpdate and OnHide
			scriptInfo.GlobalScriptObject = globalScriptObject
			return scriptInfo
		end,
		
		--if the global script had an update or if the first time running this script on this widget, run the constructor
		ScriptHotReload = function (self, scriptInfo)
			--dispatch constructor if necessary
			if (scriptInfo.HotReload < scriptInfo.GlobalScriptObject.HotReload) then
				--update the hotreload state
				scriptInfo.HotReload = scriptInfo.GlobalScriptObject.HotReload

				--there's some bug with the global env becoming nil after saving a script
				--print(scriptInfo.GlobalScriptObject.DBScriptObject.Name, PLATER_GLOBAL_SCRIPT_ENV [scriptInfo.GlobalScriptObject.DBScriptObject.scriptId])

				--dispatch the constructor
				local unitFrame = self.unitFrame or self
				local scriptName = scriptInfo.GlobalScriptObject.DBScriptObject.Name
				Plater.StartLogPerformance("Scripts", scriptName, "Constructor")
				local okay, errortext = xpcall (scriptInfo.GlobalScriptObject ["ConstructorCode"], GetErrorHandler("Plater Script |cFFAAAA22" .. scriptName .. "|r Constructor error: ", scriptInfo.GlobalScriptObject.DBScriptObject), self, unitFrame.displayedUnit or unitFrame.unit or unitFrame.PlateFrame[MEMBER_UNITID], unitFrame, scriptInfo.Env, PLATER_GLOBAL_SCRIPT_ENV [scriptInfo.GlobalScriptObject.DBScriptObject.scriptId])
				Plater.EndLogPerformance("Scripts", scriptName, "Constructor")
				if (not okay) then
					--handled via error handler
					--Plater:Msg ("Plater Script |cFFAAAA22" .. scriptName .. "|r Constructor error: " .. errortext)
				end
			end
		end,
		
		--run the update script, called when the castbar updates, from within the tick and from the aura file on the AddAura()
		ScriptRunOnUpdate = function (self, scriptInfo)
			if (not scriptInfo.IsActive) then
				--run constructor
				self:ScriptHotReload (scriptInfo)
				--run on show
				self:ScriptRunOnShow (scriptInfo)
			end
			
			--dispatch the runtime script
			local unitFrame = self.unitFrame or self
			local scriptName = scriptInfo.GlobalScriptObject.DBScriptObject.Name
			Plater.StartLogPerformance("Scripts", scriptName, "OnUpdate")
			local okay, errortext = xpcall (scriptInfo.GlobalScriptObject ["UpdateCode"], GetErrorHandler("Plater Script |cFFAAAA22" .. scriptName .. "|r OnUpdate error: ", scriptInfo.GlobalScriptObject.DBScriptObject), self, unitFrame.displayedUnit or unitFrame.unit or unitFrame.PlateFrame[MEMBER_UNITID], unitFrame, scriptInfo.Env, PLATER_GLOBAL_SCRIPT_ENV [scriptInfo.GlobalScriptObject.DBScriptObject.scriptId])
			Plater.EndLogPerformance("Scripts", scriptName, "OnUpdate")
			if (not okay) then
				--handled via error handler
				--Plater:Msg ("Plater Script |cFFAAAA22" .. scriptName .. "|r OnUpdate error: " .. errortext)
			end
		end,
		
		--run the OnShow script
		ScriptRunOnShow = function(self, scriptInfo)
			--dispatch the on show script
			local unitFrame = self.unitFrame or self
			scriptInfo.Env._DefaultWidth = self:GetWidth()
			scriptInfo.Env._DefaultHeight = self:GetHeight()

			local scriptName = scriptInfo.GlobalScriptObject.DBScriptObject.Name
			local func = scriptInfo.GlobalScriptObject["OnShowCode"]
			
			Plater.StartLogPerformance("Scripts", scriptName, "OnShow")
			local okay, errortext = xpcall(func, GetErrorHandler("Plater Script |cFFAAAA22" .. scriptName .. "|r OnShow error: ", scriptInfo.GlobalScriptObject.DBScriptObject), self, unitFrame.displayedUnit or unitFrame.unit or unitFrame.PlateFrame[MEMBER_UNITID], unitFrame, scriptInfo.Env, PLATER_GLOBAL_SCRIPT_ENV [scriptInfo.GlobalScriptObject.DBScriptObject.scriptId])
			Plater.EndLogPerformance("Scripts", scriptName, "OnShow")

			if (not okay) then
				--handled via error handler
				--Plater:Msg ("Plater Script |cFFAAAA22" .. scriptName .. "|r OnShow error: " .. errortext)
			end
			
			scriptInfo.IsActive = true
			self.ScriptKey = scriptInfo.GlobalScriptObject.ScriptKey
		end,
		
		--run the OnHide script
		ScriptRunOnHide = function (self, scriptInfo)
			--dispatch the on hide script
			local unitFrame = self.unitFrame or self
			local scriptName = scriptInfo.GlobalScriptObject.DBScriptObject.Name
			Plater.StartLogPerformance("Scripts", scriptName, "OnHide")
			local okay, errortext = xpcall (scriptInfo.GlobalScriptObject ["OnHideCode"], GetErrorHandler("Plater Script |cFFAAAA22" .. scriptName .. "|r OnHide error: ", scriptInfo.GlobalScriptObject.DBScriptObject), self, unitFrame.displayedUnit or unitFrame.unit or unitFrame.PlateFrame[MEMBER_UNITID], unitFrame, scriptInfo.Env, PLATER_GLOBAL_SCRIPT_ENV [scriptInfo.GlobalScriptObject.DBScriptObject.scriptId])
			Plater.EndLogPerformance("Scripts", scriptName, "OnHide")
			if (not okay) then
				--handled via error handler
				--Plater:Msg ("Plater Script |cFFAAAA22" .. scriptName .. "|r OnHide error: " .. errortext)
			end
			
			scriptInfo.IsActive = false
			self.ScriptKey = nil
		end,
		
		--run the Initialization script, called during compile time
		ScriptRunInitialization = function (globalScriptObject)
			--dispatch the init script
			local scriptName = globalScriptObject.DBScriptObject.Name
			Plater.StartLogPerformance("Scripts", scriptName, "Initialization")
			local okay, errortext = xpcall (globalScriptObject ["Initialization"], GetErrorHandler("Plater Script |cFFAAAA22" .. scriptName .. "|r Initialization error: ", globalScriptObject.DBScriptObject), PLATER_GLOBAL_SCRIPT_ENV [globalScriptObject.DBScriptObject.scriptId])
			Plater.EndLogPerformance("Scripts", scriptName, "Initialization")
			if (not okay) then
				--handled via error handler
				--Plater:Msg ("Plater Script |cFFAAAA22" .. scriptName .. "|r Initialization error: " .. errortext)
			end
		end,
		
		ScriptRunCommMessageHook = function(globalScriptObject, hookName, source, ...)
			local modName = globalScriptObject.DBScriptObject.Name
			Plater.StartLogPerformance("Mod-RunHooks", modName, hookName)
			local okay, errortext = xpcall (globalScriptObject [hookName], GetErrorHandler("Plater Mod |cFFAAAA22" .. modName .. "|r code for |cFFBB8800" .. hookName .. "|r error: ", globalScriptObject.DBScriptObject), PLATER_GLOBAL_MOD_ENV [globalScriptObject.DBScriptObject.scriptId], source, ...)
			Plater.EndLogPerformance("Mod-RunHooks", modName, hookName)
			if (not okay) then
				--handled via error handler
				--Plater:Msg ("Plater Mod |cFFAAAA22" .. modName .. "|r code for |cFFBB8800" .. hookName .. "|r error: " .. errortext)
			end
		end,
		
		ScriptRunHook = function (self, scriptInfo, hookName, frame, ...)
			--dispatch a hook for the script
			--at the moment, self is always the unit frame
			local modName = scriptInfo.GlobalScriptObject.DBScriptObject.Name
			Plater.StartLogPerformance("Mod-RunHooks", modName, hookName)
			local okay, errortext = xpcall (scriptInfo.GlobalScriptObject [hookName], GetErrorHandler("Plater Mod |cFFAAAA22" .. modName .. "|r code for |cFFBB8800" .. hookName .. "|r error: ", scriptInfo.GlobalScriptObject.DBScriptObject), frame or self, self.displayedUnit, self, scriptInfo.Env, PLATER_GLOBAL_MOD_ENV [scriptInfo.GlobalScriptObject.DBScriptObject.scriptId], ...)
			Plater.EndLogPerformance("Mod-RunHooks", modName, hookName)
			if (not okay) then
				--handled via error handler
				--Plater:Msg ("Plater Mod |cFFAAAA22" .. modName .. "|r code for |cFFBB8800" .. hookName .. "|r error: " .. errortext)
			end
		end,
		
		--run only once without attach to the script or hook
		ScriptRunNoAttach = function (hookInfo, hookName)
			local func = hookInfo [hookName]
			local modName = hookInfo.DBScriptObject.Name
			Plater.StartLogPerformance("Mod-RunHooks", modName, " -NoAttach- " .. hookName)
			local okay, errortext = xpcall (func, GetErrorHandler("Plater Mod |cFFAAAA22" .. modName .. "|r code for |cFFBB8800" .. hookName .. "|r error: ", hookInfo.DBScriptObject), PLATER_GLOBAL_MOD_ENV [hookInfo.DBScriptObject.scriptId])
			Plater.EndLogPerformance("Mod-RunHooks", modName, " -NoAttach- " .. hookName)
			if (not okay) then
				--handled via error handler
				--Plater:Msg ("Plater Mod |cFFAAAA22" .. modName .. "|r code for |cFFBB8800" .. hookName .. "|r error: " .. errortext)
			end
		end,
		
		--run when the widget hides
		OnHideWidget = function(self)
			--check if can quickly quit (if there's no script container for the nameplate)
			if (self.ScriptInfoTable) then
				local triggerCacheTable
				
				if (self.IsAuraIcon) then
					triggerCacheTable = SCRIPT_AURA_TRIGGER_CACHE
				elseif (self.IsCastBar) then
					triggerCacheTable = SCRIPT_CASTBAR_TRIGGER_CACHE
				elseif (self.IsUnitNameplate) then
					triggerCacheTable = SCRIPT_UNIT_TRIGGER_CACHE
				end

				--ScriptKey holds the trigger of the script currently running
				local globalScriptObject = triggerCacheTable[self.ScriptKey]
				
				--does the aura has a custom script?
				if (globalScriptObject) then
					--does the aura icon has a table with script information?
					local scriptContainer = self:ScriptGetContainer() --return self.ScriptInfoTable
					if (scriptContainer) then
						local scriptInfo = self:ScriptGetInfo(globalScriptObject, scriptContainer)
						if (scriptInfo and scriptInfo.IsActive) then
							self:ScriptRunOnHide(scriptInfo)
						end
					end
				end
			end

			--> hooks, check which kind of widget this is and then run the appropriate hook
			if (self.IsCastBar) then
				if (HOOK_CAST_STOP.ScriptAmount > 0) then
					for i = 1, HOOK_CAST_STOP.ScriptAmount do
						local globalScriptObject = HOOK_CAST_STOP [i]
						local unitFrame = self.unitFrame
						local scriptContainer = unitFrame:ScriptGetContainer()
						local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Cast Stop")
						--run
						unitFrame:ScriptRunHook (scriptInfo, "Cast Stop", self)
					end
				end
			end
			
		end,
		
		--stop a running script by the trigger ID
		--this is used when deleting a script or disabling it
		KillScript = function(self, triggerID)
			local triggerCacheTable
			
			if (self.IsAuraIcon) then
				triggerCacheTable = SCRIPT_AURA_TRIGGER_CACHE
				triggerID = GetSpellInfo(triggerID)
				
			elseif (self.IsCastBar) then
				triggerCacheTable = SCRIPT_CASTBAR_TRIGGER_CACHE
				triggerID = GetSpellInfo(triggerID)
				
			elseif (self.IsUnitNameplate) then
				triggerCacheTable = SCRIPT_UNIT_TRIGGER_CACHE
			end
			
			if (self.ScriptKey and self.ScriptKey == triggerID) then
				local globalScriptObject = triggerCacheTable[triggerID]
				if (globalScriptObject) then
					local scriptContainer = self:ScriptGetContainer()
					if (scriptContainer) then
						local scriptInfo = self:ScriptGetInfo (globalScriptObject, scriptContainer)
						if (scriptInfo and scriptInfo.IsActive) then
							self:ScriptRunOnHide (scriptInfo)
						end
					end
				end
			end
		end,
		
		IsProtected = function (self)
		-- assume that nameplates are always protected since 8.2
			if self then
				if self.PlateFrame then
					return self.PlateFrame:IsProtected()
				end
			end
			
			return false
		end,
	}
	 
	function Plater.GetAllScripts (scriptType)
		if (scriptType == "script") then
			return Plater.db.profile.script_data
		elseif (scriptType == "hook") then
			return Plater.db.profile.hook_data
		end
	end
	
	function Plater.GetAllScriptsAsPrioSortedCopy (scriptType)
		local scripts
		local seeds = {}
		
		local function copyHookTables (t1, t2)
			for key, value in pairs (t2) do 
				if (key ~= "__index") then
					if (type (value) == "table") then
						t1 [key] = t1 [key] or {}
						-- add hashID to the hook-data
						t1 [key].scriptId = tostring(value) -- keep this internal hashed

						--create UID if it does not exist --TODO maybe not needed in the future
						local uID = value.UID
						if not uID then
							local seed = tonumber(value.Time) or time()
							while seeds[seed] do
								seed = math.random(value.Time)
							end
							seeds[seed] = true
							
							uID = Plater.CreateUniqueIdentifier(seed)
							--value.UID = uID -- TODO permanently set UID
							t1 [key].UID = uID -- TODO temporary volatile for now
						end
						
						DF.table.copy (t1 [key], t2 [key])
					else
						t1 [key] = value
					end
				end
			end
			return t1
		end
		
		if (scriptType == "script") then
			--cleanup first
			for scriptId, scriptObject in ipairs (Plater.db.profile.script_data) do
				scriptObject.scriptId = nil
			end
			
			--copy
			scripts = copyHookTables({}, Plater.db.profile.script_data)
			
		elseif (scriptType == "hook") then
			--cleanup first
			for scriptId, scriptObject in ipairs (Plater.db.profile.hook_data) do
				scriptObject.scriptId = nil
			end
			
			--copy
			scripts = copyHookTables({}, Plater.db.profile.hook_data)
		end
		
		local function round(x)
			if not x then return nil end
			return (x + 0.5 - (x + 0.5) % 1)
		end
		
		if scripts then
			table.sort(scripts, function(a,b)
				if a and not b then
					return false
				elseif not a and b then
					return true
				else
					return round(a.Prio or 99) > round(b.Prio or 99)
				end
				return false
			end)
		end
		--table.foreach(scripts, function(index, self) if self then print(self.Prio, self.Name) else print("not self") end end)
		return scripts
	end
	
	--compile all scripts
	function Plater.CompileAllScripts (scriptType, noHotReload)
		if (scriptType == "script") then
			for scriptId, scriptObject in ipairs (Plater.GetAllScriptsAsPrioSortedCopy ("script")) do
				if (scriptObject.Enabled) then
					Plater.CompileScript (scriptObject, noHotReload)
				end
			end
		elseif (scriptType == "hook") then
			--get all hook scripts from the profile database
			for scriptId, scriptObject in ipairs (Plater.GetAllScriptsAsPrioSortedCopy ("hook")) do
				Plater.CompileHook (scriptObject, noHotReload)
			end
		end
	end

	--recompile a single scriptObject deleting the global environment
	function Plater.RecompileScript(scriptObject)
		if not scriptObject.scriptId then
			scriptObject.scriptId = tostring(scriptObject)
		end
		local scriptType = Plater.GetScriptType(scriptObject)
		if (scriptType == "script") then
			Plater.CompileScript(scriptObject)

		elseif (scriptType == "hook") then
			Plater.CompileHook(scriptObject)
		end
	end

	function Plater.IsModEnabled(modName)
		for scriptId, scriptObject in ipairs (Plater.db.profile.hook_data) do
			if (scriptObject.Name == modName) then
				if (scriptObject.Enabled) then
					return true
				end
			end
		end
	end

	--when a script object get disabled, need to clear all compiled scripts in the cache and recompile than again
	--this other scripts that uses the same trigger name get activated
	-- ~scripts

	Plater.CoreVersion = 1
	
	--internal Plater functions
	--in the list/false: can't be overwritten; true->can't be read at all
	local privateFunctions = {
		["Plater"] = {
			["CompileAllScripts"] = true,
			["GetAllScripts"] = true,
			["GetAllScriptsAsPrioSortedCopy"] = true,
			["ScriptMetaFunctions"] = true,
			["DecompressData"] = true,
			["CompressData"] = true,
			["WipeAndRecompileAllScripts"] = true,
			["AllHookGlobalContainers"] = true,
			["WipeHookContainers"] = true,
			["GetContainerForHook"] = true,
			["CurrentlyLoadedHooks"] = true,
			["RunDestructorForHook"] = true,
			["CompileHook"] = true,
			["CompileScript"] = true,
			["CheckScriptTriggerOverlap"] = true,
			["GetScriptObject"] = true,
			["GetScriptDB"] = true,
			["GetScriptType"] = true,
			["GetDecodedScriptType"] = true,
			["ImportScriptsFromLibrary"] = true,
			["ImportScriptString"] = true,
			["AddScript"] = true,
			["BuildScriptObjectFromIndexTable"] = true,
			["DecodeImportedString"] = true,
			["PrepareTableToExport"] = true,
			["ScriptReceivedFromGroup"] = true,
			["ExportScriptToGroup"] = true,
			["ShowImportScriptConfirmation"] = true,
			["DispatchTalentUpdateHookEvent "] = true,
			["ScheduleHookForCombat"] = true,
			["ScheduleRunFunctionForEvent"] = true,
			["RunFunctionForEvent"] = true,
			["EventHandler"] = true,
			["RegisterRefreshDBCallback"] = true,
			["FireRefreshDBCallback"] = true,
			["RefreshDBUpvalues"] = false,
			["RefreshDBLists"] = false,
			["UpdateAuraCache"] = false,
			["ApplyPatches"] = true,
			["RefreshConfig"] = true,
			["RefreshConfigProfileChanged"] = true,
			["SaveConsoleVariables"] = true,
			["GetSettings"] = true,
			["CodeTypeNames"] = true,
			["HookScripts"] = true,
			["HookScriptsDesc"] = true,
			["IncreaseHookBuildID"] = true,
			["IncreaseRefreshID"] = true,
			["IncreaseRefreshID_Auras"] = true,
			["SpecList"] = true,
			["UpdateSettingsCache"] = true,
			["ActorTypeSettingsCache"] = true,
			["RunScheduledUpdate"] = true,
			["ScheduleUpdateForNameplate"] = true,
			["EventHandlerFrame"] = true,
			["OnInit"] = true,
			["HookLoadCallback"] = true,
			["CheckFirstRun"] = true,
			["CommHandler"] = true,
			["CommReceived"] = true,
			["GetAllShownPlates"] = false,
			["GetHashKey"] = false,
			["IsShowingResourcesOnTarget"] = false,
			["OnRetailNamePlateShow"] = true,
			["UpdateSelfPlate"] = true,
			["CastBarOnShow_Hook"] = true,
			["CastBarOnEvent_Hook"] = true,
			["CastBarOnTick_Hook"] = true,
			["RefreshAuras"] = true,
			["CreateAuraIcon"] = true,
			["RefreshColorOverride"] = true,
			["ChangeHealthBarColor_Internal"] = true,
			["UpdateAllPlates"] = true,
			["FullRefreshAllPlates"] = true,
			["UpdatePlateClickSpace"] = true,
			["SetNamePlatePreferredClickInsets"] = true,
			["EveryFrameFPSCheck"] = true,
			["NameplateTick"] = true,
			["OnPlayerTargetChanged"] = true,
			["UpdateTarget"] = true,
			["UpdateSoftInteractTarget"] = true,
			["UpdatePlateText"] = true,
			["CheckLifePercentText"] = true,
			["UpdateAllNames"] = true,
			--["UpdateLevelTextAndColor"] = true,
			["UpdatePlateFrame"] = true,
			["ForceChangeBorderColor"] = true,
			["UpdatePlateBorders"] = true,
			["UpdateRaidMarkersOnAllNameplates"] = true,
			["RefreshAutoToggle"] = true,
			["HasRefreshAutoToggleScheduled"] = true,
			["ParseHealthSettingForPlayer"] = true,
			["CreateAlphaAnimation"] = true,
			["CreateHighlightNameplate"] = true,
			["CreateHealthFlashFrame"] = true,
			["CreateAggroFlashFrame"] = true,
			["CreateScaleAnimation"] = true,
			["DoNameplateAnimation"] = true,
			["RefreshIsEditingAnimations"] = true,
			["IsNpcInIgnoreList"] = true,
			["CanChangePlateSize"] = true,
			["RefreshOmniCCGroup"] = true,
			["CreatePlaterButtonAtInterfaceOptions"] = true,
			["SetCVarsOnFirstRun"] = true,
			["GetActorSubName"] = false,
			["QuestLogUpdated"] = true,
			["GetNpcIDFromGUID"] = false,
			["GetNpcID"] = false,
			["ForceTickOnAllNameplates"] = true,
			["UpdateUIParentScale"] = true,
			["SetNameplateScale"] = false,
			["UpdateUIParentLevels"] = true,
			["UpdateUIParentTargetLevels"] = true,
			["RefreshTankCache"] = true,
			["ToggleThreatColorMode"] = false,
			["ForceFindPetOwner"] = true,
			["UpdateBgPlayerRoleCache"] = false,
			["GetSpecIconForUnitFromBG"] = false,
			["GetUnitBGInfo"] = false,
			["GetSpecIcon"] = false,
			["InitLDB"] = true,
			["APIList"] = true,
			["FrameworkList"] = true,
			["UnitFrameMembers"] = true,
			["NameplateComponents"] = true,
			["UpdateOptionsTabUpdateState"] = true,
			["EnableProfiling"] = false,
			["DisableProfiling"] = false,
			["StartLogPerformance"] = false,
			["EndLogPerformance"] = false,
			["StartLogPerformanceCore"] = false,
			["EndLogPerformanceCore"] = false,
			["DumpPerformance"] = true,
			["ShowPerfData"] = true,
			["StoreEventLogData"] = true,
			["CheckOptionsTab"] = true,
			["OpenOptionsPanel"] = true,
			["TriggerDefaultMembers"] = true,
			["OpenCopyUrlDialog"] = true,
			["CreateOptionTableForScriptObject"] = true,
			["HasWagoUpdate"] = true,
			["GetWagoUpdateDataFromCompanion"] = true,
			["CheckWagoUpdates"] = true,
			["AddCompanionData"] = true,
			["CompanionDataSlugs"] = true,
			["GetVersionInfo"] = false,
			["versionString"] = false,
			["fullVersionInfo"] = false,
			["DispatchCommReceivedMessageHookEvent"] = true,
			["DispatchCommSendMessageHookEvents"] = true,
			["VerifyScriptIdForComm"] = true,
			["MessageReceivedFromScript"] = true,
			["CreateUniqueIdentifier"] = false,
			["GetScriptFromUID"] = true,
			["SendCommMessage"] = true,
			["CreateCommHeader"] = true,
			["SendComm"] = false,
			["FPSData"] = {
				["startTime"] = true,
				["frames"] = true,
				["platesUpdatedThisFrame"] = true,
				["platesToUpdatePerFrame"] = true,
				["curFPS"] = false,
			},
			["UnitReaction"] = {
				["UNITREACTION_HOSTILE"] = false,
				["UNITREACTION_NEUTRAL"] = false,
				["UNITREACTION_FRIENDLY"] = false,
			},
			["Export_NpcColors"] = true,
			["Export_CastColors"] = true,
			["ScriptAura"] = true,
			["ScriptCastBar"] = true,
			["ScriptUnit"] = true,
			["RemoveFromAuraUpdate"] = true,
			["AddToAuraUpdate"] = true,
			["AnchorSides"] = false,
			["SetAnchor"] = false,
			["RunScriptTriggersForAuraIcons"] = true,
			["AddAura"] = true,
			["GetAuraIcon"] = true,
			["HideNonUsedAuraIcons"] = true,
			["AddExtraIcon"] = true,
			["ResetAuraContainer"] = true,
			["TrackSpecificAuras"] = true,
			["UpdateAuras_Manual"] = true,
			["UpdateAuras_Automatic"] = true,
			["UpdateAuras_Self_Automatic"] = true,
			["GetUnitAuras"] = false,
			["GetUnitAurasForUnitID"] = false,
			["PerformanceUnits"] = true,
			["ForceBlizzardNameplateUnits"] = true,
			["COMM_PLATER_PREFIX"] = true,
			["COMM_SCRIPT_GROUP_EXPORTED"] = true,
			["COMM_SCRIPT_MSG"] = true,
			["Resources"] = {
				["GetResourceWidgetCreationTable"] = true,
				["GetCreateResourceWidgetFunctionForSpecId"] = true,
				["RefreshResourcesDBUpvalues"] = true,
				["CreateMainResourceFrame"] = true,
				["UpdateResourceFrameToUse"] = true,
				["GetMainResourceFrame"] = true,
				["GetResourceBarInUse"] = true,
				["EnableEvents"] = true,
				["DisableEvents"] = true,
				["HidePlaterResourceFrame"] = true,
				["OnSpecChanged"] = true,
				["CanUsePlaterResourceFrame"] = true,
				["UpdateResourceFramePosition"] = true,
				["UpdateMainResourceFrame"] = true,
				["UpdateResourceBar"] = true,
				["UpdateResourcesFor_HideDeplete"] = true,
				["UpdateResourcesFor_ShowDepleted"] = true,
				["UpdateResources_NoDepleted"] = true,
				["UpdateResources_WithDepleted"] = true,
				["GetRuneKeyBySpec"] = true,
				["GetCDEdgeBySpec"] = true,
			},
			["UpdateBaseNameplateOptions"] = true,
			["BossModsTimeBarDBM"] = true,
			["BossModsTimeBarBW"] = true,
			["BigWigs_BarCreated"] = true,
			["UpdateBossModAuras"] = true,
			["EnsureUpdateBossModAuras"] = true,
			["CreateBossModAuraFrame"] = true,
			["UpdateBossModAuraFrameSettings"] = true,
			["RegisterBossModAuras"] = true,
			["GetBossModsEventTimeLeft"] = false,
			["GetAltCastBarAltId"] = false,
			["ClearAltCastBar"] = false,
			["SetAltCastBar"] = false,
			["StopAltCastBar"] = false,
			["GetBossTimer"] = false,
			["RegisterBossModsBars"] = false,
			["TranslateNPCCache"] = true,
		},
		
		["DetailsFramework"] = {
			["SetEnvironment"] = true,
		},

		["WeakAuras"] = {
			["Add"] = true,
			["AddMany"] = true,
			["Delete"] = true,
			["NewAura"] = true,
		},
		
		["C_GuildInfo"] = {
			["RemoveFromGuild"] = true,
		},
		
		
		--block mail, trades, action house, banks
		["C_AuctionHouse"] 	= true,
		["C_Bank"] = true,
		["C_GuildBank"] = true,
		["SetSendMailMoney"] = true,
		["SendMail"]		= true,
		["SetTradeMoney"]	= true,
		["AddTradeMoney"]	= true,
		["PickupTradeMoney"]	= true,
		["PickupPlayerMoney"]	= true,
		["AcceptTrade"]		= true,

		--frames
		["BankFrame"] 		= true,
		["TradeFrame"]		= true,
		["GuildBankFrame"] 	= true,
		["MailFrame"]		= true,
		["EnumerateFrames"] = true,

		--block run code inside code
		["RunScript"] = true,
		["securecall"] = true,
		["getfenv"] = true,
		["setfenv"] = true,
		["loadstring"] = true,
		["pcall"] = true,
		["xpcall"] = true,
		["getglobal"] = true,
		["setmetatable"] = true,
		["DevTools_DumpCommand"] = true,

		--avoid creating/running macros
		["SetBindingMacro"] = true,
		["CreateMacro"] = true,
		["EditMacro"] = true,
		["hash_SlashCmdList"] = true,
		["SlashCmdList"] = true,
		["MacroEditBox"] = true,
		["ChatEdit_SendText"] = true,
		["AreDangerousScriptsAllowed"] = true,

		--block guild commands
		["GuildDisband"] = true,
		["GuildUninvite"] = true,

		--other things
		["C_GMTicketInfo"] = true,

		--deny messing addons with script support
		["PlaterDB"] = true,
		["PlaterDBChr"] = true,
		["_detalhes_global"] = true,
		["WeakAurasSaved"] = true,
	}
	
	local overrideFunctions = {
		["CreateFrame"] = function(frameType, name, parent, template, id)
			if template then
				template = string.gsub(template, "SecureActionButtonTemplate", "")
				template = string.gsub(template, "SecureHandlerClickTemplate", "")
			end
			return CreateFrame(frameType, name, parent, template, id)
		end,
	}
	
	--this allows full shadowing on 'Plater' global with the filter above
	local function buildShadowTable(privateFunctionsTable, tableKey, shadowTable)
		if not privateFunctionsTable then return end
		shadowTable = shadowTable or {}
		
		--ViragDevTool_AddData({privateFunctionsTable, tableKey, shadowTable}, "buildShadowTable")
		local shadowValuesTable = {}
		if tableKey then
			shadowTable[tableKey] = {}
			shadowTable = shadowTable[tableKey]
		end
		--ViragDevTool_AddData({tableKey, shadowValuesTable}, "buildShadowTable_tables")
		for key, value in pairs(privateFunctionsTable) do
			--ViragDevTool_AddData({key, value}, "buildShadowTable_ITER")
			if type(value) == "table" then
				buildShadowTable(value, key, shadowTable)
			else
				--ViragDevTool_AddData({key, value}, "buildShadowTable_ADD")
				shadowValuesTable [key] = value
			end
		end
		
		--ViragDevTool_AddData({shadowValuesTable, tableKey}, "buildShadowTable_SET")
		setmetatable(shadowTable, {
			__index = function (env, key)
				--ViragDevTool_AddData({env, key, tableKey, tableKey and _G[tableKey] or _G}, "GET")
				if key == "_G" then
					return env
				elseif overrideFunctions [key] then
					return overrideFunctions [key]
				elseif shadowValuesTable [key] then -- if true, don't return value
					return nil
				else
					return rawget(tableKey and _G[tableKey] or _G, key)
				end
			end,
			
			__newindex = function (t, k, v)
				--ViragDevTool_AddData({t, k, v, tableKey, tableKey and _G[tableKey] or _G}, "SET")
				if shadowValuesTable [k] ~= nil then -- if in the list: don't overwrite
					error ("'" .. tableKey .. "." .. k .. "' is protected and may not be overwritten.")
				else
					rawset(tableKey and _G[tableKey] or _G, k, v)
				end
			end,
		})
		
		--ViragDevTool_AddData({shadowTable}, "buildShadowTable_return")
		return shadowTable
	end
	
	local ShadowTable = nil
	local getShadowTable = function()
		if not ShadowTable then
			ShadowTable = buildShadowTable(privateFunctions)
		end
		return ShadowTable
	end
	
	local platerModEnvironment = {} -- needed for DF:SetEnvironment to have a common mod/script environment in Plater
	local platerModEnvironment2 = getShadowTable()
	local function SetPlaterEnvironment(func)
		setfenv(func, platerModEnvironment2)
	end

	function Plater.WipeAndRecompileAllScripts (scriptType, noHotReload)
		if (scriptType == "script") then
			Plater.StartLogPerformanceCore("Plater-Core", "Mod/Script", "WipeAndRecompileAllScripts - script")
			
			table.wipe(SCRIPT_AURA_TRIGGER_CACHE)
			table.wipe(SCRIPT_CASTBAR_TRIGGER_CACHE)
			table.wipe(SCRIPT_UNIT_TRIGGER_CACHE)
			Plater.CompileAllScripts (scriptType, noHotReload)
			
			Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "WipeAndRecompileAllScripts - script")
			
		elseif (scriptType == "hook") then
			Plater.StartLogPerformanceCore("Plater-Core", "Mod/Script", "WipeAndRecompileAllScripts - hook")
			
			Plater.WipeHookContainers (noHotReload)
			Plater.CompileAllScripts (scriptType, noHotReload)
			
			Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "WipeAndRecompileAllScripts - hook")
		end
	end

	Plater.AllHookGlobalContainers = {
		HOOK_NAMEPLATE_CREATED,
		HOOK_NAMEPLATE_ADDED,
		HOOK_NAMEPLATE_REMOVED,
		HOOK_NAMEPLATE_UPDATED,
		HOOK_TARGET_CHANGED,
		HOOK_CAST_START,
		HOOK_CAST_UPDATE,
		HOOK_CAST_STOP,
		HOOK_RAID_TARGET,
		HOOK_COMBAT_ENTER,
		HOOK_COMBAT_LEAVE,
		HOOK_NAMEPLATE_CONSTRUCTOR,
		HOOK_PLAYER_POWER_UPDATE,
		HOOK_PLAYER_TALENT_UPDATE,
		HOOK_HEALTH_UPDATE,
		HOOK_ZONE_CHANGED,
		HOOK_UNITNAME_UPDATE,
		HOOK_LOAD_SCREEN,
		HOOK_PLAYER_LOGON,
		HOOK_MOD_INITIALIZATION,
		HOOK_COMM_RECEIVED_MESSAGE,
		HOOK_COMM_SEND_MESSAGE,
		HOOK_OPTION_CHANGED,
		HOOK_MOD_OPTION_CHANGED,
		HOOK_NAMEPLATE_DESTRUCTOR,
	}

	function Plater.WipeHookContainers (noHotReload)
		Plater.IncreaseHookBuildID()
		
		for _, container in ipairs (Plater.AllHookGlobalContainers) do
			if (not noHotReload) then
				for _, globalScriptObject in ipairs (container) do
					globalScriptObject.NeedHotReload = true
				end
			end
			table.wipe (container)
			container.ScriptAmount = 0
		end
	end

	function Plater.GetContainerForHook (hookName)
		if (hookName == "Initialization") then
			return HOOK_MOD_INITIALIZATION
		elseif (hookName == "Deinitialization") then
			return HOOK_MOD_DEINITIALIZATION
		elseif (hookName == "Constructor") then
			return HOOK_NAMEPLATE_CONSTRUCTOR	
		elseif (hookName == "Nameplate Created") then
			return HOOK_NAMEPLATE_CREATED
		elseif (hookName == "Nameplate Added") then
			return HOOK_NAMEPLATE_ADDED
		elseif (hookName == "Nameplate Removed") then
			return HOOK_NAMEPLATE_REMOVED
		elseif (hookName == "Nameplate Updated") then
			return HOOK_NAMEPLATE_UPDATED	
		elseif (hookName == "Target Changed") then
			return HOOK_TARGET_CHANGED
		elseif (hookName == "Cast Start") then
			return HOOK_CAST_START
		elseif (hookName == "Cast Update") then
			return HOOK_CAST_UPDATE
		elseif (hookName == "Cast Stop") then
			return HOOK_CAST_STOP
		elseif (hookName == "Raid Target") then
			return HOOK_RAID_TARGET
		elseif (hookName == "Enter Combat") then
			return HOOK_COMBAT_ENTER
		elseif (hookName == "Leave Combat") then
			return HOOK_COMBAT_LEAVE
		elseif (hookName == "Player Power Update") then
			return HOOK_PLAYER_POWER_UPDATE	
		elseif (hookName == "Player Talent Update") then
			return HOOK_PLAYER_TALENT_UPDATE
		elseif (hookName == "Health Update") then
			return HOOK_HEALTH_UPDATE
		elseif (hookName == "Zone Changed") then
			return HOOK_ZONE_CHANGED	
		elseif (hookName == "Name Updated") then	
			return HOOK_UNITNAME_UPDATE
		elseif (hookName == "Load Screen") then
			return HOOK_LOAD_SCREEN	
		elseif (hookName == "Player Logon") then
			return HOOK_PLAYER_LOGON
		elseif (hookName == "Receive Comm Message") then
			return HOOK_COMM_RECEIVED_MESSAGE
		elseif (hookName == "Send Comm Message") then
			return HOOK_COMM_SEND_MESSAGE
		elseif (hookName == "Option Changed") then
			return HOOK_OPTION_CHANGED
		elseif (hookName == "Mod Option Changed") then
			return HOOK_MOD_OPTION_CHANGED
		elseif (hookName == "Destructor") then
			return HOOK_NAMEPLATE_DESTRUCTOR
		else
			Plater:Msg ("Unknown hook: " .. (hookName or "Invalid Hook Name"))
		end
	end

	--store the names of hooks that passed the filters
	Plater.CurrentlyLoadedHooks = {}

	function Plater.RunDestructorForHook (scriptObject)
		--check if the script has a destructor script
		if (scriptObject.Hooks ["Destructor"]) then
			--load and compile the destructor code
			local code = "return " .. scriptObject.Hooks ["Destructor"]
			
			if IS_WOW_PROJECT_NOT_MAINLINE then
				code = string.gsub(code, "\"NamePlateFullBorderTemplate\"", "\"PlaterNamePlateFullBorderTemplate\"")
			end
			
			local compiledScript, errortext = loadstring (code, "Destructor for " .. scriptObject.Name)
			if (not compiledScript) then
				Plater:Msg ("failed to compile destructor for script " .. scriptObject.Name .. ": " .. errortext)
			else
				--store the function to execute
				--setfenv (compiledScript, functionFilter)
				if (Plater.db.profile.shadowMode and Plater.db.profile.shadowMode == 0) then -- legacy mode
					DF:SetEnvironment(compiledScript, nil, platerModEnvironment)
				elseif (not Plater.db.profile.shadowMode or Plater.db.profile.shadowMode == 1) then
					SetPlaterEnvironment(compiledScript)
				end
				
				local func = compiledScript()
				
				--iterate among all nameplates
				for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
					if (plateFrame) then
						
						--local globalScriptObject = HOOK_NAMEPLATE_DESTRUCTOR [scriptObject.scriptId]
						--does not exist when mod is not loaded through load conditions or similar
						local globalScriptObject = HOOK_NAMEPLATE_DESTRUCTOR [scriptObject.scriptId] or {
							HotReload = -1,
							DBScriptObject = scriptObject,
							Build = PLATER_HOOK_BUILD,
						}
						local unitFrame = plateFrame.unitFrame
						local scriptContainer = unitFrame:ScriptGetContainer()
						local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Destructor")

						local okay, errortext = xpcall (func, GetErrorHandler("Mod: |cFFAAAA22" .. scriptInfo.GlobalScriptObject.DBScriptObject.Name .. "|r " .. scriptObject.Name .. " error: ", scriptObject), unitFrame, unitFrame.displayedUnit, unitFrame, scriptInfo.Env, PLATER_GLOBAL_MOD_ENV [scriptInfo.GlobalScriptObject.DBScriptObject.scriptId])
						if (not okay) then
							--handled via error handler
							--Plater:Msg ("Mod: |cFFAAAA22" .. scriptInfo.GlobalScriptObject.DBScriptObject.Name .. "|r " .. scriptObject.Name .. " error: " .. errortext)
						end
					end
				end
			end		
		end
	end
	
	function Plater.RunDeinitializationForHook (scriptObject)
		--check if the script has a destructor script
		if (scriptObject.Hooks ["Deinitialization"]) then
			--load and compile the destructor code
			local code = "return " .. scriptObject.Hooks ["Deinitialization"]
			
			if IS_WOW_PROJECT_NOT_MAINLINE then
				code = string.gsub(code, "\"NamePlateFullBorderTemplate\"", "\"PlaterNamePlateFullBorderTemplate\"")
			end
			
			local compiledScript, errortext = loadstring (code, "Deinitialization for " .. scriptObject.Name)
			if (not compiledScript) then
				Plater:Msg ("failed to compile Deinitialization for script " .. scriptObject.Name .. ": " .. errortext)
			else
				--store the function to execute
				--setfenv (compiledScript, functionFilter)
				if (Plater.db.profile.shadowMode and Plater.db.profile.shadowMode == 0) then -- legacy mode
					DF:SetEnvironment(compiledScript, nil, platerModEnvironment)
				elseif (not Plater.db.profile.shadowMode or Plater.db.profile.shadowMode == 1) then
					SetPlaterEnvironment(compiledScript)
				end
				
				--does not exist when mod is not loaded through load conditions or similar
				local globalScriptObject = HOOK_NAMEPLATE_DESTRUCTOR [scriptObject.scriptId] or {
					HotReload = -1,
					DBScriptObject = scriptObject,
					Build = PLATER_HOOK_BUILD,
				}
				
				globalScriptObject ["Deinitialization"] = compiledScript()
				
				Plater.ScriptMetaFunctions.ScriptRunNoAttach (globalScriptObject, "Deinitialization")

			end		
		end
	end
	
	-- which option types should be copied to modTable.config?
	local options_for_config_table = {
		[1] = true, -- Color
		[2] = true, -- Number
		[3] = true, -- Text
		[4] = true, -- Toggle
		[5] = false, -- Label
		[6] = false, -- Blank Line
		[7] = true, -- List
		[8] = true, -- Audio
	}
	
	--compile scripts from the Hooking tab
	function Plater.CompileHook (scriptObject, noHotReload)
		Plater.StartLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
		
		--check if the script is valid and if is enabled
		if (not scriptObject) then
			Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
			return
		end
		
		if not scriptObject.scriptId then
			scriptObject.scriptId = tostring(scriptObject)
		end
		
		--check if this hook is currently loaded
		if (not scriptObject.Enabled) then
			if (Plater.CurrentlyLoadedHooks [scriptObject.scriptId]) then
				Plater.CurrentlyLoadedHooks [scriptObject.scriptId] = false
				Plater.RunDestructorForHook (scriptObject)
				Plater.RunDeinitializationForHook (scriptObject)
			end
			--clear env when disabling/disabled
			PLATER_GLOBAL_MOD_ENV [scriptObject.scriptId] = nil
			Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
			return
		end
		
		do --check integrity
			if (not scriptObject.Name) then
				Plater:Msg ("fail to load mod: " .. (scriptObject.Name or "") .. ".")
				Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
				return
			end

			if (not scriptObject.LoadConditions) then
				Plater:Msg ("fail to load mod: " .. (scriptObject.Name or "") .. ".")
				Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
				return
			end
			
			if (
				not scriptObject.LoadConditions.class or
				not scriptObject.LoadConditions.spec or
				not scriptObject.LoadConditions.race or
				not scriptObject.LoadConditions.talent or
				not scriptObject.LoadConditions.pvptalent or
				not scriptObject.LoadConditions.group or
				not scriptObject.LoadConditions.role or
				not scriptObject.LoadConditions.affix or
				not scriptObject.LoadConditions.encounter_ids or
				not scriptObject.LoadConditions.map_ids
			) then
				Plater:Msg ("fail to load mod: " .. (scriptObject.Name or "") .. ".")
				Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
				return
			end

			if (not scriptObject.Hooks) then
				Plater:Msg ("fail to load mod: " .. (scriptObject.Name or "") .. ".")
				Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
				return
			end
		end
		
		--check if can load this hook
		if (not DF:PassLoadFilters (scriptObject.LoadConditions, Plater.EncounterID)) then
			--check if this hook is currently loaded
			if (Plater.CurrentlyLoadedHooks [scriptObject.scriptId]) then
				Plater.CurrentlyLoadedHooks [scriptObject.scriptId] = false
				Plater.RunDestructorForHook (scriptObject)
				Plater.RunDeinitializationForHook (scriptObject)
			end
			if not noHotReload then
				--clear env if needed
				PLATER_GLOBAL_MOD_ENV [scriptObject.scriptId] = nil
			end
			Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
			return
		else
			Plater.CurrentlyLoadedHooks [scriptObject.scriptId] = true
		end
		
		if not noHotReload then
			--clear env if needed
			PLATER_GLOBAL_MOD_ENV [scriptObject.scriptId] = nil
		end
		
		--store the scripts to be compiled
		local scriptCode = {}
		
		--get scripts from the object
		for hookName, code in pairs (scriptObject.Hooks) do
			scriptCode [hookName] = "return " .. code
		end
		
		--get or create the global script object
		local globalScriptObject = {
			HotReload = -1,
			DBScriptObject = scriptObject,
			Build = PLATER_HOOK_BUILD,
			scriptId = scriptObject.scriptId,
		}
		
		--init modEnv if necessary
		local needsInitCall = false
		if (not PLATER_GLOBAL_MOD_ENV [scriptObject.scriptId]) then
			needsInitCall = true
			PLATER_GLOBAL_MOD_ENV [scriptObject.scriptId] = {
				config = {}
			}
		end

		--copy options to global env
		-- ensure options are valid:
		Plater.CreateOptionTableForScriptObject(scriptObject)
		local scriptOptions = scriptObject.Options
		local scriptOptionsValues = scriptObject.OptionsValues

		for i = 1, #scriptOptions do
			local thisOption = scriptOptions[i]
			if (options_for_config_table[thisOption.Type]) then
				if (type(scriptOptionsValues[thisOption.Key]) == "boolean") then
					PLATER_GLOBAL_MOD_ENV [scriptObject.scriptId].config[thisOption.Key] = scriptOptionsValues[thisOption.Key]

				elseif (thisOption.Type == 7) then --list type
					--check if the options is a list
					
					--build default values if needed
					if not scriptOptionsValues[thisOption.Key] then
						scriptOptionsValues[thisOption.Key] = DF.table.copy({}, thisOption.Value)
					end
					
					--build a hash table with the entries in the list
					local hashTable = {}
					for index, entryTable in ipairs(scriptOptionsValues[thisOption.Key]) do
						local key = entryTable[1]
						local value = entryTable[2]
						hashTable[key] = value
					end

					PLATER_GLOBAL_MOD_ENV [scriptObject.scriptId].config[thisOption.Key] = hashTable
				else
					PLATER_GLOBAL_MOD_ENV [scriptObject.scriptId].config[thisOption.Key] = scriptOptionsValues[thisOption.Key] or thisOption.Value
				end
			end
		end
		
		--compile
		for hookName, code in pairs (scriptCode) do
			
			local globalScriptContainer = Plater.GetContainerForHook (hookName)
			
			if (type (code) ~= "string") then
				Plater:Msg ("fail to load mod: " .. (scriptObject.Name or "") .. ".")
				Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
				return
			end
			
			if IS_WOW_PROJECT_NOT_MAINLINE then
				code = string.gsub(code, "\"NamePlateFullBorderTemplate\"", "\"PlaterNamePlateFullBorderTemplate\"")
			end

			--find occurences of Plater.SendComm(arg1, arg2, arg3, ...) and replace with Plater.SendComm_Internal(uniqueIdentifier, arg1, arg2, arg3, ...) or fail to compile for all than "Send Comm Message" and don't replace (use empty dummy)
			if hookName == "Send Comm Message" then
				code = string.gsub(code, "Plater.SendComm%s*%(", "Plater.SendComm(" .. globalScriptContainer.ScriptAmount + 1 .. ", \"" .. scriptObject.scriptId .. "\", \"" .. scriptObject.UID .. "\", ")
			else
				local foundSendComm = string.find(code, "Plater.SendComm")
				if foundSendComm then
					Plater:Msg ("failed to compile " .. hookName .. " for script " .. scriptObject.Name .. ": " .. "Usage of 'Plater.SendComm' is only allowed in 'Send Comm Message' hook.")
				end
			end
			
			local compiledScript, errortext = loadstring (code, "" .. hookName .. " for " .. scriptObject.Name)
			if (not compiledScript) then
				Plater:Msg ("failed to compile " .. hookName .. " for script " .. scriptObject.Name .. ": " .. errortext)
			else
				--setfenv (compiledScript, functionFilter)
				if (Plater.db.profile.shadowMode and Plater.db.profile.shadowMode == 0) then -- legacy mode
					DF:SetEnvironment(compiledScript, nil, platerModEnvironment)
				elseif (not Plater.db.profile.shadowMode or Plater.db.profile.shadowMode == 1) then
					SetPlaterEnvironment(compiledScript)
				end
				
				--store the function to execute inside the global script object
				globalScriptObject [hookName] = compiledScript()
				
				--insert the script in the global script container, remove existing, as option changes re-compile only single mod without wipe
				local isReplace = false
				for i, curScriptObject in ipairs(globalScriptContainer) do
					if scriptObject.scriptId == curScriptObject.scriptId then
						tremove(globalScriptContainer, i)
						isReplace = true
					end
				end
				tinsert (globalScriptContainer, globalScriptObject)
				globalScriptContainer.ScriptAmount = globalScriptContainer.ScriptAmount + (isReplace and 0 or 1)
				
				if (hookName == "Constructor") then
					globalScriptObject.HasConstructor = true
				elseif (hookName == "Initialization") and needsInitCall then
					Plater.ScriptMetaFunctions.ScriptRunNoAttach (globalScriptObject, "Initialization")
				end
			end
		end
		
		Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
	end

	--compile scripts from the Scripting tab
	function Plater.CompileScript(scriptObject, noHotReload, ...)
		Plater.StartLogPerformanceCore("Plater-Core", "Mod/Script", "CompileScript")
		
		--check if the script is valid and if is enabled
		if (not scriptObject) then
			Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileScript")
			return
		elseif (not scriptObject.Enabled) then
			Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileScript")
			return
		end
		
		if (not scriptObject.scriptId) then
			scriptObject.scriptId = tostring(scriptObject)
		end

		--clear env on re-compilation if necessary
		if (not noHotReload) then
			PLATER_GLOBAL_SCRIPT_ENV[scriptObject.scriptId] = nil
		end
		
		--store the scripts to be compiled
		local scriptCode, scriptFunctions = {}, {}
		
		--get scripts passed
		for i = 1, select ("#",...) do
			scriptCode[Plater.CodeTypeNames [i]] = "return " .. select(i, ...)
		end
		
		--get scripts which wasn't passed
		for i = 1, #Plater.CodeTypeNames do
			local scriptType = Plater.CodeTypeNames[i]
			-- ensure init is filled always, 5 is the index where the init code is
			if (not scriptObject[scriptType] and i == 5) then
				scriptObject[scriptType] = [=[
					function (scriptTable)
						--insert code here
						
					end
				]=]	
			end
			if (not scriptCode[scriptType]) then
				scriptCode[scriptType] = "return " .. scriptObject[scriptType]
			end
		end
		
		--init modEnv if necessary
		local needsInitCall = false
		if (not PLATER_GLOBAL_SCRIPT_ENV[scriptObject.scriptId]) then
			needsInitCall = true
			PLATER_GLOBAL_SCRIPT_ENV[scriptObject.scriptId] = {
				config = {}
			}
		end

		--copy options to global env and ensure options are valid
		Plater.CreateOptionTableForScriptObject(scriptObject)
		local scriptOptions = scriptObject.Options
		local scriptOptionsValues = scriptObject.OptionsValues

		for i = 1, #scriptOptions do
			local thisOption = scriptOptions[i]
			if (options_for_config_table[thisOption.Type]) then
				if (type(scriptOptionsValues[thisOption.Key]) == "boolean") then
					PLATER_GLOBAL_SCRIPT_ENV [scriptObject.scriptId].config[thisOption.Key] = scriptOptionsValues[thisOption.Key]

				elseif (thisOption.Type == 7) then --check if the options is a list
					--build default values if needed
					if not scriptOptionsValues[thisOption.Key] then
						scriptOptionsValues[thisOption.Key] = DF.table.copy({}, thisOption.Value)
					end
					
					--build a hash table with the entries in the list
					local hashTable = {}
					for index, entryTable in ipairs(scriptOptionsValues[thisOption.Key]) do
						local key = entryTable[1]
						local value = entryTable[2]
						hashTable[key] = value
					end

					PLATER_GLOBAL_SCRIPT_ENV [scriptObject.scriptId].config[thisOption.Key] = hashTable
				else
					PLATER_GLOBAL_SCRIPT_ENV [scriptObject.scriptId].config[thisOption.Key] = scriptOptionsValues[thisOption.Key] or thisOption.Value
				end
			end
		end

		--compile
		for scriptType, code in pairs(scriptCode) do
			if (IS_WOW_PROJECT_NOT_MAINLINE) then
				code = string.gsub(code, "\"NamePlateFullBorderTemplate\"", "\"PlaterNamePlateFullBorderTemplate\"")
			end
			
			local compiledScript, errortext = loadstring(code, "" .. scriptType .. " for " .. scriptObject.Name)
			if (not compiledScript) then
				Plater:Msg ("failed to compile " .. scriptType .. " for script " .. scriptObject.Name .. ": " .. errortext)
			else
				--get the function to execute
				--setfenv(compiledScript, functionFilter) deprecated
				if (Plater.db.profile.shadowMode and Plater.db.profile.shadowMode == 0) then --legacy mode
					DF:SetEnvironment(compiledScript, nil, platerModEnvironment)

				elseif (not Plater.db.profile.shadowMode or Plater.db.profile.shadowMode == 1) then
					SetPlaterEnvironment(compiledScript)
				end

				--extract the function
				scriptFunctions[scriptType] = compiledScript()
			end
		end
		
		--trigger container is the table with spellIds for auras and/or spellcast
		--triggerId is the spellId converted to spellName or the unitName in case of a Unit name
		local triggerContainer, triggerId
		if (scriptObject.ScriptType == 1 or scriptObject.ScriptType == 2) then --aura or castbar
			triggerContainer = "SpellIds"

		elseif (scriptObject.ScriptType == 3) then --unit name
			triggerContainer = "NpcNames"
		end
		
		for i = 1, #scriptObject[triggerContainer] do
			local triggerId = scriptObject[triggerContainer][i]
			
			--if the trigger is using spellId, check if the spell exists
			if (scriptObject.ScriptType == 1 or scriptObject.ScriptType == 2) then
				if (type(triggerId) == "number") then
					triggerId = GetSpellInfo(triggerId)
					if (not triggerId) then
						if IS_WOW_PROJECT_MAINLINE then -- disable this in classic for now... too spammy
							--just ignore, the spell on trigger list will be blank with a remove trigger button
							--Plater:Msg ("failed to get the spell name for spellId: " .. (scriptObject [triggerContainer] [i] or "invalid spellId") .. " for script '" .. scriptObject.Name .. "', the spell has been removed from the triggers.")
						end
					end
				end
			
			elseif (scriptObject.ScriptType == 3) then --unit names
				--cast the string to number to see if it's a npcId
				triggerId = tonumber(triggerId) or triggerId
				
				--if is a unit name, make it be in lower case
				if (type(triggerId) == "string") then
					triggerId = triggerId:lower()
				end
			end

			if (triggerId) then
				--get the global script object table
				local triggerCacheTable
				
				if (scriptObject.ScriptType == 1) then
					triggerCacheTable = SCRIPT_AURA_TRIGGER_CACHE
				elseif (scriptObject.ScriptType == 2) then
					triggerCacheTable = SCRIPT_CASTBAR_TRIGGER_CACHE
				elseif (scriptObject.ScriptType == 3) then
					triggerCacheTable = SCRIPT_UNIT_TRIGGER_CACHE
				end
				
				local globalScriptObject = triggerCacheTable[triggerId]
				
				if (not globalScriptObject) then
					--first time compiled, create the global script object
					--this table controls the hot reload state, holds the original object from the database and has the compiled functions
					globalScriptObject = {
						DBScriptObject = scriptObject,
						--whenever the script is applied or saved the hot reload increases making it run again when the script is triggered
						HotReload = 1,
						--script key is set in the widget so it can lookup for a script using the key when the widget is hidding
						ScriptKey = triggerId,
					}

					--insert the table just created inthe the triggerCacheTable
					triggerCacheTable[triggerId] = globalScriptObject
					
				else --hot reload and update
					globalScriptObject.HotReload = globalScriptObject.HotReload + 1
					globalScriptObject.DBScriptObject = scriptObject
				end

				globalScriptObject.LastUpdateTime = GetTime()-0.05
				
				--add the script functions to the global object table
				for scriptType, func in pairs(scriptFunctions) do
					globalScriptObject[scriptType] = func
				end
				
				--run initialization (once)
				if (needsInitCall) then
					Plater.ScriptMetaFunctions.ScriptRunInitialization(globalScriptObject)
					needsInitCall = false
				end
			end
		end
		
		Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileScript")
	end

	--check all triggers of all scripts for overlaps
	--where a same spellId, npcName or npcId is being used by two or more scripts
	--return a table with the triggerId with a index table of all scripts using that trigger
	function Plater.CheckScriptTriggerOverlap()
		--store all triggers of all scripts in the format [triggerId] = {scripts using this trigger}
		local allTriggers = {
			Auras = {},
			Casts = {},
			Npcs = {},
		}
		
		--build the table containinf all scripts and what scripts they trigger
		for index, scriptObject in ipairs (Plater.GetAllScripts ("script")) do
			if (scriptObject.Enabled) then
				for _, spellId in ipairs (scriptObject.SpellIds) do
					
					if (scriptObject.ScriptType == 1) then
						--> triggers auras
						local triggerTable = allTriggers.Auras [spellId]
						if (not triggerTable) then
							allTriggers.Auras [spellId] = {scriptObject}
						else
							tinsert (triggerTable, scriptObject)
						end
					
					elseif (scriptObject.ScriptType == 2) then
						--> triggers cast
						local triggerTable = allTriggers.Casts [spellId]
						if (not triggerTable) then
							allTriggers.Casts [spellId] = {scriptObject}
						else
							tinsert (triggerTable, scriptObject)
						end

					end
				end
				
				for _, NpcId in ipairs (scriptObject.NpcNames) do
					local triggerTable = allTriggers.Npcs [NpcId]
					if (not triggerTable) then
						allTriggers.Npcs [NpcId] = {scriptObject}
					else
						tinsert (triggerTable, scriptObject)
					end
				end
			end
		end
		
		--> store scripts with overlap
		local scriptsWithOverlap = {
			Auras = {},
			Casts = {},
			Npcs = {},
		}
		
		local amount = 0
		
		--> check if there's more than 1 script for each trigger
		for triggerId, scriptsTable in pairs (allTriggers.Auras) do
			if (#scriptsTable > 1) then
				--overlap found
				scriptsWithOverlap.Auras [triggerId] = scriptsTable
				amount = amount + 1
			end
		end
		for triggerId, scriptsTable in pairs (allTriggers.Casts) do
			if (#scriptsTable > 1) then
				--overlap found
				scriptsWithOverlap.Casts [triggerId] = scriptsTable
				amount = amount + 1
			end
		end
		for triggerId, scriptsTable in pairs (allTriggers.Npcs) do
			if (#scriptsTable > 1) then
				--overlap found
				scriptsWithOverlap.Npcs [triggerId] = scriptsTable
				amount = amount + 1
			end
		end
		
		return scriptsWithOverlap, amount
	end

	---add a trigger to a script
	---@param triggerId number|string triggerId can be a npcId, npcName for NPCs or a spellId or spellName for auras and casts
	---@param triggerType string|number there's 3 types of triggers: Auras, Casts and Npcs. Auras and Casts uses 'scriptObject.SpellIds' to store the triggerId and Npcs uses 'scriptObject.NpcNames'
	---what define the type of trigger is the scriptObject.ScriptType, in other places of this project, triggerType can also be called scriptType
	---triggerType expects: aura = 1, cast = 2, npc = 3
	---@param scriptName string
	---@return boolean 'true' if the trigger was added to the script, false if something went wrong
	---@return string|nil message of error if the trigger wasn't added to the script
	function Plater.AddTriggerToScript(triggerId, triggerType, scriptName)
		--attempt to get the scriptObject for the passed scriptName
		local scriptObject = Plater.GetScriptObject(scriptName, "script")
		if (not scriptObject) then
			return false, "script not found"
		end
		
		--remove the trigger from any script to avoid overlaps (a trigger can only exists in one script at time)
		platerInternal.Scripts.RemoveTriggerFromAnyScript(triggerId)

		--check the triggerType to know in what table the script will store the triggerId
		if (triggerType == 1 or triggerType == 2 or triggerType == "aura" or triggerType == "cast") then
			--aura or cast
			DF.table.addunique(scriptObject.SpellIds, triggerId)

		elseif (triggerType == 3 or triggerType == "npc") then
			--npc
			DF.table.addunique(scriptObject.NpcNames, triggerId)

		else
			return false, "invalid triggerType"
		end
		
		Plater.WipeAndRecompileAllScripts("script")

		return true
	end

	function platerInternal.Scripts.RemoveTriggerFromAnyScript(triggerId)
		local scriptObject = platerInternal.Scripts.IsTriggerOnAnyScript(triggerId)
		if (scriptObject) then
			platerInternal.Scripts.RemoveTriggerFromScript(scriptObject, triggerId)
		end
	end

	function platerInternal.Scripts.IsTriggerOnAnyScript(triggerId)
		local allScripts = Plater.db.profile.script_data
		for i = 1, #allScripts do
			local scriptObject = allScripts[i]
			if (platerInternal.Scripts.DoesScriptHasTrigger(scriptObject, triggerId)) then
				return scriptObject
			end
		end
	end

	function platerInternal.Scripts.GetScriptObjectByName(scriptName)
		local allScripts = Plater.db.profile.script_data
		for i = 1, #allScripts do
			local scriptObject = allScripts[i]
			if (scriptObject.Name == scriptName) then
				return scriptObject
			end
		end
	end

	--add or remove a trigger without the need to pass through the scripting panel
	function platerInternal.Scripts.AddSpellToScriptTriggers(scriptObject, spellId)
		DF.table.addunique(scriptObject.SpellIds, spellId)
		Plater.WipeAndRecompileAllScripts("script")
	end

	function platerInternal.Scripts.RemoveSpellFromScriptTriggers(scriptObject, spellId, noRecompile)
		local index = DF.table.find(scriptObject.SpellIds, spellId)
		if (index) then
			tremove(scriptObject.SpellIds, index)

			if (not noRecompile) then
				Plater.WipeAndRecompileAllScripts("script")
			end
		end
	end

	function platerInternal.Scripts.DoesScriptHasTrigger(scriptObject, trigger)
		local index = DF.table.find(scriptObject.SpellIds, trigger)
		if (index) then
			return true
		end

		local index = DF.table.find(scriptObject.NpcNames, trigger)
		if (index) then
			return true
		end
	end

	function platerInternal.Scripts.RemoveTriggerFromScript(scriptObject, triggerId)
		local index = DF.table.find(scriptObject.SpellIds, triggerId)
		if (index) then
			tremove(scriptObject.SpellIds, index)
			Plater.WipeAndRecompileAllScripts("script")
		end

		local index = DF.table.find(scriptObject.NpcNames, triggerId)
		if (index) then
			tremove(scriptObject.NpcNames, index)
			Plater.WipeAndRecompileAllScripts("script")
		end
	end

	function platerInternal.Scripts.AddNpcToScriptTriggers(scriptObject, npcId)
		DF.table.addunique(scriptObject.NpcNames, npcId)
		Plater.WipeAndRecompileAllScripts("script")
	end

	function platerInternal.Scripts.RemoveNpcFromScriptTriggers(scriptObject, npcId)
		local index = DF.table.find(scriptObject.NpcNames, npcId)
		if (index) then
			tremove(scriptObject.NpcNames, index)
			Plater.WipeAndRecompileAllScripts("script")
		end
	end

	---retrive the script object for a selected scriptId
	---@param scriptID number|string if number scriptId is the index of the script in the db table, this index can change when a script is removed
	---@param scriptType string is always "script" or "hook", hooks scripts are stored in a different table, ingame they are called "Mods"
	function Plater.GetScriptObject (scriptID, scriptType)
		if (type(scriptID) == "string" and scriptType == "script") then
			return platerInternal.Scripts.GetScriptObjectByName(scriptID)
		end

		if (scriptType == "script") then
			local script = Plater.db.profile.script_data [scriptID]
			if (script) then
				return script
			end
			
		elseif (scriptType == "hook") then
			local script = Plater.db.profile.hook_data [scriptID]
			if (script) then
				return script
			end

		end
	end

	--return the main db table for the script type
	function Plater.GetScriptDB (scriptType)
		if (scriptType == "script") then
			return Plater.db.profile.script_data
			
		elseif (scriptType == "hook") then
			return Plater.db.profile.hook_data
		end
	end

	--if the type of a scriptObject is unknown
	function Plater.GetScriptType (scriptObject)
		if (scriptObject.Hooks) then
			return "hook"
		elseif (scriptObject.SpellIds) then
			return "script"
		end
	end

	function Plater.DispatchCommReceivedMessageHookEvent(scriptUID, source, ...)
		if (HOOK_COMM_RECEIVED_MESSAGE.ScriptAmount > 0) then
			for i = 1, HOOK_COMM_RECEIVED_MESSAGE.ScriptAmount do
				local globalScriptObject = HOOK_COMM_RECEIVED_MESSAGE[i]
				
				if (globalScriptObject.DBScriptObject.UID == scriptUID) then
					--run
					Plater.ScriptMetaFunctions.ScriptRunCommMessageHook(globalScriptObject, "Receive Comm Message", source, ...)
				end
			end
		end
	end
	
	function Plater.DispatchCommSendMessageHookEvents()
		if (HOOK_COMM_SEND_MESSAGE.ScriptAmount > 0) then
			for i = 1, HOOK_COMM_SEND_MESSAGE.ScriptAmount do
				local globalScriptObject = HOOK_COMM_SEND_MESSAGE[i]
				
				--run
				Plater.ScriptMetaFunctions.ScriptRunCommMessageHook(globalScriptObject, "Send Comm Message")
			end
		end
	end
	
	function Plater.VerifyScriptIdForComm(scriptIndex, scriptId, uniqueId)
		if not scriptIndex or not scriptId or not uniqueId then return end
		
		local globalScriptObject = HOOK_COMM_SEND_MESSAGE[scriptIndex]
		if globalScriptObject and globalScriptObject.DBScriptObject and globalScriptObject.DBScriptObject.scriptId and globalScriptObject.DBScriptObject.scriptId == scriptId and globalScriptObject.DBScriptObject.UID and globalScriptObject.DBScriptObject.UID == uniqueId then
			return true
		end
		
		return false
	end

	function Plater.DispatchTalentUpdateHookEvent()
		if (HOOK_PLAYER_TALENT_UPDATE.ScriptAmount > 0) then
			for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
				if (plateFrame and plateFrame.unitFrame.PlaterOnScreen) then
					for i = 1, HOOK_PLAYER_TALENT_UPDATE.ScriptAmount do
						local globalScriptObject = HOOK_PLAYER_TALENT_UPDATE [i]
						local unitFrame = plateFrame.unitFrame
						if not plateFrame.unitFrame.PlaterOnScreen then
							return
						end
						local scriptContainer = unitFrame:ScriptGetContainer()
						local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Player Talent Update")
						--run
						unitFrame:ScriptRunHook (scriptInfo, "Player Talent Update")
					end
				end
			end
		end
	end

	function Plater.ScheduleHookForCombat (timerObject)
		if (timerObject.Event == "Enter Combat") then
			if (HOOK_COMBAT_ENTER.ScriptAmount > 0) then
				for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
					for i = 1, HOOK_COMBAT_ENTER.ScriptAmount do
						local globalScriptObject = HOOK_COMBAT_ENTER [i]
						local unitFrame = plateFrame.unitFrame
						if not plateFrame.unitFrame.PlaterOnScreen then
							return
						end
						local scriptContainer = unitFrame:ScriptGetContainer()
						local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Enter Combat")
						--run
						unitFrame:ScriptRunHook (scriptInfo, "Enter Combat")
					end
				end
			end
			
		elseif (timerObject.Event == "Leave Combat") then
			if (HOOK_COMBAT_LEAVE.ScriptAmount > 0) then
				for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
					for i = 1, HOOK_COMBAT_LEAVE.ScriptAmount do
						local globalScriptObject = HOOK_COMBAT_LEAVE [i]
						local unitFrame = plateFrame.unitFrame
						if not plateFrame.unitFrame.PlaterOnScreen then
							return
						end
						local scriptContainer = unitFrame:ScriptGetContainer()
						local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Leave Combat")
						--run
						unitFrame:ScriptRunHook (scriptInfo, "Leave Combat")
					end
				end
			end
		end
	end	





