--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
---@type detailsframework
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 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 select = select
local UnitGUID = UnitGUID
local lower = string.lower
local max = math.max
local min = math.min

local IsPlayerSpell = IsPlayerSpell
local IsSpellKnownOrOverridesKnown = IsSpellKnownOrOverridesKnown

if (not IsPlayerSpell) then
	IsPlayerSpell = function(spellID)
		local spellBank = Enum.SpellBookSpellBank.Player
		return C_SpellBook.IsSpellKnown(spellID, spellBank)
	end
end
if not (IsSpellKnownOrOverridesKnown) then
	IsSpellKnownOrOverridesKnown = function(spellID)
		local spellBank = Enum.SpellBookSpellBank.Player
		C_SpellBook.IsSpellKnownOrInSpellBook(spellID, spellBank, true)
	end
end

local GetSpecialization = C_SpecializationInfo and C_SpecializationInfo.GetSpecialization or GetSpecialization
local GetSpecializationInfo = C_SpecializationInfo and C_SpecializationInfo.GetSpecializationInfo or GetSpecializationInfo

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 IS_WOW_PROJECT_CLASSIC_MOP = IS_WOW_PROJECT_NOT_MAINLINE and ClassicExpansionAtLeast and LE_EXPANSION_MISTS_OF_PANDARIA and ClassicExpansionAtLeast(LE_EXPANSION_MISTS_OF_PANDARIA)
local IS_WOW_PROJECT_MIDNIGHT = DF.IsAddonApocalypseWow()

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()
Plater.fullVersionInfoContainsName = false
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() .. " - " .. Plater.db:GetCurrentProfile()
	Plater.fullVersionInfoContainsName = true
	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.VarSharing.HOOK_NAMEPLATE_ADDED = HOOK_NAMEPLATE_ADDED
platerInternal.VarSharing.HOOK_NAMEPLATE_CREATED = HOOK_NAMEPLATE_CREATED
platerInternal.VarSharing.HOOK_NAMEPLATE_REMOVED = HOOK_NAMEPLATE_REMOVED
platerInternal.VarSharing.HOOK_NAMEPLATE_UPDATED = HOOK_NAMEPLATE_UPDATED
platerInternal.VarSharing.HOOK_TARGET_CHANGED = HOOK_TARGET_CHANGED
platerInternal.VarSharing.HOOK_CAST_START = HOOK_CAST_START
platerInternal.VarSharing.HOOK_CAST_UPDATE = HOOK_CAST_UPDATE
platerInternal.VarSharing.HOOK_CAST_STOP = HOOK_CAST_STOP
platerInternal.VarSharing.HOOK_RAID_TARGET = HOOK_RAID_TARGET
platerInternal.VarSharing.HOOK_COMBAT_ENTER = HOOK_COMBAT_ENTER
platerInternal.VarSharing.HOOK_COMBAT_LEAVE = HOOK_COMBAT_LEAVE
platerInternal.VarSharing.HOOK_NAMEPLATE_CONSTRUCTOR = HOOK_NAMEPLATE_CONSTRUCTOR
platerInternal.VarSharing.HOOK_PLAYER_POWER_UPDATE = HOOK_PLAYER_POWER_UPDATE
platerInternal.VarSharing.HOOK_PLAYER_TALENT_UPDATE = HOOK_PLAYER_TALENT_UPDATE
platerInternal.VarSharing.HOOK_HEALTH_UPDATE = HOOK_HEALTH_UPDATE
platerInternal.VarSharing.HOOK_ZONE_CHANGED = HOOK_ZONE_CHANGED
platerInternal.VarSharing.HOOK_UNITNAME_UPDATE = HOOK_UNITNAME_UPDATE
platerInternal.VarSharing.HOOK_LOAD_SCREEN = HOOK_LOAD_SCREEN
platerInternal.VarSharing.HOOK_PLAYER_LOGON = HOOK_PLAYER_LOGON
platerInternal.VarSharing.HOOK_MOD_INITIALIZATION = HOOK_MOD_INITIALIZATION
platerInternal.VarSharing.HOOK_MOD_DEINITIALIZATION = HOOK_MOD_DEINITIALIZATION
platerInternal.VarSharing.HOOK_COMM_RECEIVED_MESSAGE = HOOK_COMM_RECEIVED_MESSAGE
platerInternal.VarSharing.HOOK_COMM_SEND_MESSAGE = HOOK_COMM_SEND_MESSAGE
platerInternal.VarSharing.HOOK_OPTION_CHANGED = HOOK_OPTION_CHANGED
platerInternal.VarSharing.HOOK_MOD_OPTION_CHANGED = HOOK_MOD_OPTION_CHANGED
platerInternal.VarSharing.HOOK_NAMEPLATE_DESTRUCTOR = HOOK_NAMEPLATE_DESTRUCTOR


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>"

platerInternal.VarSharing.PLATER_GLOBAL_MOD_ENV = PLATER_GLOBAL_MOD_ENV
platerInternal.VarSharing.PLATER_GLOBAL_SCRIPT_ENV = PLATER_GLOBAL_SCRIPT_ENV

--> 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"

platerInternal.VarSharing.MEMBER_UNITID = MEMBER_UNITID
platerInternal.VarSharing.MEMBER_GUID = MEMBER_GUID
platerInternal.VarSharing.MEMBER_NPCID = MEMBER_NPCID
platerInternal.VarSharing.MEMBER_QUEST = MEMBER_QUEST
platerInternal.VarSharing.MEMBER_REACTION = MEMBER_REACTION
platerInternal.VarSharing.MEMBER_RANGE = MEMBER_RANGE
platerInternal.VarSharing.MEMBER_NOCOMBAT = MEMBER_NOCOMBAT
platerInternal.VarSharing.MEMBER_NAME = MEMBER_NAME
platerInternal.VarSharing.MEMBER_NAMELOWER = MEMBER_NAMELOWER
platerInternal.VarSharing.MEMBER_TARGET = MEMBER_TARGET

--> 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"

platerInternal.VarSharing.ACTORTYPE_FRIENDLY_PLAYER = ACTORTYPE_FRIENDLY_PLAYER
platerInternal.VarSharing.ACTORTYPE_FRIENDLY_NPC = ACTORTYPE_FRIENDLY_NPC
platerInternal.VarSharing.ACTORTYPE_ENEMY_PLAYER = ACTORTYPE_ENEMY_PLAYER
platerInternal.VarSharing.ACTORTYPE_ENEMY_NPC = ACTORTYPE_ENEMY_NPC
platerInternal.VarSharing.ACTORTYPE_PLAYER = ACTORTYPE_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 HEALTHCUTOFF_AT_DATA = {}
	HEALTHCUTOFF_AT_DATA.healthCutOffActive = false
	HEALTHCUTOFF_AT_DATA.healthCutOffValue = 0.2
	HEALTHCUTOFF_AT_DATA.healthCutOffUpperValue = 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
						if IsPlayerSpell(392507) then
							lowExecute = 0.35 -- Deathspeaker
						else
							lowExecute = 0.20
						end
					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 isTalentLearned(94987) then --IsPlayerSpell(466930) then --> Black Arrow
						lowExecute = 0.2
						highExecute = 0.8
					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
						if IsPlayerSpell(456939) then -- Blistering Atrophy
							lowExecute = 0.30
						else
							lowExecute = 0.20
						end
					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
			-- classic and such
			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
					elseif IsPlayerSpell(17877) then
						lowExecute = 0.2
					else
						lowExecute = 0.20
					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.20
					end
				
				elseif (class == "DEATHKNIGHT") then
					if IsPlayerSpell(130735) or IsPlayerSpell(130736) or IsPlayerSpell(114866) then --Soul Reaper
						lowExecute = 0.35
					end
				
				elseif  (class == "ROGUE") then
					if IsPlayerSpell(111240) then --Dispatch
						lowExecute = 0.35
					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 = onAdded and 1 or plateFrame:GetAlpha()
		end
		unitFrame.IsInRange = nil
		local unitReaction = plateFrame [MEMBER_REACTION] or Plater.UnitReaction.UNITREACTION_HOSTILE
		
		--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 unitReaction >= 5) or (not DB_USE_ALPHA_ENEMIES and unitReaction < 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 unitReaction < 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)
				maxRange = maxRange or minRange or 0
				return maxRange <= (rangeCheckRange or 40)
			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] or unitFrame [MEMBER_UNITID])
			--print(isInRange, issecretvalue(isInRange))

			if (IS_WOW_PROJECT_MIDNIGHT and issecretvalue(isInRange)) then
				unitFrame.IsInRange = isInRange -- this might complicate things...
				
				unitFrame:SetAlphaFromBoolean (isInRange, nameplateAlpha * inRangeAlpha * (notTheTarget and overallRangeCheckAlpha or 1), (unitIsTarget and inRangeAlpha or overallRangeCheckAlpha) * nameplateAlpha * (notTheTarget and alphaMultiplier or 1))
				healthBar:SetAlphaFromBoolean (isInRange, inRangeAlpha * (notTheTarget and healthBar_rangeCheckAlpha or 1), (unitIsTarget and inRangeAlpha or healthBar_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
				if not castBarFade then
					castBar:SetAlphaFromBoolean (isInRange, inRangeAlpha * (notTheTarget and castBar_rangeCheckAlpha or 1), (unitIsTarget and inRangeAlpha or castBar_rangeCheckAlpha) * (notTheTarget and alphaMultiplier  or 1))
				end
				--powerBar:SetAlphaFromBoolean (isInRange, inRangeAlpha * (notTheTarget and powerBar_rangeCheckAlpha or 1), (unitIsTarget and inRangeAlpha or powerBar_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
				buffFrame1:SetAlphaFromBoolean (isInRange, inRangeAlpha * (notTheTarget and buffFrames_rangeCheckAlpha or 1), (unitIsTarget and inRangeAlpha or buffFrames_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
				buffFrame2:SetAlphaFromBoolean (isInRange, inRangeAlpha * (notTheTarget and buffFrames_rangeCheckAlpha or 1), (unitIsTarget and inRangeAlpha or buffFrames_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))

				plateFrame [MEMBER_RANGE] = isInRange
				plateFrame.unitFrame [MEMBER_RANGE] = isInRange
				
			elseif (isInRange) then
				--unit is in rage
				unitFrame.IsInRange = 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
				if not IS_WOW_PROJECT_MIDNIGHT then
					powerBar:SetAlpha (inRangeAlpha * (notTheTarget and powerBar_rangeCheckAlpha or 1))
				end
				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
				--unit is out of range
				unitFrame.IsInRange = false
				
				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
				if not IS_WOW_PROJECT_MIDNIGHT then
					powerBar:SetAlpha ((unitIsTarget and inRangeAlpha or powerBar_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
				end
				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
			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
					unitFrame:SetAlpha (nameplateAlpha * inRangeAlpha)
					healthBar:SetAlpha (inRangeAlpha)
					if not castBarFade then
						castBar:SetAlpha (inRangeAlpha)
					end
					if not IS_WOW_PROJECT_MIDNIGHT then
						powerBar:SetAlpha (inRangeAlpha)
					end
					buffFrame1:SetAlpha (DB_AURA_ALPHA)
					buffFrame2:SetAlpha (DB_AURA_ALPHA)

				else
					--this unit isnt the current player target
					unitFrame:SetAlpha (nameplateAlpha * inRangeAlpha * overallRangeCheckAlpha)
					healthBar:SetAlpha (inRangeAlpha * healthBar_rangeCheckAlpha)
					if not castBarFade then
						castBar:SetAlpha (inRangeAlpha * castBar_rangeCheckAlpha)
					end
					if not IS_WOW_PROJECT_MIDNIGHT then
						powerBar:SetAlpha (inRangeAlpha * powerBar_rangeCheckAlpha)
					end
					buffFrame1:SetAlpha (inRangeAlpha * buffFrames_rangeCheckAlpha)
					buffFrame2:SetAlpha (inRangeAlpha * buffFrames_rangeCheckAlpha)
				end
			else
				--player does not have a target, so just set to regular alpha
				unitFrame:SetAlpha (nameplateAlpha * inRangeAlpha)
				healthBar:SetAlpha (1)
				if not castBarFade then
					castBar:SetAlpha (1)
				end
				if not IS_WOW_PROJECT_MIDNIGHT then
					powerBar:SetAlpha (1)
				end
				buffFrame1:SetAlpha (DB_AURA_ALPHA)
				buffFrame2:SetAlpha (DB_AURA_ALPHA)
			end
		else
			-- no alpha settings, so just go to default
			unitFrame:SetAlpha (nameplateAlpha * inRangeAlpha)
			healthBar:SetAlpha (1)
			if not castBarFade then
				castBar:SetAlpha (1)
			end
			if not IS_WOW_PROJECT_MIDNIGHT then
				powerBar:SetAlpha (1)
			end
			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 or IS_WOW_PROJECT_CLASSIC_MOP then
			local assignedRole = UnitGroupRolesAssigned ("player")
			if assignedRole == "NONE" and UnitLevel ("player") >= 10 then
				if (IS_WOW_PROJECT_CLASSIC_MOP) then
					--this is the way to get the role assigned to the player in MOP
					local specSelected = C_SpecializationInfo.GetSpecialization()
					assignedRole = select(5, C_SpecializationInfo.GetSpecializationInfo(specSelected))
				else
					assignedRole = GetTalentGroupRole(GetActiveTalentGroup())
				end
			end
			local playerIsTank = assignedRole == "TANK"
			
			if not playerIsTank then
				playerIsTank = GetPartyAssignment("MAINTANK", "player") or false
			end
			
			return playerIsTank
		else
			if UnitGroupRolesAssigned then
				local assignedRole = UnitGroupRolesAssigned ("player")
				local playerIsTank = assignedRole == "TANK"
				if not playerIsTank and GetPartyAssignment then
					playerIsTank = GetPartyAssignment("MAINTANK", "player") or false
				end
				if not playerIsTank and IS_WOW_PROJECT_CLASSIC_MOP and GetSpecialization and GetSpecializationRole then
					local spec = GetSpecialization()
					playerIsTank = spec and GetSpecializationRole (spec) == "TANK"
				end
				return playerIsTank
			end
		
			--fallback
			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 or IS_WOW_PROJECT_CLASSIC_MOP 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
			if UnitGroupRolesAssigned then
				local assignedRole = UnitGroupRolesAssigned (unit)
				local unitIsTank = assignedRole == "TANK"
				if not unitIsTank and GetPartyAssignment then
					unitIsTank = GetPartyAssignment("MAINTANK", unit) or false
				end
				return unitIsTank
			end
			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 and not IS_WOW_PROJECT_CLASSIC_MOP 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
			if not IS_WOW_PROJECT_MIDNIGHT then
				TANK_CACHE [UnitName ("player")] = true
			else
				TANK_CACHE ["player"] = true
			end
			Plater.PlayerIsTank = true
		else
			if not IS_WOW_PROJECT_MIDNIGHT then
				TANK_CACHE [UnitName ("player")] = false
			else
				TANK_CACHE ["player"] = false
			end
			if IS_WOW_PROJECT_MAINLINE 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
				local unit = "raid" .. i
				if (IsUnitEffectivelyTank (unit)) then
					if (not UnitIsUnit (unit, "player")) then
						if IS_WOW_PROJECT_MIDNIGHT then
							TANK_CACHE [unit] = true
						else
							local unitName = UnitName (unit)
							if unitName ~= UNKNOWN then
								TANK_CACHE [unitName] = true
							end
						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
				local unit = "party" .. i
				if (IsUnitEffectivelyTank (unit)) then
					if (not UnitIsUnit (unit, "player")) then
						if IS_WOW_PROJECT_MIDNIGHT then
							TANK_CACHE [unit] = true
						else
							local unitName = UnitName (unit)
							if unitName ~= UNKNOWN then
								TANK_CACHE [unitName] = true
							end
						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
	---@return frame[]
	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] or plateFrame.unitFrame [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:RefreshConfigProfileReset() --private
		Plater:RefreshConfig()
		
		Plater.db.profile.use_ui_parent = true
		Plater.db.profile.use_ui_parent_just_enabled = false
		Plater.db.profile.ui_parent_scale_tune = 1 / (IS_WOW_PROJECT_MIDNIGHT and 1 or UIParent:GetEffectiveScale())
		
		--call the user to /reload his UI
		DF:ShowPromptPanel ("Plater profile reset, do you want /reload now (recommended)?", function() ReloadUI() end, function() end, true, 500)
	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"] = not IS_WOW_PROJECT_MIDNIGHT,
		["NamePlateVerticalScale"] = not IS_WOW_PROJECT_MIDNIGHT,
		["ShowClassColorInNameplate"] = true,
		["ShowNamePlateLoseAggroFlash"] = true,
		["nameplateGlobalScale"] = not IS_WOW_PROJECT_MIDNIGHT,
		["nameplateLargerScale"] = not IS_WOW_PROJECT_MIDNIGHT,
		["nameplateLargeTopInset"] = not IS_WOW_PROJECT_MIDNIGHT,
		["nameplateLargeBottomInset"] = not IS_WOW_PROJECT_MIDNIGHT,
		["nameplateMaxDistance"] = true,
		["nameplatePlayerMaxDistance"] = true,
		["nameplateMinScale"] = true,
		["nameplateMotion"] = not IS_WOW_PROJECT_MIDNIGHT,
		["nameplateStackingTypes"] = IS_WOW_PROJECT_MIDNIGHT,
		["nameplateMotionSpeed"] = not IS_WOW_PROJECT_MIDNIGHT,
		["nameplateOccludedAlphaMult"] = true,
		["nameplateOtherAtBase"] = true,
		["nameplateOtherTopInset"] = not IS_WOW_PROJECT_MIDNIGHT,
		["nameplateOtherBottomInset"] = not IS_WOW_PROJECT_MIDNIGHT,
		["nameplateOverlapV"] = true,
		["nameplateOverlapH"] = true,
		["nameplatePersonalHideDelaySeconds"] = (IS_WOW_PROJECT_MAINLINE and not IS_WOW_PROJECT_MIDNIGHT),
		["nameplatePersonalShowAlways"] = (IS_WOW_PROJECT_MAINLINE and not IS_WOW_PROJECT_MIDNIGHT),
		["nameplatePersonalShowInCombat"] = (IS_WOW_PROJECT_MAINLINE and not IS_WOW_PROJECT_MIDNIGHT),
		["nameplatePersonalShowWithTarget"] = (IS_WOW_PROJECT_MAINLINE and not IS_WOW_PROJECT_MIDNIGHT),
		["nameplateResourceOnTarget"] = (IS_WOW_PROJECT_MAINLINE),
		["nameplateSelectedScale"] = true,
		["nameplateSelfAlpha"] = (IS_WOW_PROJECT_MAINLINE and not IS_WOW_PROJECT_MIDNIGHT),
		["nameplateSelfBottomInset"] = (IS_WOW_PROJECT_MAINLINE and not IS_WOW_PROJECT_MIDNIGHT),
		["nameplateSelfScale"] = (IS_WOW_PROJECT_MAINLINE and not IS_WOW_PROJECT_MIDNIGHT),
		["nameplateSelfTopInset"] = (IS_WOW_PROJECT_MAINLINE and not IS_WOW_PROJECT_MIDNIGHT),
		["nameplateSimplifiedTypes"] = IS_WOW_PROJECT_MIDNIGHT,
		["nameplateShowAll"] = true,
		["nameplateShowEnemies"] = true,
		["nameplateShowEnemyGuardians"] = true,
		["nameplateShowEnemyMinions"] = true,
		["nameplateShowEnemyMinus"] = true,
		["nameplateShowEnemyPets"] = true,
		["nameplateShowEnemyTotems"] = true,
		["nameplateShowFriends"] = true,
		["nameplateShowFriendlyNPCs"] = true,
		["nameplateShowFriendlyMinions"] = not IS_WOW_PROJECT_MIDNIGHT,
		["nameplateShowFriendlyPets"] = not IS_WOW_PROJECT_MIDNIGHT,
		["nameplateShowFriendlyGuardians"] = not IS_WOW_PROJECT_MIDNIGHT,
		["nameplateShowFriendlyTotems"] = not IS_WOW_PROJECT_MIDNIGHT,
		["nameplateShowFriendlyPlayers"] = IS_WOW_PROJECT_MIDNIGHT,
		["nameplateShowFriendlyPlayerMinions"] = IS_WOW_PROJECT_MIDNIGHT,
		["nameplateShowFriendlyPlayerPets"] = IS_WOW_PROJECT_MIDNIGHT,
		["nameplateShowFriendlyPlayerGuardians"] = IS_WOW_PROJECT_MIDNIGHT,
		["nameplateShowFriendlyPlayerTotems"] = IS_WOW_PROJECT_MIDNIGHT,
		["nameplateShowFriendlyBuffs"] = true,
		["nameplateShowOffscreen"] = IS_WOW_PROJECT_MIDNIGHT,
		["nameplateShowOnlyNames"] = not IS_WOW_PROJECT_MIDNIGHT,
		["nameplateShowOnlyNameForFriendlyPlayerUnits"] = IS_WOW_PROJECT_MIDNIGHT,
		["nameplateUseClassColorForFriendlyPlayerUnitNames"] = IS_WOW_PROJECT_MIDNIGHT,
		["nameplateShowPersonalCooldowns"] = 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"] = true,
		["SoftTargetInteract"] = true,
		["SoftTargetNameplateInteract"] = true,
	}
	
	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,
			["nameplateShowFriendlyPlayerMinions"] = 3,
			["nameplateShowFriendlyPlayerPets"] = 4,
			["nameplateShowFriendlyPlayerGuardians"] = 5,
			["nameplateShowFriendlyPlayerTotems"] = 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, enumValue) --private
		if not canSaveCVars then return end
		if cvar and value and enumValue ~= nil then
			value = C_CVar.GetCVar(cvar)
		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 = IS_WOW_PROJECT_MIDNIGHT and debugstack(2) or debugstack() -- 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 = cvars_to_store[CVarName] and 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.ResetCVars(cvar)
		canSaveCVars = false
		
		if type(cvar) == "string" then
			SetCVarToDefault(cvar)
		else
			for CVarName in pairs (cvars_to_store) do
				SetCVarToDefault(CVarName)
			end
		end
		
		canSaveCVars = true
	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 = not IS_WOW_PROJECT_MIDNIGHT and profile.use_color_lerp or false

		--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 = IS_WOW_PROJECT_MIDNIGHT and 1 or profile.aura_tracker.track_method
		
		DB_DO_ANIMATIONS = not IS_WOW_PROJECT_MIDNIGHT and profile.use_health_animation or false -- done implicit in midnight
		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

		platerInternal.VarSharing.DB_NAME_NPCENEMY_ANCHOR = DB_NAME_NPCENEMY_ANCHOR
		platerInternal.VarSharing.DB_PLATE_CONFIG = DB_PLATE_CONFIG
		platerInternal.VarSharing.DB_CASTBAR_HIDE_ENEMIES = DB_CASTBAR_HIDE_ENEMIES
	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 = not IS_WOW_PROJECT_MIDNIGHT
			elseif (detailsPlaterConfig.realtime_dps_player_enabled) then
				IS_USING_DETAILS_INTEGRATION = not IS_WOW_PROJECT_MIDNIGHT
			elseif (detailsPlaterConfig.damage_taken_enabled) then
				IS_USING_DETAILS_INTEGRATION = not IS_WOW_PROJECT_MIDNIGHT
			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
		--DevTool:AddData({debugstack(), self:GetEffectiveScale(), unitFrame and unitFrame.PlaterOnScreen, unitFrame.PlateFrame.UnitFrame.unit, UnitName(unitFrame.PlateFrame.UnitFrame.unit)}, unitFrame.PlateFrame.UnitFrame.unit)
		if (unitFrame and unitFrame.PlaterOnScreen) then
			--local defaultScale = self:GetEffectiveScale() / self:GetScale()
			local defaultScale = self:GetEffectiveScale()
			if IS_WOW_PROJECT_MIDNIGHT then
				defaultScale = defaultScale / UIParent:GetEffectiveScale() --MIDNIGHT!! normalize for now
			end
			--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 powerBar = unitFrame.powerBar
		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)
			unitFrame.PlateFrame.PlaterAnchorFrame:SetFrameStrata(profile.ui_parent_base_strata)
			healthBar:SetFrameStrata (profile.ui_parent_base_strata)
			powerBar: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)
		unitFrame.PlateFrame.PlaterAnchorFrame:SetFrameLevel ((baseLevel > 0) and baseLevel or 0)
		powerBar:SetFrameLevel (((baseLevel > 0) and baseLevel or 0) + 2)
			
		local tmplevel = baseLevel + profile.ui_parent_cast_level + 3
		castBar:SetFrameLevel ((tmplevel > 0) and tmplevel or 0)
		
		tmplevel = baseLevel + profile.ui_parent_buff_level + 10
		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 powerBar = unitFrame.powerBar
		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)
			powerBar:SetFrameStrata (targetStrata)
			castBar:SetFrameStrata (targetStrata)
			buffFrame1:SetFrameStrata (targetStrata)
			buffFrame2:SetFrameStrata (targetStrata)
			buffSpecial:SetFrameStrata (targetStrata)
		end

		--level
		local baseLevel = unitFrame.baseFrameLevel or unitFrame:GetFrameLevel()
		baseLevel = DB_USE_UIPARENT and max(baseLevel + 5000, 5000) or baseLevel
		healthBar:SetFrameLevel ((baseLevel > 0) and baseLevel or 0)
		powerBar:SetFrameLevel (((baseLevel > 0) and baseLevel or 0) + 2)
			
		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 + 10, 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
	platerInternal.last_GetShapeshiftFormID = GetShapeshiftFormID()
	platerInternal.last_GROUP_ROSTER_UPDATE = 0
	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
			if IS_WOW_PROJECT_MIDNIGHT and not string.match(unit, "nameplate%d%d?$") then return end
			---@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()
			if (platerInternal.last_GROUP_ROSTER_UPDATE + 2) > GetTime() or platerInternal.has_GROUP_ROSTER_UPDATE_Scheduled then
				if not platerInternal.has_GROUP_ROSTER_UPDATE_Scheduled then
					platerInternal.has_GROUP_ROSTER_UPDATE_Scheduled = C_Timer.NewTimer (2.5, function()
						platerInternal.last_GROUP_ROSTER_UPDATE = GetTime()
						platerInternal.has_GROUP_ROSTER_UPDATE_Scheduled = nil
						Plater.RefreshTankCache()
					end)
				end
				return
			end
			platerInternal.last_GROUP_ROSTER_UPDATE = GetTime()
			platerInternal.has_GROUP_ROSTER_UPDATE_Scheduled = nil
			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.isPerformanceUnitAura then
						Plater.AddToAuraUpdate(plateFrame.unitFrame.unit, plateFrame.unitFrame) -- 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)

			if not IS_WOW_PROJECT_MIDNIGHT then
				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
		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
				if IS_WOW_PROJECT_MIDNIGHT and not string.match(unitID, "nameplate%d%d?$") then return end
				---@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 and (not IS_WOW_PROJECT_MIDNIGHT or (IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(unitName))) then
						unitNameTranslit = LibTranslit:Transliterate(unitName, TRANSLIT_MARK)
					end
					plateFrame [MEMBER_NAME] = unitNameTranslit
					plateFrame [MEMBER_NAMELOWER] = (IS_WOW_PROJECT_MIDNIGHT and plateFrame [MEMBER_NAME] or "") or 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
			if C_CVar and C_CVar.SetCVarBitfield then
				hooksecurefunc(C_CVar, 'SetCVarBitfield', 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,
		
		ADDON_LOADED = function(_, addOnName, containsBindings)
			if addonName == "BigWigs" or addonName == "DBM-Core" then
				Plater.RegisterBossModAuras()
			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()
				if not IS_WOW_PROJECT_MIDNIGHT then
					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
			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
					Plater.db.profile.ui_parent_scale_tune = 1 / (IS_WOW_PROJECT_MIDNIGHT and 1 or UIParent:GetEffectiveScale())
					Plater.RefreshDBUpvalues()
					Plater.UpdateAllPlates()
				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
					local guid
					if IS_WOW_PROJECT_MIDNIGHT and (issecretvalue(arg1) or issecretvalue(arg2)) then
						Plater.UpdateSoftInteractTarget(plateFrame, true) -- just update if any secret...
					elseif IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(plateFrame [MEMBER_GUID]) then
						guid = plateFrame [MEMBER_GUID]
					elseif IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(plateFrame.unitFrame [MEMBER_GUID]) then
						guid = plateFrame.unitFrame [MEMBER_GUID]
					elseif not IS_WOW_PROJECT_MIDNIGHT then
						guid = plateFrame [MEMBER_GUID]
					end
					if guid then
						if guid == arg1 or guid == arg2 then
							Plater.UpdateSoftInteractTarget(plateFrame, true)
						end
					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
					local guid
					if IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(plateFrame [MEMBER_GUID]) then
						guid = plateFrame [MEMBER_GUID]
					elseif IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(plateFrame.unitFrame [MEMBER_GUID]) then
						guid = plateFrame.unitFrame [MEMBER_GUID]
					elseif not IS_WOW_PROJECT_MIDNIGHT then
						guid = plateFrame [MEMBER_GUID]
					end
					if guid then
						if guid == arg1 or guid == arg2 then
							Plater.UpdateSoftInteractTarget(plateFrame, true)
						end
					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
					local guid
					if IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(plateFrame [MEMBER_GUID]) then
						guid = plateFrame [MEMBER_GUID]
					elseif IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(plateFrame.unitFrame [MEMBER_GUID]) then
						guid = plateFrame.unitFrame [MEMBER_GUID]
					elseif not IS_WOW_PROJECT_MIDNIGHT then
						guid = plateFrame [MEMBER_GUID]
					end
					if guid then
						if guid == arg1 or guid == arg2 then
							Plater.UpdateSoftInteractTarget(plateFrame, true)
						end
					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")

					if IS_WOW_PROJECT_MIDNIGHT then
						plateFrame:HookScript("OnSizeChanged", Plater.UpdateUIParentScale)
					end
					
					--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

				local unitFrame = plateFrame.unitFrame

				unitFrame:EnableMouse(false)

				---@class plateanchorframe : frame
				
				---@type plateanchorframe
				plateFrame.PlaterAnchorFrame = CreateFrame ("Frame", newUnitFrame:GetName() .. "AnchorFrame", plateFrame)
				plateFrame.PlaterAnchorFrame:SetSize(Plater.db.profile.plate_config.enemynpc.health[1] or 112, Plater.db.profile.plate_config.enemynpc.health[2] or 12)
				plateFrame.PlaterAnchorFrame:EnableMouse(false)
				plateFrame.PlaterAnchorFrame:SetParent(plateFrame)
				
				-- stacking size for midnight
				if plateFrame.SetStackingBoundsFrame then
					unitFrame.stackSizeFrame = CreateFrame("Frame", nil, unitFrame, "BackdropTemplate")
					local tex = unitFrame.stackSizeFrame:CreateTexture()
					tex:SetColorTexture(1, 0, 0, 0)
					tex:SetAllPoints(unitFrame.stackSizeFrame)
					
					plateFrame.debugAreaTextureStacking = plateFrame:CreateTexture (nil, "background")
					plateFrame.debugAreaTextureStacking:SetColorTexture (.1, .7, .1, .5)
					plateFrame.debugAreaTextureStacking:SetAllPoints(unitFrame.stackSizeFrame)
					plateFrame.debugAreaTextureStacking:Hide()
					plateFrame.debugAreaTextStacking = plateFrame:CreateFontString (nil, "artwork", "GameFontNormal")
					plateFrame.debugAreaTextStacking:SetPoint ("top", plateFrame.debugAreaTextureStacking, "bottom", 0, 1)
					plateFrame.debugAreaTextStacking:SetText ("valid area for stacking")
					plateFrame.debugAreaTextStacking:SetTextColor (.1, .7, .1)
					plateFrame.debugAreaTextStacking:Hide()
					--unitFrame.stackSizeFrame:SetBackdrop ({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16, edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1})
					--unitFrame.stackSizeFrame:SetBackdropColor (0, 0, 0, 0.5)
					--unitFrame.stackSizeFrame:SetBackdropBorderColor (0, 0, 0, 1)
				end
				
				--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)
				if (newUnitFrame.castBar.OnEvent) then
					hooksecurefunc(newUnitFrame.castBar, "OnEvent", Plater.CastBarOnEvent_Hook)
				end
				if (newUnitFrame.castBar.OnTick) then
					hooksecurefunc(newUnitFrame.castBar, "OnTick", Plater.CastBarOnTick_Hook)
				end
				
				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
				unitFrame.Plater = true
				
				unitFrame.PlateFrame = plateFrame
				plateFrame.isNamePlate = true
				unitFrame.isNamePlate = true
				unitFrame.IsUnitNameplate = true
				
				plateFrame.NameAnchor = 0
				unitFrame.healthBar.isNamePlate = true
			
			unitFrame.RefreshID = 0
			
			-- "PlaterMainAuraIcon"
			-- "PlaterSecondaryAuraIcon"

			---@class buffframe : frame
			---@field amountAurasShown number
			---@field PlaterBuffList table
			---@field isNameplate boolean
			---@field unitFrame unitframe
			---@field healthBar df_healthbar
			---@field AuraCache table
			---@field Name string
			---@field BuffFrame2 buffframe
			---@field BuffFrame1 buffframe

			--> buff frames
				--main buff frame
				local buffFrame = CreateFrame ("frame", unitFrame:GetName() .. "BuffFrame1", unitFrame, BackdropTemplateMixin and "BackdropTemplate")
				buffFrame.amountAurasShown = 0
				buffFrame.PlaterBuffList = {}
				buffFrame.isNameplate = true
				buffFrame.unitFrame = unitFrame --used on resource frame anchor update
				buffFrame.healthBar = unitFrame.healthBar
				buffFrame.AuraCache = {}
				unitFrame.BuffFrame = buffFrame

				--secondary buff frame
				local buffFrame2 = CreateFrame ("frame", unitFrame:GetName() .. "BuffFrame2", unitFrame, BackdropTemplateMixin and "BackdropTemplate")
				buffFrame2 = CreateFrame ("frame", unitFrame:GetName() .. "BuffFrame2", unitFrame, BackdropTemplateMixin and "BackdropTemplate")
				buffFrame2.amountAurasShown = 0
				buffFrame2.PlaterBuffList = {}
				buffFrame2.isNameplate = true
				buffFrame2.unitFrame = unitFrame
				buffFrame2.healthBar = unitFrame.healthBar
				buffFrame2.AuraCache = {}
				unitFrame.BuffFrame2 = buffFrame2
			
			--> identify aura containers
				buffFrame.Name = "Main" --aura frame 1
				buffFrame2.Name = "Secondary" --aura frame 2
			
			--> store the secondary anchor inside the regular buff container for speed
			buffFrame.BuffFrame2 = buffFrame2
			buffFrame2.BuffFrame1 = buffFrame
			
			--> unit aura cache
			unitFrame.AuraCache = {}
			unitFrame.GhostAuraCache = {}
			unitFrame.ExtraAuraCache = {}
			
			local healthBar = unitFrame.healthBar
			
			--cache the unit frame within the health and cast bars, this avoid GetParent() calls
			healthBar.unitFrame = unitFrame
			unitFrame.castBar.unitFrame = 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
			unitFrame.CustomIndicators = {}
			
			--> cliclable area debug
				plateFrame.debugAreaTexture = plateFrame:CreateTexture (nil, "background")
				plateFrame.debugAreaTexture:SetColorTexture (.1, .1, .1, .5)
				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
				unitFrame.TargetTextures2Sides = {}
				unitFrame.TargetTextures4Sides = {}
				for i = 1, 2 do
					local targetTexture = healthBar:CreateTexture (nil, "overlay")
					targetTexture:SetDrawLayer ("overlay", 7)
					tinsert (unitFrame.TargetTextures2Sides, targetTexture)
				end
				for i = 1, 4 do
					local targetTexture = healthBar:CreateTexture (nil, "overlay")
					targetTexture:SetDrawLayer ("overlay", 7)
					tinsert (unitFrame.TargetTextures4Sides, targetTexture)
				end
				
				--two extra target glow placed outside the healthbar, one above and another below the health bar
				local TargetNeonUp = unitFrame:CreateTexture (nil, "overlay")
				TargetNeonUp:SetDrawLayer ("overlay", 7)
				TargetNeonUp:SetBlendMode ("ADD")
				TargetNeonUp:Hide()
				plateFrame.TargetNeonUp = TargetNeonUp
				unitFrame.TargetNeonUp = TargetNeonUp
				
				local TargetNeonDown = unitFrame:CreateTexture (nil, "overlay")
				TargetNeonDown:SetDrawLayer ("overlay", 7)
				TargetNeonDown:SetBlendMode ("ADD")
				TargetNeonDown:SetTexCoord (0, 1, 1, 0)
				TargetNeonDown:Hide()
				plateFrame.TargetNeonDown = TargetNeonDown
				unitFrame.TargetNeonDown = TargetNeonDown
				
			--> target overlay (the texture added above the nameplate when the unit is selected)
				unitFrame.targetOverlayTexture = healthBar:CreateTexture (nil, "artwork")
				unitFrame.targetOverlayTexture:SetDrawLayer ("artwork", 2)
				unitFrame.targetOverlayTexture:SetBlendMode ("ADD")
				unitFrame.targetOverlayTexture:SetAllPoints()
				unitFrame.targetOverlayTexture:Hide()
			
			--> 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 healthCutOffUpper = healthBar:CreateTexture (nil, "overlay")
				healthCutOffUpper:SetDrawLayer ("overlay", 7)
				healthCutOffUpper:SetTexture ([[Interface\AddOns\Plater\images\health_bypass_indicator]])

				healthCutOffUpper:SetBlendMode ("ADD")
				healthCutOffUpper:Hide()
				healthBar.healthCutOffUpper = healthCutOffUpper
				healthBar.ExecuteRangeHealthCutOffUpper = healthCutOffUpper --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
				
				local healthCutOffUpperShowAnimation = DF:CreateAnimationHub (healthCutOffUpper, cutoffAnimationOnPlay, cutoffAnimationOnStop)
				DF:CreateAnimation (healthCutOffUpperShowAnimation, "Scale", 1, .2, .3, .3, 1.2, 1.2)
				DF:CreateAnimation (healthCutOffUpperShowAnimation, "Scale", 2, .2, 1.2, 1.2, 1, 1)
				DF:CreateAnimation (healthCutOffUpperShowAnimation, "Alpha", 1, .2, .2, 1)
				DF:CreateAnimation (healthCutOffUpperShowAnimation, "Alpha", 2, .2, 1, .5)
				healthCutOffUpper.ShowAnimation = healthCutOffUpperShowAnimation
				
				--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()
				
				local executeRangeUpper = healthBar:CreateTexture (nil, "border")
				executeRangeUpper:SetTexture ([[Interface\AddOns\Plater\images\execute_bar]])
				PixelUtil.SetPoint (executeRangeUpper, "left", healthBar, "left", 0, 0)
				healthBar.executeRangeUpper = executeRangeUpper
				healthBar.ExecuteRangeUpperBar = executeRangeUpper --alias for scripting
				executeRangeUpper: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 executeGlowUpperUp = healthBar:CreateTexture (nil, "overlay")
				executeGlowUpperUp:SetTexture ([[Interface\AddOns\Plater\images\blue_neon]])
				executeGlowUpperUp:SetTexCoord (0, 1, 0, 0.5)
				executeGlowUpperUp:SetHeight (32)		
				executeGlowUpperUp:SetBlendMode ("ADD")
				executeGlowUpperUp:Hide()
				PixelUtil.SetPoint (executeGlowUpperUp, "bottomright", healthBar, "topright", 0, 0)
				PixelUtil.SetPoint (executeGlowUpperUp, "bottomleft", healthBar, "topleft", 0, 0)
				healthBar.ExecuteGlowUpperUp = executeGlowUpperUp
				
				local executeGlowUpperDown = healthBar:CreateTexture (nil, "overlay")
				executeGlowUpperDown:SetTexture ([[Interface\AddOns\Plater\images\blue_neon]])
				executeGlowUpperDown:SetTexCoord (0, 1, 0.5, 1)
				executeGlowUpperDown:SetHeight (32)
				executeGlowUpperDown:SetBlendMode ("ADD")
				executeGlowUpperDown:Hide()
				PixelUtil.SetPoint (executeGlowUpperDown, "topright", healthBar, "bottomright", 0, 0)
				PixelUtil.SetPoint (executeGlowUpperDown, "topleft", healthBar, "bottomleft", 0, 0)
				healthBar.ExecuteGlowUpperDown = executeGlowUpperDown
				
				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)
				
				executeGlowUpperUp.ShowAnimation = DF:CreateAnimationHub (executeGlowUpperUp, executeGlowAnimationOnPlay, executeGlowAnimationOnStop)
				DF:CreateAnimation (executeGlowUpperUp.ShowAnimation, "Scale", 1, .2, 1, .1, 1, 1.2, "bottom", 0, 0)
				DF:CreateAnimation (executeGlowUpperUp.ShowAnimation, "Scale", 1, .2, 1, 1, 1, 1)
				
				executeGlowUpperDown.ShowAnimation = DF:CreateAnimationHub (executeGlowUpperDown, executeGlowAnimationOnPlay, executeGlowAnimationOnStop)
				DF:CreateAnimation (executeGlowUpperDown.ShowAnimation, "Scale", 1, .2, 1, .1, 1, 1.2, "top", 0, 0)
				DF:CreateAnimation (executeGlowUpperDown.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
				unitFrame.PlaterRaidTargetFrame = CreateFrame ("frame", nil, unitFrame, BackdropTemplateMixin and "BackdropTemplate")
				--unitFrame.PlaterRaidTargetFrame = CreateFrame ("frame", nil, unitFrame.healthBar, BackdropTemplateMixin and "BackdropTemplate")
				local targetFrame = 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] --or unitFrame [MEMBER_UNITID]
				onTickFrame.HealthBar = healthBar
				onTickFrame.PlateFrame = plateFrame
				onTickFrame.unitFrame = unitFrame
				onTickFrame.BuffFrame = unitFrame.BuffFrame
				onTickFrame.BuffFrame2 = unitFrame.BuffFrame2
				
				
				--> create a second castbar
				local castBar2 = DF:CreateCastBar (unitFrame, "$parentCastBar2")
				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", unitFrame.castBar, "bottomleft", 0, -2)
				castBar2:SetPoint("topright", unitFrame.castBar, "bottomright", 0, -2)

			
			--> unit name
				--regular name
				unitFrame.unitName:SetParent (healthBar) --the name is parented to unitFrame in the framework, parent it to health bar
				healthBar.unitName = unitFrame.unitName
				healthBar.PlateFrame = plateFrame
				plateFrame.unitName = unitFrame.unitName
				plateFrame.CurrentUnitNameString = unitFrame.unitName
				healthBar.unitName:SetDrawLayer ("overlay", 7)
				
				--special name and title
				local ActorNameSpecial = unitFrame:CreateFontString (nil, "artwork", "GameFontNormal")
				unitFrame.ActorNameSpecial = ActorNameSpecial --alias for scripts
				plateFrame.ActorNameSpecial = ActorNameSpecial
				PixelUtil.SetPoint (plateFrame.ActorNameSpecial, "center", plateFrame, "center", 0, 0)
				plateFrame.ActorNameSpecial:Hide()
				
				local ActorTitleSpecial = unitFrame:CreateFontString (nil, "artwork", "GameFontNormal")
				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
				plateFrame.Obscured:Hide()

				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,
				}
				
				unitFrame.ExtraIconFrame = DF:CreateIconRow (unitFrame, "$parentExtraIconRow", options)
				unitFrame.ExtraIconFrame:ClearIcons()
				unitFrame.ExtraIconFrame.RefreshID = 0
				unitFrame.ExtraIconFrame.AuraCache = {}
				--> cache the extra icon frame inside the buff frame for speed
				unitFrame.BuffFrame.ExtraIconFrame = unitFrame.ExtraIconFrame
			
			--> Support for DBM and BigWigs Nameplate Auras
				Plater.CreateBossModAuraFrame(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()
				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 = unitFrame.castBar
				castBar.PlateFrame = plateFrame
				castBar.unitFrame = 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, 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", unitFrame.healthBar, "TOPLEFT", -borderOffset, borderOffset)
				PixelUtil.SetPoint (healthBarBorder, "TOPRIGHT", unitFrame.healthBar, "TOPRIGHT", borderOffset, borderOffset)
				PixelUtil.SetPoint (healthBarBorder, "BOTTOMLEFT", unitFrame.healthBar, "BOTTOMLEFT", -borderOffset, -borderOffset)
				PixelUtil.SetPoint (healthBarBorder, "BOTTOMRIGHT", 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)
				unitFrame.healthBar.border = healthBarBorder
				
				local powerBarBorder = DF:CreateFullBorder(nil, unitFrame.powerBar)
				PixelUtil.SetPoint (powerBarBorder, "TOPLEFT", unitFrame.powerBar, "TOPLEFT", -borderOffset, borderOffset)
				PixelUtil.SetPoint (powerBarBorder, "TOPRIGHT", unitFrame.powerBar, "TOPRIGHT", borderOffset, borderOffset)
				PixelUtil.SetPoint (powerBarBorder, "BOTTOMLEFT", unitFrame.powerBar, "BOTTOMLEFT", -borderOffset, -borderOffset)
				PixelUtil.SetPoint (powerBarBorder, "BOTTOMRIGHT", 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)
				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(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
				unitFrame.FocusIndicator = focusIndicator
			
			--> low aggro warning
				unitFrame.aggroGlowUpper = plateFrame:CreateTexture (nil, "background", nil, -4)
				PixelUtil.SetPoint (unitFrame.aggroGlowUpper, "bottomleft", unitFrame.healthBar, "topleft", -3, 0)
				PixelUtil.SetPoint (unitFrame.aggroGlowUpper, "bottomright", unitFrame.healthBar, "topright", 3, 0)
				unitFrame.aggroGlowUpper:SetTexture ([[Interface\BUTTONS\UI-Panel-Button-Glow]])
				unitFrame.aggroGlowUpper:SetTexCoord (0, 95/128, 0, 9/64)
				unitFrame.aggroGlowUpper:SetBlendMode ("ADD")
				unitFrame.aggroGlowUpper:SetHeight (4)
				unitFrame.aggroGlowUpper:Hide()
				
				unitFrame.aggroGlowLower = plateFrame:CreateTexture (nil, "background", nil, -4)
				PixelUtil.SetPoint (unitFrame.aggroGlowLower, "topleft", unitFrame.healthBar, "bottomleft", -3, 0)
				PixelUtil.SetPoint (unitFrame.aggroGlowLower, "topright", unitFrame.healthBar, "bottomright", 3, 0)
				unitFrame.aggroGlowLower:SetTexture ([[Interface\BUTTONS\UI-Panel-Button-Glow]])
				unitFrame.aggroGlowLower:SetTexCoord (0, 95/128, 30/64, 38/64)
				unitFrame.aggroGlowLower:SetBlendMode ("ADD")
				unitFrame.aggroGlowLower:SetHeight (4)
				unitFrame.aggroGlowLower:Hide()

			--> soft-interact icon
				unitFrame.softInteractIconFrame = CreateFrame ("frame",unitFrame:GetName() .. "softInteractIconFrame", plateFrame, BackdropTemplateMixin and "BackdropTemplate")
				unitFrame.softInteractIcon = unitFrame.softInteractIconFrame:CreateTexture("$parentIcon", "OVERLAY")
				unitFrame.softInteractIcon:SetParent(plateFrame)
				unitFrame.softInteractIcon:SetTexture(136243)
				unitFrame.softInteractIcon:Show()
				unitFrame.softInteractIconFrame:SetFrameLevel(unitFrame.healthBar:GetFrameLevel() + 25)
				unitFrame.softInteractIconFrame.Mask = unitFrame.softInteractIconFrame:CreateMaskTexture(nil, "OVERLAY", nil, 1)
				unitFrame.softInteractIconFrame.Mask:Show()
				unitFrame.softInteractIconFrame.Mask:SetAtlas("CircleMaskScalable", true)
				--unitFrame.softInteractIconFrame.Mask:SetScale(1)
				unitFrame.softInteractIcon:AddMaskTexture(unitFrame.softInteractIconFrame.Mask)
				unitFrame.softInteractIconFrame.Mask:ClearAllPoints()
				PixelUtil.SetPoint(unitFrame.softInteractIconFrame.Mask, "CENTER", unitFrame.softInteractIconFrame, "CENTER", 0, 0)
				unitFrame.softInteractIconFrame.Mask:SetAllPoints(unitFrame.softInteractIcon)
				unitFrame.softInteractIconFrame:Hide()
				unitFrame.softInteractIcon.anchor = { side = 8, x = 0, y = 18, }
				unitFrame.softInteractIcon.size = 24
				--Plater.SetAnchor(unitFrame.softInteractIconFrame, unitFrame.softInteractIcon.anchor or { side = 8, x = 0, y = 18, }, unitFrame.healthBar)
				--Plater.SetAnchor(unitFrame.softInteractIconFrame, unitFrame.softInteractIcon.anchor or { side = 8, x = 0, y = 18, }, 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 = unitFrame:ScriptGetContainer()
						local scriptInfo = unitFrame:HookGetInfo (globalScriptObject, scriptContainer, "Nameplate Created")
						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 IS_WOW_PROJECT_MIDNIGHT then -- don't simplify
					--C_NamePlateManager.SetNamePlateSimplified(unitID, false) -- some people like them
				end
				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
--print("test", CastSpellByName("This is a test", unitBarId))
--print("test", CastSpellByID(355913, unitBarId))
		
			local unitID = unitBarId
			---@type plateframe
			local plateFrame

			local nameplateIsEditor = false

			if (unitBarId == "preview") then
				unitBarId = "player"
				unitID = "player"
				nameplateIsEditor = true
				plateFrame = PlaterDesignerPlatePreview
			else
				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
			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 or plateFrame.unitFrame or plateFrame.UnitFrame
			end
			if (not plateFrame.unitFrame) then
				return
			end
			
			--get and format the reaction to always be the value of the constants, then cache the reaction in some widgets for performance
			plateFrame.unitFrame [MEMBER_UNITID] = unitID
			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 = not nameplateIsEditor and UnitIsPlayer (unitID)
			local isSelf = not nameplateIsEditor and UnitIsUnit (unitID, "player")
			if IS_WOW_PROJECT_MIDNIGHT then isSelf = false end --MIDNIGHT!!
			
			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 or plateFrame.unitFrame)
			plateFrame.unitFrame.blizzardPlateFrameID = blizzardPlateFrameID
			
			--if (not plateFrame.UnitFrame.HasPlaterHooksRegistered) then
			if not HOOKED_BLIZZARD_PLATEFRAMES[blizzardPlateFrameID] then
				--print(GetTime(), HOOKED_BLIZZARD_PLATEFRAMES[tostring(plateFrame.UnitFrame)], tostring(plateFrame.UnitFrame), plateFrame.UnitFrame.HasPlaterHooksRegistered)
				--hook the retail nameplate
				--plateFrame.UnitFrame:HookScript("OnShow", Plater.OnRetailNamePlateShow)
				local blizzardUnitFrame = plateFrame.UnitFrame
				if (blizzardUnitFrame and blizzardUnitFrame.Show) then
					hooksecurefunc(blizzardUnitFrame, "Show", Plater.OnRetailNamePlateShow)
				end
				if IS_WOW_PROJECT_MIDNIGHT and blizzardUnitFrame and blizzardUnitFrame.SetAlpha then
					local locked = false
					hooksecurefunc(blizzardUnitFrame, "SetAlpha", function(self)
						if locked or self:IsForbidden() then
							return
						end
						if ENABLED_BLIZZARD_PLATEFRAMES[tostring(self)] then return end
						locked = true
						self:SetAlpha(0)
						locked = false
					end)
				end
				
				--plateFrame.UnitFrame.HasPlaterHooksRegistered = true
				HOOKED_BLIZZARD_PLATEFRAMES[blizzardPlateFrameID] = true
				
			end
			
			--MIDNIGHT TESTING
			if IS_WOW_PROJECT_MIDNIGHT then
				--TextureLoadingGroupMixin.AddTexture({ textures = plateFrame.UnitFrame.healthBar }, "capNumericDisplay") -- this is, luckily, baseline now.
				C_NamePlateManager.SetNamePlateSimplified(unitID, false)
				
				local isPlayer = UnitIsPlayer (unitID)
				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
			end
			
			if (nameplateIsEditor) then
				reaction = Plater.UnitReaction.UNITREACTION_HOSTILE
			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()
				
				Plater.UpdateStackingSize(plateFrame)
				
				local onlyNames = GetCVarBool ("nameplateShowOnlyNames") or Plater.db.profile.saved_cvars.nameplateShowOnlyNames == "1" or GetCVarBool("nameplateShowOnlyNameForFriendlyPlayerUnits") or Plater.db.profile.saved_cvars.nameplateShowOnlyNameForFriendlyPlayerUnits == "1"
				
				C_Timer.After(0.1, function()
					if not plateFrame.UnitFrame then return end
					plateFrame.PlaterAnchorFrame:ClearAllPoints()
					if onlyNames then
						plateFrame.PlaterAnchorFrame:SetParent(plateFrame)
						plateFrame.PlaterAnchorFrame:Hide()
						plateFrame.PlaterAnchorFrame:SetPoint("topright", plateFrame, "topright")
						plateFrame.PlaterAnchorFrame:SetPoint("bottomleft", plateFrame, "bottomleft")

					else
						plateFrame.PlaterAnchorFrame:SetParent(plateFrame.UnitFrame.healthBar)
						plateFrame.PlaterAnchorFrame:Show()
						plateFrame.PlaterAnchorFrame:SetPoint("topright", plateFrame.UnitFrame.healthBar, "topright")
						plateFrame.PlaterAnchorFrame:SetPoint("bottomleft", plateFrame.UnitFrame.healthBar, "bottomleft")
					end
					plateFrame.PlaterAnchorFrame:SetFrameStrata(plateFrame.UnitFrame.healthBar:GetFrameStrata())
					plateFrame.PlaterAnchorFrame:SetFrameLevel(plateFrame.UnitFrame.healthBar:GetFrameLevel()+1)
				end)
				
				
				-- this is for classic cast bars on blizzard default nameplates and frame anchor
				if (not IS_WOW_PROJECT_MAINLINE) then
					if onlyNames 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] = plateFrame.unitFrame
				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, plateFrame.unitFrame)
			-- 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
			---@type unitframe
			local unitFrame = plateFrame.unitFrame
			---@type castbar
			local castBar = unitFrame.castBar
			---@type healthbar
			local healthBar = unitFrame.healthBar

			plateFrame.PlaterAnchorFrame:ClearAllPoints()
			plateFrame.PlaterAnchorFrame:SetParent(healthBar)
			plateFrame.PlaterAnchorFrame:SetPoint("topright", healthBar, "topright")
			plateFrame.PlaterAnchorFrame:SetPoint("bottomleft", healthBar, "bottomleft")
			plateFrame.PlaterAnchorFrame:Show()

			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
				if not IS_WOW_PROJECT_MIDNIGHT then
					plateFrame:HookScript("OnSizeChanged", Plater.UpdateUIParentScale)
				end
				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.
			
			plateFrame.actorType = actorType
			unitFrame.actorType = actorType
			unitFrame.ActorType = actorType --exposed to scripts
			
			--set the unit
			unitFrame:SetUnit (unitID)
			
			--reset performance unit
			unitFrame.isPerformanceUnit = nil
			unitFrame.isPerformanceUnitAura = nil
			unitFrame.isPerformanceUnitThreat = nil
			unitFrame.isPerformanceUnitCast = nil
			unitFrame.healthBar.isPerformanceUnit = nil
			
			if (Plater.PerformanceUnits[plateFrame[MEMBER_NPCID]]) then
				local perfUnitData = tonumber(Plater.PerformanceUnits[plateFrame[MEMBER_NPCID]]) or 0
				--print("perf", plateFrame[MEMBER_NPCID])
				unitFrame.isPerformanceUnit = true
				unitFrame.isPerformanceUnitAura = bit.band(perfUnitData, platerInternal.PERF_UNIT_OVERRIDES_BIT.AURA) == 0 and true or false
				unitFrame.isPerformanceUnitThreat = bit.band(perfUnitData, platerInternal.PERF_UNIT_OVERRIDES_BIT.THREAT) == 0 and true or false
				unitFrame.isPerformanceUnitCast = bit.band(perfUnitData, platerInternal.PERF_UNIT_OVERRIDES_BIT.CAST) == 0 and true or false
				unitFrame.healthBar.isPerformanceUnit = true
				
				if unitFrame.isPerformanceUnitCast then
					unitFrame.castBar:SetUnit(nil) -- no casts
				end
				if unitFrame.isPerformanceUnitAura then
					Plater.RemoveFromAuraUpdate (unitID, unitFrame) -- no auras
				end
			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 and (not IS_WOW_PROJECT_MIDNIGHT or (IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(unitName))) then
				unitNameTranslit = LibTranslit:Transliterate(unitName, TRANSLIT_MARK)
			end
			plateFrame [MEMBER_NAME] = unitNameTranslit
			plateFrame [MEMBER_NAMELOWER] = (IS_WOW_PROJECT_MIDNIGHT and plateFrame [MEMBER_NAME] or "") or lower (plateFrame [MEMBER_NAME])
			plateFrame ["namePlateClassification"] = UnitClassification (unitID)
			plateFrame.namePlateIsBossMob = UnitIsBossMob and UnitIsBossMob(unitID)
			plateFrame.namePlateIsLieutenant = UnitIsLieutenant and UnitIsLieutenant(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.namePlateIsBossMob = plateFrame.namePlateIsBossMob
			unitFrame.namePlateIsLieutenant = plateFrame.namePlateIsLieutenant
			unitFrame.unitNameInternal = unitName
			unitFrame [MEMBER_UNITID] = unitID
			unitFrame.namePlateThreatPercent = 0
			unitFrame.namePlateThreatIsTanking = nil
			unitFrame.namePlateThreatStatus = nil
			unitFrame.namePlateThreatOffTankIsTanking = false
			unitFrame.namePlateThreatOffTankName = nil
			unitFrame.isGoodAggroState = true
			
			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.healthCutOffUpper:Hide()
			healthBar.executeRange:Hide()
			healthBar.executeRangeUpper: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.QuickHealthUpdate (unitFrame) -- ensure up to date, for good measure
					Plater.OnUpdateHealth (healthBar, true)

				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 (not isObject and (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 or npcCacheInfo[2] == "UNKNOWN") then
											--the npc is cached but the language is different or zone unknown -> update.
											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)
							if IS_WOW_PROJECT_MIDNIGHT then
								threatStatus = UnitThreatSituation ("player", unitID)
								isTanking = (threatStatus or 0) >= 2
							end
							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
			
			--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)
					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.isPerformanceUnitThreat
			
			--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) -- 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)
			if (not plateFrame or not plateFrame.unitFrame) then
				return
			end
			
			Plater.RemoveFromAuraUpdate (unitBarId, plateFrame.unitFrame) -- ensure no updates
			
			plateFrame.PlaterAnchorFrame:ClearAllPoints()
			plateFrame.PlaterAnchorFrame:SetParent(plateFrame)
			local enemyHealthSize = Plater.db.profile.plate_config.enemynpc and Plater.db.profile.plate_config.enemynpc.health or {112, 12}
			plateFrame.PlaterAnchorFrame:SetSize(enemyHealthSize[1] or 112, enemyHealthSize[2] or 12)
			
			if (plateFrame.unitFrame.blizzardPlateFrameID) then
				ENABLED_BLIZZARD_PLATEFRAMES[plateFrame.unitFrame.blizzardPlateFrameID] = true -- OnRetailNamePlateShow is called first. ensure the plate might show!
			end
			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

			--remove private aura anchors
			Plater.HandlePrivateAuraAnchors(plateFrame)
			
			--reset auras
			Plater.ResetAuraContainer (plateFrame.unitFrame.BuffFrame, true, true)
			Plater.HideNonUsedAuraIcons (plateFrame.unitFrame.BuffFrame)
			
			--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.UnitFrame)
				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()
			--this is to work around UPDATE_SHAPESHIFT_FORM firing for all units and not just the player... causing lag...
			if platerInternal.last_GetShapeshiftFormID == GetShapeshiftFormID() then
				return
			end
			platerInternal.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)
	--events
	Plater.EventHandlerFrame:RegisterEvent ("PLAYER_ENTERING_WORLD")
	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")
	
	Plater.EventHandlerFrame:RegisterEvent ("PLAYER_SOFT_INTERACT_CHANGED")
	Plater.EventHandlerFrame:RegisterEvent ("PLAYER_SOFT_FRIEND_CHANGED")
	Plater.EventHandlerFrame:RegisterEvent ("PLAYER_SOFT_ENEMY_CHANGED")
	
	Plater.EventHandlerFrame:RegisterEvent ("PLAYER_REGEN_DISABLED")
	Plater.EventHandlerFrame:RegisterEvent ("PLAYER_REGEN_ENABLED")
	
	Plater.EventHandlerFrame:RegisterEvent ("PLAYER_LOGIN")
	Plater.EventHandlerFrame:RegisterEvent ("VARIABLES_LOADED")	
	Plater.EventHandlerFrame:RegisterEvent ("ADDON_LOADED")
	
	
	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
	---@param self frame blizzard unitframe 'plateFrame.UnitFrame'
	function Plater.OnRetailNamePlateShow (self) --private
		if ENABLED_BLIZZARD_PLATEFRAMES[tostring(self)] then
			-- do not hide
			return
		end
		
		--print(GetTime(), self.unit, SUPPORT_BLIZZARD_PLATEFRAMES, ENABLED_BLIZZARD_PLATEFRAMES[tostring(self)])
		--DevTool:AddData({debugstack()})
		
		--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
		elseif IS_WOW_PROJECT_MIDNIGHT then
			self:SetAlpha(0)
			local hiddenParent = platerInternal.hiddenParentFrame
			if self.AurasFrame then -- assume this suffices
				self.AurasFrame.DebuffListFrame:SetParent(hiddenParent)
				self.AurasFrame.BuffListFrame:SetParent(hiddenParent)
				self.AurasFrame.CrowdControlListFrame:SetParent(hiddenParent)
				self.AurasFrame.LossOfControlFrame:SetParent(hiddenParent)
				for _, key in ipairs(platerInternal.blizzNameplateObjects) do
					self[key]:SetParent(hiddenParent)
				end
				platerInternal.reparentedUnitFrames[self.unit] = self
			end
		else
			self:Hide()
		end
		
		
		if not SUPPORT_BLIZZARD_PLATEFRAMES then -- due to aura tracking currently not advised in midnight...
			-- should be done if events are not needed
			-- CompactUnitFrame_UnregisterEvents only removes event hanlder functions
			if not Plater.db.profile.aura_show_debuff_as_blizzard_does then
				self:UnregisterAllEvents()
			end
			if self.HealthBarsContainer and self.HealthBarsContainer.healthBar then self.HealthBarsContainer.healthBar:UnregisterAllEvents() end
			if self.castBar then self.castBar:UnregisterAllEvents() end
		end
		
		if (CompactUnitFrame_UnregisterEvents) then
			if not Plater.db.profile.aura_show_debuff_as_blizzard_does then
				CompactUnitFrame_UnregisterEvents (self)
			end
			if self.HealthBarsContainer and self.HealthBarsContainer.healthBar then CompactUnitFrame_UnregisterEvents(self.HealthBarsContainer.healthBar) end
			if self.castBar then CompactUnitFrame_UnregisterEvents (self.castBar) end
		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
	
	-- Inspired by and mostly copied from https://github.com/Cidan/BetterBags/pull/934/, thanks Cidan
	-- PatchWerk breaks WoW addons by patching _G functions and overwriting addon code.
	-- Detect it early and force the user to disable it.
	C_Timer.After(5, function()
		if C_AddOns.IsAddOnLoaded("PatchWerk") then
		StaticPopupDialogs["PLATER_PATCHWERK_CONFLICT"] = {
			text = "Plater has detected that the addon |cFFFF4400PatchWerk|r is loaded.\n\nPatchWerk potentially breaks every WoW addon, even addons it is not configured to patch, by modifying available WoW base API.\nYou must disable PatchWerk entirely for Plater and other addons to work correctly.\n\nYou may ignore this warning at your own risk, in which case Plater will be disabled for compatibility issues with PatchWerk.\nYou may re-enable Plater in your Addons once you have disable PatchWerk.\n\nClick 'Disable & Reload' to disable PatchWerk and reload your UI or 'Ignore' to disable Plater.",
			button1 = "Disable & Reload",
			button2 = "Ignore",
			timeout = 0,
			whileDead = true,
			hideOnEscape = false,
			preferredIndex = 3,
			OnAccept = function()
				C_AddOns.DisableAddOn("!PatchWerk")
				C_AddOns.DisableAddOn("PatchWerk")
				ReloadUI()
			end,
			OnCancel = function()
				C_AddOns.DisableAddOn("Plater")
				ReloadUI()
			end,
		}
		StaticPopup_Show("PLATER_PATCHWERK_CONFLICT")
		end
	end)

	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(true)
	hooksecurefunc(LibSharedMedia, 'Register', function(_, mediaType, key, data)
		if not mediaType or type(mediaType) ~= 'string' then return end
		if mediaType:lower() == 'font' then
			Plater.UpdateBlizzardNameplateFonts(key, true)
		end
	end)
	
	-- 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
				if not Plater.db.profile.use_ui_parent or Plater.db.profile.ui_parent_scale_tune == 0 then
					Plater.db.profile.use_ui_parent = true
					--adjust the fine tune to player's screen scale
					Plater.db.profile.ui_parent_scale_tune = 1 / (IS_WOW_PROJECT_MIDNIGHT and 1 or UIParent:GetEffectiveScale())
					Plater.db.profile.use_ui_parent_just_enabled = false
					Plater.RefreshDBUpvalues()
					Plater.UpdateAllPlates()
				end
				
			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 or IS_WOW_PROJECT_CLASSIC_MOP 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 or IS_WOW_PROJECT_CLASSIC_MOP 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")
--		
--		Plater.EventHandlerFrame:RegisterEvent ("ADDON_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 or IS_WOW_PROJECT_MIDNIGHT) 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 and 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 and NamePlateDriverFrame then
			hooksecurefunc (NamePlateDriverFrame, "SetupClassNameplateBars", function (self)
				return Plater.UpdatePersonalBar (self)
			end)
			
			if IS_WOW_PROJECT_MIDNIGHT then -- MIDNIGHT!! fucking bullshit workaround for SetAlpha(0) instead of :Hide() until we have proper click-frame control...
				platerInternal.reparentedUnitFrames = {}
				platerInternal.hiddenParentFrame = CreateFrame("Frame")
				platerInternal.hiddenParentFrame:Hide()
				platerInternal.blizzNameplateObjects = {
					"aggroHighlight",
					"aggroHighlightAdditive",
					"aggroHighlightBase",
					"castBar",
					"name",
					"AurasFrame",
					"ClassificationFrame",
					"HealthBarsContainer",
					"PlayerLevelDiffFrame",
					"RaidTargetFrame",
					"SoftTargetFrame",
				}
				-- the hidden parent is set in OnRetailNameplateShow
				hooksecurefunc(NamePlateDriverFrame, "OnNamePlateRemoved", function(_, unit)
					local blizzUnitFrame = platerInternal.reparentedUnitFrames[unit]
					if blizzUnitFrame then
						blizzUnitFrame.AurasFrame.DebuffListFrame:SetParent(blizzUnitFrame)
						blizzUnitFrame.AurasFrame.BuffListFrame:SetParent(blizzUnitFrame)
						blizzUnitFrame.AurasFrame.CrowdControlListFrame:SetParent(blizzUnitFrame)
						blizzUnitFrame.AurasFrame.LossOfControlFrame:SetParent(blizzUnitFrame)
						for _, key in ipairs(platerInternal.blizzNameplateObjects) do
							blizzUnitFrame[key]:SetParent(blizzUnitFrame)
						end
						platerInternal.reparentedUnitFrames[unit] = nil
					end
				end)
				
				--Nameplate base options tables: NamePlateFriendlyFrameOptions / NamePlateEnemyFrameOptions
				hooksecurefunc(NamePlateDriverFrame, "OnNamePlateAdded", function(_, unit)
					if not unit:match("^nameplate") then return end
					local profile = Plater.db.profile
					if not profile.blizzard_nameplate_font_override_enabled then return end
					local plateFrame = C_NamePlate.GetNamePlateForUnit(unit, issecure())
					if not plateFrame then
						Plater.UpdateBlizzardNameplateFonts(true)
					else
						DF:SetFontFace (plateFrame.UnitFrame.name, profile.blizzard_nameplate_font)
						DF:SetFontOutline (plateFrame.UnitFrame.name, profile.blizzard_nameplate_font_outline)
						DF:SetFontSize (plateFrame.UnitFrame.name, profile.blizzard_nameplate_font_size)
					end
				end)
				hooksecurefunc(NamePlatePreviewMixin, "ShowPreviewNamePlateCastBar", function()
					if not Plater.db.profile.blizzard_nameplate_font_override_enabled then return end
					Plater.UpdateBlizzardNameplateFonts(true)
					C_Timer.After(0.1, function ()
						Plater.UpdateBlizzardNameplateFonts(true)
					end)
				end)
				hooksecurefunc(NamePlateDriverFrame, "UpdateNamePlateSize", function()
					if not Plater.db.profile.blizzard_nameplate_font_override_enabled then return end
					Plater.UpdateBlizzardNameplateFonts(true)
					C_Timer.After(0.1, function ()
						Plater.UpdateBlizzardNameplateFonts(true)
					end)
				end)
				hooksecurefunc(NamePlateUnitFrameMixin, "UpdateNameClassColor", function(self)
					local plateFrame = C_NamePlate.GetNamePlateForUnit(self.unit)
					if not plateFrame then -- secure in dungeon
						--local onlyNamesEnabled = GetCVarBool("nameplateShowOnlyNames") or GetCVarBool("nameplateShowOnlyNameForFriendlyPlayerUnits")

						if not UnitIsPlayer(self.unit) then
							if Plater.db.profile.hide_friendly_npc_healthbar then
								TextureLoadingGroupMixin.AddTexture({ textures = self.HealthBarsContainer.healthBar }, "showOnlyName")
								TextureLoadingGroupMixin.AddTexture({ textures = self.castBar }, "showOnlyName")
								TextureLoadingGroupMixin.AddTexture({ textures = self.castBar }, "widgetsOnly")
							else
								TextureLoadingGroupMixin.RemoveTexture({ textures = self.HealthBarsContainer.healthBar }, "showOnlyName")
								TextureLoadingGroupMixin.RemoveTexture({ textures = self.castBar }, "showOnlyName")
								TextureLoadingGroupMixin.RemoveTexture({ textures = self.castBar }, "widgetsOnly")
								--TextureLoadingGroupMixin.RemoveTexture({ textures = self }, "explicitIsPlayer")
							end
							--TextureLoadingGroupMixin.AddTexture({ textures = self }, "explicitIsPlayer")
						end
						TextureLoadingGroupMixin.AddTexture({ textures = self.optionTable }, "colorNameBySelection")
					end
				end)
				hooksecurefunc(NamePlateUnitFrameMixin, "UpdateIsFriend", function(self)
					local plateFrame = C_NamePlate.GetNamePlateForUnit(self.unit)
					if not plateFrame and not self:IsFriend() then
						TextureLoadingGroupMixin.RemoveTexture({ textures = self }, "isPlayer")
					end
				end)
				hooksecurefunc(NamePlateUnitFrameMixin, "OnUnitSet", function(self)
					local plateFrame = C_NamePlate.GetNamePlateForUnit(self.unit)
					if not plateFrame then -- secure in dungeon
						local onlyNamesEnabled = GetCVarBool("nameplateShowOnlyNames") or GetCVarBool("nameplateShowOnlyNameForFriendlyPlayerUnits")
						if onlyNamesEnabled then
							TextureLoadingGroupMixin.AddTexture({ textures = self }, "showOnlyName")
						end
					end
				end)
			end
			
		end

		--update the resource location and anchor
		function Plater.UpdateResourceFrame()
			--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 or IS_WOW_PROJECT_MIDNIGHT) then
				if (not onCurrentTarget) then
					return
				end
			end
			
			local resourceFrame = NamePlateDriverFrame and 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 and 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
		
		-- do this now
		Plater.UpdateBaseNameplateOptions()
		
		--this function is declared inside 'NamePlateDriverMixin' at Blizzard_NamePlates.lua
		if NamePlateDriverFrame and NamePlateDriverFrame.UpdateNamePlateOptions then
			hooksecurefunc (NamePlateDriverFrame, "UpdateNamePlateOptions", function()
				Plater.UpdateSelfPlate()
				Plater.UpdateBaseNameplateOptions()
				Plater.UpdatePlateClickSpace()
				C_Timer.After(0.1, function ()
					Plater.UpdateBlizzardNameplateFonts(true)
				end)
			end)
		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)
				end
			else
				C_Timer.After(1, function() Plater.SetNamePlatePreferredClickInsets(nameplateType, left, right, top, bottom) end)
			end
		end
		if not IS_WOW_PROJECT_MIDNIGHT and NamePlateDriverFrame and NamePlateDriverFrame.namePlateSetInsetFunctions then
			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)
		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)
				local curTime = GetTime()

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

				castBar.spellName = 		spellName
				castBar.spellID = 			116
				castBar.spellTexture = 		spellIcon
				castBar.spellStartTime = 	curTime
				castBar.spellEndTime = 		curTime + castBar.maxValue
				
				castBar.SpellStartTime = 	curTime
				castBar.SpellEndTime = 		curTime + castBar.maxValue
				
				castBar.playedFinishedTest = nil
				
				if IS_WOW_PROJECT_MIDNIGHT then
					local durationObject = C_DurationUtil.CreateDuration()
					durationObject:SetTimeFromEnd(castBar.spellEndTime , castBar.maxValue, 1)
					castBar.durationObject = durationObject
					if (castBar.channeling) then
						castBar:SetTimerDuration(durationObject, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.RemainingTime)
					else
						castBar:SetTimerDuration(durationObject, Enum.StatusBarInterpolation.Immediate, Enum.StatusBarTimerDirection.ElapsedTime)
					end
					C_Timer.NewTimer(castBar.maxValue, function()
						if (Plater.IsShowingCastBarTest) then
							castBar:CheckCastIsDone(nil, true)
						end
					end)
				end
				
				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, targetFontString)
			local profile = Plater.db.profile
			
			if (profile.castbar_target_show) then
				local textString = targetFontString or 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)
				Plater.SetTextAlignmentFromAnchor(textString, profile.castbar_target_anchor)
			else
				if targetFontString then
					targetFontString:Hide()
				end
				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)

					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 = Plater.GetHashKey (isInCombat)
					local healthBarHeight = unitFrame.customHealthBarHeight or (plateConfigs and plateConfigs [healthBarConfigKey][2]) or 0
					castBarHeight = unitFrame.customCastBarHeight or (plateConfigs and plateConfigs [castBarConfigKey][2]) or 0
					local castBarOffSetY = plateConfigs and plateConfigs.castbar_offset or 0

					local height
					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)
							
							height = castBarHeight

						elseif (profile.castbar_icon_size == "same as castbar plus healthbar") then
							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)
								height = castBarHeight + healthBarHeight - castBarOffSetY
							else
								icon:SetPoint("topright", unitFrame.healthBar, "topleft", profile.castbar_icon_x_offset, 0)
								icon:SetPoint("bottomright", castBar, "bottomleft", profile.castbar_icon_x_offset, 0)
								height = castBarHeight + healthBarHeight + castBarOffSetY
							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)
							
							height = castBarHeight

						elseif (profile.castbar_icon_size == "same as castbar plus healthbar") then
							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)
								height = castBarHeight + healthBarHeight - castBarOffSetY
							else
								icon:SetPoint("topleft", unitFrame.healthBar, "topright", profile.castbar_icon_x_offset, 0)
								icon:SetPoint("bottomleft", castBar, "bottomright", profile.castbar_icon_x_offset, 0)
								height = castBarHeight + healthBarHeight + castBarOffSetY
							end
							
							PixelUtil.SetPoint (borderShield, "center", castBar, "right", 0, 0)
						end
					end

					--print(icon:GetHeight(), issecretvalue(icon:GetHeight()))
					if IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(icon:GetHeight()) then
						icon:SetWidth(icon:GetHeight())
					else
						icon:SetWidth(height)
					end

					castBar:UpdateInterruptState() -- ensure icon is shown as appropriate
				else
					icon:Hide()
					borderShield:SetTexture (nil)
					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)

				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)
				PixelUtil.SetPoint (borderShield, "center", castBar, "left", 0, 0)

				castBar:UpdateInterruptState() -- ensure icon is shown as appropriate
			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

		function Plater.SetCastBarInterruptedState(unitFrame, sourceGUID, name)
			if not Plater.db.profile.show_interrupt_author then return end
			if not unitFrame or not sourceGUID then return end
			local castBar = unitFrame.castBar
			
			local coloredName
			if IS_WOW_PROJECT_MIDNIGHT or not name then
				local _, class, _, race, _, name, realm = GetPlayerInfoByGUID(sourceGUID)
				local classColor
				if class then
					classColor = C_ClassColor.GetClassColor(class)
				end
				if not name then
					name = UnitNameFromGUID(sourceGUID)
				end
				if classColor then
					coloredName = classColor:WrapTextInColorCode(name)
				else
					coloredName = name
				end
			else
				coloredName = Plater.SetTextColorByClass (sourceGUID, name)
			end
			
			castBar.Text:SetText (INTERRUPTED .. " [" .. coloredName .. "]")
			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
		--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 not IS_WOW_PROJECT_MIDNIGHT or (IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(self.SpellName)) then
						-- 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
					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.SpellNameRenamed = self.spellName
					self.SpellID = 		self.spellID
					self.SpellTexture = 	self.spellTexture
					if IS_WOW_PROJECT_MIDNIGHT then
						if type(self.spellStartTime) ~= "nil" then
							self.SpellStartTime = self.spellStartTime
						else
							self.spellStartTime = GetTimePreciseSec() * 1000
							self.SpellStartTime = self.spellStartTime
						end
						if type(self.SpellEndTime) ~= "nil" then
							self.SpellEndTime = self.spellEndTime
						else
							self.SpellEndTime = GetTimePreciseSec() * 1000
							self.SpellEndTime = self.SpellEndTime
						end
					else
						self.SpellStartTime = self.spellStartTime or curTime
						self.SpellEndTime = self.spellEndTime or curTime
					end
					
					self.IsInterrupted = false
					self.InterruptSourceName = nil
					self.InterruptSourceGUID = nil
					self.ReUpdateNextTick = true
					self.ThrottleUpdate = -1
					
					--castbar icon
					Plater.UpdateCastbarIcon(self)

					if IS_WOW_PROJECT_MIDNIGHT then
						self.CanInterrupt = nil
					else
						self.CanInterrupt = not self.notInterruptible
					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())

					Plater.UpdateCastbarTargetText (self)

					shouldRunCastStartHook = true

					--spell color
					self.castColorTexture:Hide()

					--cast color (from options tab Cast Colors)
					if not IS_WOW_PROJECT_MIDNIGHT or (IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(self.spellID)) then
						local castColors = profile.cast_colors
						local customColor = castColors[self.spellID]
						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

								--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
							
							if (customSpellName and customSpellName ~= "") then
								self.SpellNameRenamed = customSpellName
							end
						end
					
					
						if (self.SpellNameRenamed == self.SpellName or Plater.db.profile.bossmod_castrename_priority) and Plater.db.profile.bossmod_support_enabled 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.SpellNameRenamed = bmSpellName
							end
						end
					end
					
					if not IS_WOW_PROJECT_MIDNIGHT and (self.channeling and (self.SpellStartTime + 0.25 > curTime)) then
						platerInternal.Audio.PlaySoundForCastStart(self.spellID) --fallback for edge cases. should not double play
					end
					
					--cut the spell name text to fit within the castbar
					local plateConfig = DB_PLATE_CONFIG [unitFrame.ActorType] or {}
					Plater.UpdateTextSize (self.SpellNameRenamed or "", self.Text, plateConfig.spellname_text_max_width or 0, nil, plateConfig.spellname_text_wrap)
					
					-- in some occasions channeled casts don't have a CLEU entry... check this here
					if not IS_WOW_PROJECT_MIDNIGHT and (unitFrame.ActorType == "enemynpc" and event == "UNIT_SPELLCAST_CHANNEL_START" and (not DB_CAPTURED_SPELLS[self.spellID] or DB_CAPTURED_SPELLS[self.spellID].isChanneled == nil or not DB_CAPTURED_CASTS[self.spellID] or DB_CAPTURED_CASTS[self.spellID].isChanneled == nil)) then
						parserFunctions.SPELL_CAST_SUCCESS (nil, "SPELL_CAST_SUCCESS", nil, unitFrame[MEMBER_GUID], unitFrame.unitNameInternal, 0x00000040, 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
					
					Plater.SetCastBarInterruptedState(unitFrame, self.interruptedBy)

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

					if self.interruptedBy ~= nil then
						Plater.SetCastBarInterruptedState(unitFrame, self.interruptedBy)
						self.IsInterrupted = true
					else
						self:OnHideWidget()
						if (Plater.db.profile.cast_statusbar_quickhide) then
							self:Hide()
						end
					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 = not IS_WOW_PROJECT_MIDNIGHT and (self.SpellEndTime - self.SpellStartTime) or nil
							scriptEnv._StartTime = self.SpellStartTime
							scriptEnv._CanInterrupt = self.CanInterrupt
							scriptEnv._CannotInterrupt = self.notInterruptible
							scriptEnv._EndTime = self.SpellEndTime
							scriptEnv._RemainingTime = not IS_WOW_PROJECT_MIDNIGHT and max (self.SpellEndTime - GetTime(), 0) or nil
							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
			---@cast self castbar
			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")
				
					if (self.ReUpdateNextTick) then
						self.ReUpdateNextTick = nil
					end
					
					if (self.unit and Plater.db.profile.castbar_target_show and not self.unitFrame.IsSelf) then
						if IS_WOW_PROJECT_MIDNIGHT then
							local targetName = UnitSpellTargetName(self.unit)
							if targetName then
								local targetNameShort = Ambiguate(targetName, "none")
								if targetNameShort then
									targetName = targetNameShort
								end
								local classFilename = UnitSpellTargetClass(self.unit)
								if classFilename then
									local color = C_ClassColor.GetClassColor(classFilename)
									targetName = C_ColorUtil.WrapTextInColor(targetName, color)
								end
								
								targetName = Plater.UpdateTextSize (targetName or "", self.FrameOverlay.TargetName, Plater.db.profile.castbar_target_text_max_width or 0, nil, Plater.db.profile.castbar_target_text_wrap)
								
							else
								self.FrameOverlay.TargetName:SetText(nil)
							end
						else
							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 and (not IS_WOW_PROJECT_MIDNIGHT or (IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(targetName))) then
										targetName = LibTranslit:Transliterate(targetName, TRANSLIT_MARK)
									end
									
									targetName = Plater.UpdateTextSize (targetName or "", self.FrameOverlay.TargetName, Plater.db.profile.castbar_target_text_max_width or 0, nil, Plater.db.profile.castbar_target_text_wrap)

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

					if not IS_WOW_PROJECT_MIDNIGHT or (IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(self.SpellName)) then
						--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 not IS_WOW_PROJECT_MIDNIGHT and (globalScriptObject and self.SpellEndTime and GetTime() < self.SpellEndTime and (self.casting or self.channeling) and not self.IsInterrupted) then
						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 = not IS_WOW_PROJECT_MIDNIGHT and (self.SpellEndTime - self.SpellStartTime) or nil
							scriptEnv._StartTime = self.SpellStartTime
							scriptEnv._CanInterrupt = self.CanInterrupt
							scriptEnv._EndTime = self.SpellEndTime
							scriptEnv._RemainingTime = not IS_WOW_PROJECT_MIDNIGHT and max (self.SpellEndTime - GetTime(), 0) or nil
							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 = math.abs (self.value - self.maxValue) / self.maxValue * 100
							end
						
							--run onupdate script
							self:ScriptRunOnUpdate (scriptInfo)
						end
					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 = not IS_WOW_PROJECT_MIDNIGHT and (self.SpellEndTime - self.SpellStartTime) or nil
							scriptEnv._StartTime = self.SpellStartTime
							scriptEnv._CanInterrupt = self.CanInterrupt
							scriptEnv._EndTime = self.SpellEndTime
							scriptEnv._RemainingTime = not IS_WOW_PROJECT_MIDNIGHT and max (self.SpellEndTime - GetTime(), 0) or nil
							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)
		Plater.StartLogPerformanceCore("Plater-Core", "Health", "QuickHealthUpdate")
		if IS_WOW_PROJECT_MIDNIGHT then
			--unitFrame.healthBar.currentHealthMissing = UnitHealthMissing(unitFrame.unit, true)
			--unitFrame.healthBar.currentHealthPercent = UnitHealthPercent(unitFrame.unit, true, CurveConstants.ScaleTo100)
		else
			local unitHealth = UnitHealth (unitFrame.unit)
			local unitHealthMax = UnitHealthMax (unitFrame.unit)
			unitFrame.healthBar:SetMinMaxValues (0, unitHealthMax, IS_WOW_PROJECT_MIDNIGHT and Enum.StatusBarInterpolation.ExponentialEaseOut)
			unitFrame.healthBar:SetValue (unitHealth, IS_WOW_PROJECT_MIDNIGHT and Enum.StatusBarInterpolation.ExponentialEaseOut)
		
			unitFrame.healthBar.currentHealth = unitHealth
			unitFrame.healthBar.currentHealthMax = unitHealthMax
			unitFrame.healthBar.currentHealthMissing = unitHealthMax - unitHealth
			unitFrame.healthBar.currentHealthPercent = unitHealth / unitHealthMax * 100
		end
		Plater.EndLogPerformanceCore("Plater-Core", "Health", "QuickHealthUpdate")
	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, forceUpdate) --self is unitFrame.healthBar
		if (not self.isNamePlate) then
			--this is not a nameplate, perhaps another frame from the framework
			return
		end
		if (not forceUpdate and not self.healthChanged) then 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?! - do we really need this?
			if not self.currentHealthMax then
				local maxHealth = UnitHealthMax (self.displayedUnit)
				self:SetMinMaxValues (0, maxHealth)
				self.currentHealthMax = maxHealth
			end
		end

		---@type plateframe
		local plateFrame = self.PlateFrame
		local currentHealth = self.currentHealth
		local currentHealthMax = self.currentHealthMax
		local unitFrame = self.unitFrame
		local oldHealth = self.currentHealth
		if not IS_WOW_PROJECT_MIDNIGHT then
			oldHealth = oldHealth or currentHealth
		end
		if IS_WOW_PROJECT_MIDNIGHT then
			--these should be set already...
			--self.currentHealthMissing = UnitHealthMissing(unitFrame.displayedUnit, true)
			--self.currentHealthPercent = UnitHealthPercent(unitFrame.displayedUnit, true, CurveConstants.ScaleTo100)
		else
			self.currentHealthMissing = currentHealthMax - currentHealth
			self.currentHealthPercent = currentHealth / currentHealthMax * 100
		end
		
		-- some backwards compatibility for older mods/scripts
		self.CurrentHealth = self.currentHealth
		self.CurrentHealthMax = self.currentHealthMax
	
		if (plateFrame.IsSelf) then
		
			--> 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 (math.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) and not UnitIsVisible(unitFrame.unit)) or (not IS_WOW_PROJECT_MIDNIGHT and self.currentHealth < 1)) then
					--the unit died!
					unitFrame:Hide()
					Plater.EndLogPerformanceCore("Plater-Core", "Health", "OnUpdateHealth")
					return
				end
			end
			
			if (DB_DO_ANIMATIONS and unitFrame.PlaterOnScreen and oldHealth ~= currentHealth) then -- hard disabled for midnight already
				--do healthbar animation ~animation ~healthbar
				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
			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
			
			if self.IsAnimating then
				self.AnimateFunc(self, 999)
			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)
		
		-- some backwards compatibility for older mods/scripts
		self.CurrentHealth = self.currentHealth
		self.CurrentHealthMax = self.currentHealthMax
		
		Plater.CheckLifePercentText (self.unitFrame)
		
		Plater.EndLogPerformanceCore("Plater-Core", "Health", "OnUpdateHealthMax")
	end

	function Plater.OnHealthChange (self, unitId) --~health
		self.healthChanged = true
		Plater.OnUpdateHealth (self)
		
		--> run on health changed hook
		if (HOOK_HEALTH_UPDATE.ScriptAmount > 0) then
			return run_on_health_change_hook (self.unitFrame)
		end
		self.healthChanged = false
	end
	
	function Plater.OnHealthMaxChange (self, unitId)
		self.healthChanged = true
		Plater.OnUpdateHealthMax (self)
		
		--> run on health changed hook
		if (HOOK_HEALTH_UPDATE.ScriptAmount > 0) then
			return run_on_health_change_hook (self.unitFrame)
		end
		self.healthChanged = false
	end
	
	--> profile changes and refreshes ~db
		Plater.db.RegisterCallback (Plater, "OnProfileChanged", "RefreshConfigProfileChanged")
		Plater.db.RegisterCallback (Plater, "OnProfileCopied", "RefreshConfig")
		Plater.db.RegisterCallback (Plater, "OnProfileReset", "RefreshConfigProfileReset")
		--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()
				
				--this setting is disabled by default and will be enabled for new users and new profiles
				if not Plater.db.profile.use_ui_parent or Plater.db.profile.ui_parent_scale_tune == 0 then
					--enable UIParent nameplates for new installs of Plater
					Plater.db.profile.use_ui_parent = true
					--adjust the fine tune to player's screen scale
					Plater.db.profile.ui_parent_scale_tune = 1 / (IS_WOW_PROJECT_MIDNIGHT and 1 or UIParent:GetEffectiveScale())
					Plater.RefreshDBUpvalues()
					Plater.UpdateAllPlates()
				end
				
				--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] = {}
			local GetNumSpecializationsForClassID = GetNumSpecializationsForClassID or C_SpecializationInfo.GetNumSpecializationsForClassID --10.0.5
			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', function()
			Plater.UpdatePlateClickSpace()
			C_Timer.After(0.1, function ()
				Plater.UpdateBlizzardNameplateFonts(true)
			end)
		end)
		VideoOptionsFrame:HookScript('OnHide', function()
			Plater.UpdatePlateClickSpace()
			C_Timer.After(0.1, function ()
				Plater.UpdateBlizzardNameplateFonts(true)
			end)
		end)
		InterfaceOptionsFrame:HookScript('OnShow', function()
			Plater.UpdatePlateClickSpace()
			C_Timer.After(0.1, function ()
				Plater.UpdateBlizzardNameplateFonts(true)
			end)
		end)
		VideoOptionsFrame:HookScript('OnShow', function()
			Plater.UpdatePlateClickSpace()
			C_Timer.After(0.1, function ()
				Plater.UpdateBlizzardNameplateFonts(true)
			end)
		end)
	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]
		unitFrame.hasUnitTypeColor = nil

		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)
				
			--elseif Plater.db.profile.unit_type_coloring_enabled and (Plater.ZoneInstanceType == "party" or Plater.ZoneInstanceType == "raid") and unitFrame.isGoodAggroState then
			elseif Plater.db.profile.unit_type_coloring_enabled and (Plater.ZoneInstanceType == "party" or Plater.ZoneInstanceType == "raid") then
				local pLevel = UnitEffectiveLevel("player")
                local uLevel = UnitEffectiveLevel(unitID)

				--boss
				if uLevel == pLevel + 2 or uLevel == -1 then
					r, g, b, a = unpack (Plater.db.profile.unit_type_coloring_boss)
					unitFrame.hasUnitTypeColor = true

				--miniboss
				elseif uLevel == pLevel + 1 or unitFrame.namePlateIsBossMob or unitFrame.namePlateIsLieutenant then
					r, g, b, a = unpack (Plater.db.profile.unit_type_coloring_miniboss)
					unitFrame.hasUnitTypeColor = true

				--caster
				elseif UnitClassBase(unitID) == "PALADIN" then
					r, g, b, a = unpack (Plater.db.profile.unit_type_coloring_caster)
					unitFrame.hasUnitTypeColor = true

				--elite
				elseif Plater.db.profile.unit_type_coloring_enable_elite and (unitFrame.namePlateClassification == "elite" or unitFrame.namePlateClassification == "rareelite") then
					r, g, b, a = unpack (Plater.db.profile.unit_type_coloring_elite)
					unitFrame.hasUnitTypeColor = true

				--trivial
				elseif Plater.db.profile.unit_type_coloring_enable_trivial then
					r, g, b, a = unpack (Plater.db.profile.unit_type_coloring_trivial)
					unitFrame.hasUnitTypeColor = true

				elseif (Plater.CanOverrideColor) then
					Plater.ColorOverrider (unitFrame, forceRefresh)
					return

				else
					r, g, b, a = UnitSelectionColor (unitID)
				end
				--maybe additional caster:
                --local powerMax = UnitPowerMax(unitID, Enum.PowerType.Mana)
                --local isCaster = UnitClassBase(unitID) == "PALADIN" or (not issecretvalue(powerMax) and powerMax > 0)
			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.currentHealthPercent
				
				--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.isPerformanceUnitAura then
					Plater.AddToAuraUpdate(plateFrame.unitFrame.unit, plateFrame.unitFrame) -- force aura update
				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] or plateFrame.unitFrame [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 IS_WOW_PROJECT_MIDNIGHT then return end
		if (InCombatLockdown()) then
			return C_Timer.After (.3, re_update_self_plate)
		end
	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.healthCutOffUpper:SetSize (healthBarHeight, healthBarHeight)
			healthBar.executeRange:SetHeight (healthBarHeight)
			healthBar.executeRangeUpper: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
		
		--plateFrame.PlaterAnchorFrame:SetSize(healthBar:GetSize())
		
		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)
		if plateFrame.debugAreaTextStacking then
			plateFrame.debugAreaTextStacking:Show()
			plateFrame.debugAreaTextureStacking:Show()
		end
		plateFrame.debugAreaText:Show()
		plateFrame.debugAreaTexture:Show()
	end
	function Plater.HideClickSpace (plateFrame)
		if plateFrame.debugAreaTextStacking then
			plateFrame.debugAreaTextStacking:Hide()
			plateFrame.debugAreaTextureStacking:Hide()
		end
		plateFrame.debugAreaText:Hide()
		plateFrame.debugAreaTexture:Hide()
	end
	
	-- default blizzard plate shenanigans
	function Plater.UpdateBaseNameplateOptions()
		if IS_WOW_PROJECT_MIDNIGHT then
			if Plater.db.profile.hide_realm_name_on_blizzard then
				TextureLoadingGroupMixin.RemoveTexture({ textures = NamePlateFriendlyFrameOptions }, "updateNameUsesGetUnitName")
			else
				TextureLoadingGroupMixin.AddTexture({ textures = NamePlateFriendlyFrameOptions }, "updateNameUsesGetUnitName")
			end
			return 
		end
		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 and plateFrame.UnitFrame and plateFrame.UnitFrame.CastBar) 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")
		
		if IS_WOW_PROJECT_MIDNIGHT then
			local width, height = Plater.db.profile.click_space[1], Plater.db.profile.click_space[2]
			C_NamePlate.SetNamePlateSize(width, height)
			
			local widthScale, heightScale = Plater.db.profile.click_space_scale[1], Plater.db.profile.click_space_scale[2]
			local hS, vS = widthScale < 1 and 1 or -1, heightScale < 1 and 1 or -1
			local offsetW, offsetH = hS * (width * widthScale - width), vS * (height * heightScale - height)
			C_NamePlateManager.SetNamePlateHitTestInsets(Enum.NamePlateType.Enemy, offsetW, offsetW, offsetH, offsetH)
			
			
			width, height = Plater.db.profile.click_space_friendly[1], Plater.db.profile.click_space_friendly[2]
			widthScale, heightScale = Plater.db.profile.click_space_scale_friendly[1], Plater.db.profile.click_space_scale_friendly[2]
			hS, vS = widthScale < 1 and 1 or -1, heightScale < 1 and 1 or -1
			offsetW, offsetH = hS * (width * widthScale - width), vS * (height * heightScale - height)
			C_NamePlateManager.SetNamePlateHitTestInsets(Enum.NamePlateType.Friendly, offsetW, offsetW, offsetH, offsetH)
			
			if Plater.db.profile.plate_config.friendlyplayer.click_through then
				C_NamePlateManager.SetNamePlateHitTestInsets(Enum.NamePlateType.Friendly, 10000, 10000, 10000, 10000)
			else
				C_NamePlateManager.SetNamePlateHitTestInsets(Enum.NamePlateType.Friendly, -10000, -10000, -10000, -10000)
			end
			C_NamePlateManager.SetNamePlateHitTestInsets(Enum.NamePlateType.Enemy, -10000, -10000, -10000, -10000)
			
		else
			-- ensure we support the "large nameplate" setting properly
			local success, namePlateVerticalScale = pcall(GetCVarNumberOrDefault, "NamePlateVerticalScale")
			namePlateVerticalScale = (success and namePlateVerticalScale) or 1
			local zeroBasedScale = namePlateVerticalScale - 1.0
			local clampedZeroBasedScale = Saturate(zeroBasedScale)
			local horizontalScale
			success, horizontalScale = pcall(GetCVarNumberOrDefault, "NamePlateHorizontalScale")
			horizontalScale = (success and horizontalScale) or 1

			local width, height = Plater.db.profile.click_space[1], Plater.db.profile.click_space[2]
			local scaledHeight = Lerp and Lerp(1.0, 1.25, zeroBasedScale) or (1.0 + (1.25 - 1.0) * zeroBasedScale)
			if (C_NamePlate.SetNamePlateEnemySize) then
				C_NamePlate.SetNamePlateEnemySize (width * horizontalScale, height * scaledHeight) --classic: {132, 32}, retail: {110, 45},
			end
			
			width, height = Plater.db.profile.click_space_friendly[1], Plater.db.profile.click_space_friendly[2]
			if (C_NamePlate.SetNamePlateFriendlySize) then
				C_NamePlate.SetNamePlateFriendlySize (width * horizontalScale, height * scaledHeight) --classic: {132, 32}, retail: {110, 45},
			end
			
			--C_NamePlate.SetNamePlateFriendlyPreferredClickInsets (0, 0, 0, 0)
			--C_NamePlate.SetNamePlateEnemyPreferredClickInsets (0, 0, 0, 0)
			
			if (C_NamePlate.SetNamePlateFriendlyClickThrough) then
				C_NamePlate.SetNamePlateFriendlyClickThrough (Plater.db.profile.plate_config.friendlyplayer.click_through)
			end
		end
		
		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,
	}
	
	platerInternal.EveryFrameFPSCheck = CreateFrame("Frame")
	platerInternal.EveryFrameFPSCheck:SetScript("OnUpdate", function()
		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
	end)
	
	-- ~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 (HEALTHCUTOFF_AT_DATA.healthCutOffActive and not unitFrame.IsSelf and not unitFrame.PlayerCannotAttack) then
				-- setup
				if HEALTHCUTOFF_AT_DATA.healthCutOffValue and HEALTHCUTOFF_AT_DATA.healthCutOffValue ~= healthBar.healthCutOffValue and HEALTHCUTOFF_AT_DATA.healthCutOffValue > 0 then
					healthBar.healthCutOff:ClearAllPoints()
					healthBar.healthCutOff:SetSize (healthBar:GetHeight(), healthBar:GetHeight())
					healthBar.healthCutOff:SetPoint ("center", healthBar, "left", healthBar:GetWidth() * HEALTHCUTOFF_AT_DATA.healthCutOffValue, 0)
					
					healthBar.executeRange:SetTexCoord (0, HEALTHCUTOFF_AT_DATA.healthCutOffValue, 0, 1)
					healthBar.executeRange:SetAlpha (Plater.db.profile.health_cutoff_alpha or 0.3)
					healthBar.executeRange:SetVertexColor (.3, .3, .3)
					healthBar.executeRange:SetHeight (healthBar:GetHeight())
					healthBar.executeRange:SetPoint ("right", healthBar.healthCutOff, "center")
					healthBar.executeRange:SetPoint ("left", healthBar, "left")
					
					healthBar.healthCutOffValue = HEALTHCUTOFF_AT_DATA.healthCutOffValue
				end
				if HEALTHCUTOFF_AT_DATA.healthCutOffUpperValue and HEALTHCUTOFF_AT_DATA.healthCutOffUpperValue ~= healthBar.healthCutOffUpperValue and HEALTHCUTOFF_AT_DATA.healthCutOffUpperValue < 1 then
					healthBar.healthCutOffUpper:ClearAllPoints()
					healthBar.healthCutOffUpper:SetSize (healthBar:GetHeight(), healthBar:GetHeight())
					healthBar.healthCutOffUpper:SetPoint ("center", healthBar, "right", - (healthBar:GetWidth() * (1-HEALTHCUTOFF_AT_DATA.healthCutOffUpperValue)), 0)
					
					healthBar.executeRangeUpper:SetTexCoord (0, 1-HEALTHCUTOFF_AT_DATA.healthCutOffUpperValue, 0, 1)
					healthBar.executeRangeUpper:SetAlpha (Plater.db.profile.health_cutoff_alpha or 0.3)
					healthBar.executeRangeUpper:SetVertexColor (.3, .3, .3)
					healthBar.executeRangeUpper:SetHeight (healthBar:GetHeight())
					healthBar.executeRangeUpper:SetPoint ("left", healthBar.healthCutOffUpper, "center")
					healthBar.executeRangeUpper:SetPoint ("right", healthBar, "right")
					
					healthBar.healthCutOffUpperValue = HEALTHCUTOFF_AT_DATA.healthCutOffUpperValue
				end
				
				
				if IS_WOW_PROJECT_MIDNIGHT then
					if HEALTHCUTOFF_AT_DATA.healthCutOffValue and HEALTHCUTOFF_AT_DATA.healthCutOffValue > 0 then
						local _,_,_,lowerAlpha = UnitHealthPercent(unitFrame [MEMBER_UNITID], true, HEALTHCUTOFF_AT_DATA.healthCutOffValueCurve):GetRGBA()
						--lower
						healthBar.healthCutOff:Show()
						if (not profile.health_cutoff_hide_divisor) then
							healthBar.healthCutOff:SetAlpha(lowerAlpha)
						else
							healthBar.healthCutOff:Hide()
							healthBar.healthCutOff:SetAlpha(0)
						end
						
						healthBar.executeRange:Show()
						healthBar.executeRange:SetAlpha(lowerAlpha)
						
						if (profile.health_cutoff_extra_glow) then
							healthBar.ExecuteGlowUp:Show()
							healthBar.ExecuteGlowDown:Show()
							healthBar.ExecuteGlowUp:SetAlpha(lowerAlpha)
							healthBar.ExecuteGlowDown:SetAlpha(lowerAlpha)
						else
							healthBar.ExecuteGlowUp:Hide()
							healthBar.ExecuteGlowDown:Hide()
						end
					else
						healthBar.healthCutOff:Hide()
						healthBar.executeRange:Hide()
						healthBar.ExecuteGlowUp:Hide()
						healthBar.ExecuteGlowDown:Hide()
					end
						
					if HEALTHCUTOFF_AT_DATA.healthCutOffUpperValue and HEALTHCUTOFF_AT_DATA.healthCutOffUpperValue < 1 then
						--upper
						local _,_,_,upperAlpha = UnitHealthPercent(unitFrame [MEMBER_UNITID], true, HEALTHCUTOFF_AT_DATA.healthCutOffUpperValueCurve):GetRGBA()
						healthBar.healthCutOffUpper:Show()
						if (not profile.health_cutoff_hide_divisor) then
							healthBar.healthCutOffUpper:SetAlpha(upperAlpha)
						else
							healthBar.healthCutOffUpper:Hide()
							healthBar.healthCutOffUpper:SetAlpha(0)
						end
						
						healthBar.executeRangeUpper:Show()
						healthBar.executeRangeUpper:SetAlpha(upperAlpha)
						
						if (profile.health_cutoff_extra_glow) then
							healthBar.ExecuteGlowUpperUp:Show()
							healthBar.ExecuteGlowUpperDown:Show()
							healthBar.ExecuteGlowUpperUp:SetAlpha(upperAlpha)
							healthBar.ExecuteGlowUpperDown:SetAlpha(upperAlpha)
						else
							healthBar.ExecuteGlowUpperUp:Hide()
							healthBar.ExecuteGlowUpperDown:Hide()
						end
					else
						healthBar.healthCutOffUpper:Hide()
						healthBar.executeRangeUpper:Hide()
						healthBar.ExecuteGlowUpperUp:Hide()
						healthBar.ExecuteGlowUpperDown:Hide()
					end
					
				else
					local healthPercent = (healthBar.currentHealth or 1) / (healthBar.currentHealthMax or 1)
					if (healthPercent <= HEALTHCUTOFF_AT_DATA.healthCutOffValue) then
						if (not healthBar.executeRange:IsShown()) then
							if (not profile.health_cutoff_hide_divisor) then
								healthBar.healthCutOff:Show()
								healthBar.healthCutOff.ShowAnimation:Play()
							else
								healthBar.healthCutOff:Hide()
							end

							healthBar.executeRange:Show()
							
							if (profile.health_cutoff_extra_glow) then
								healthBar.ExecuteGlowUp.ShowAnimation:Play()
								healthBar.ExecuteGlowDown.ShowAnimation:Play()
							end
						end
						
						unitFrame.InExecuteRange = true
					elseif (healthPercent > HEALTHCUTOFF_AT_DATA.healthCutOffUpperValue and healthPercent < 0.999) then
						if (not healthBar.executeRangeUpper:IsShown()) then
							if (not profile.health_cutoff_hide_divisor) then
								healthBar.healthCutOffUpper:Show()
								healthBar.healthCutOffUpper.ShowAnimation:Play()
							else
								healthBar.healthCutOffUpper:Hide()
							end

							healthBar.executeRangeUpper:Show()
							
							if (profile.health_cutoff_extra_glow) then
								healthBar.ExecuteGlowUpperUp.ShowAnimation:Play()
								healthBar.ExecuteGlowUpperDown.ShowAnimation:Play()
							end
						end
						
						unitFrame.InExecuteRange = true
					else
						healthBar.healthCutOff:Hide()
						healthBar.executeRange:Hide()
						healthBar.healthCutOffUpper:Hide()
						healthBar.executeRangeUpper:Hide()
						healthBar.ExecuteGlowUp:Hide()
						healthBar.ExecuteGlowDown:Hide()
						healthBar.ExecuteGlowUpperUp:Hide()
						healthBar.ExecuteGlowUpperDown:Hide()
						
						unitFrame.InExecuteRange = false
					end
				end
			else
				healthBar.healthCutOff:Hide()
				healthBar.healthCutOffUpper:Hide()
				healthBar.executeRange:Hide()
				healthBar.executeRangeUpper:Hide()
				healthBar.ExecuteGlowUp:Hide()
				healthBar.ExecuteGlowDown:Hide()
				healthBar.ExecuteGlowUpperUp:Hide()
				healthBar.ExecuteGlowUpperDown:Hide()
				
				unitFrame.InExecuteRange = false
			end
			
			local isSoftInteract = UnitIsUnit(tickFrame.unit, "softinteract")
			if IS_WOW_PROJECT_MIDNIGHT and issecretvalue(isSoftInteract) then isSoftInteract = false end --MIDNIGHT!!
			local plateFrame = unitFrame.PlateFrame
			unitFrame.isSoftInteract = isSoftInteract
			if (plateFrame) then
				plateFrame.isSoftInteract = isSoftInteract
			end
			unitFrame.isSoftInteractObject = isSoftInteract and ((plateFrame and plateFrame.isObject) or unitFrame.isObject)
			if (plateFrame) then
				plateFrame.isSoftInteractObject = unitFrame.isSoftInteractObject
			end
			
			local isLooseTarget = IsTargetLoose()
			unitFrame.isLooseTarget = isLooseTarget
			if (plateFrame) then
				plateFrame.isLooseTarget = isLooseTarget
			end
			local isSoftEnemy = UnitIsUnit(tickFrame.unit, "softenemy")
			unitFrame.isSoftEnemy = isSoftEnemy
			if (plateFrame) then
				plateFrame.isSoftEnemy = isSoftEnemy
			end
			local isSoftFriend = UnitIsUnit(tickFrame.unit, "softfriend")
			unitFrame.isSoftFriend = isSoftFriend
			if (plateFrame) then
				plateFrame.isSoftFriend = isSoftFriend
			end
			
			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 and tickFrame.PlateFrame 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
				platerInternal.AlignAuraFrames (tickFrame.BuffFrame)
				--update the alignment on the second aura frame as well if enabled
				if (DB_AURA_SEPARATE_BUFFS) then
					platerInternal.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)

			-- well, no scripting in Midnight
			if not IS_WOW_PROJECT_MIDNIGHT then
				--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] or tickFrame.PlateFrame.unitFrame [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
			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 (UnitGetTotalAbsorbs and not IS_WOW_PROJECT_MIDNIGHT) then
				if (profile.indicator_shield) then
					local amountAbsorb = UnitGetTotalAbsorbs(tickFrame.PlateFrame[MEMBER_UNITID] or tickFrame.PlateFrame.unitFrame[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 = unitFrame
		if (DB_AGGRO_CHANGE_HEALTHBAR_COLOR) then
			if (not self.DenyColorChange) then --tagged from a script
				if not self.hasUnitTypeColor then
					Plater.ChangeHealthBarColor_Internal (self.healthBar, r, g, b, a)

				elseif self.InCombat and not self.isGoodAggroState or (Plater.db.profile.unit_type_coloring_no_override_threat and self.isGoodAggroState) then
					Plater.ChangeHealthBarColor_Internal (self.healthBar, r, g, b, a)

				elseif Plater.db.profile.unit_type_coloring_enabled and (self.isGoodAggroState or not self.InCombat) and (Plater.ZoneInstanceType == "party" or Plater.ZoneInstanceType == "raid") then
					-- reset
					Plater.FindAndSetNameplateColor (self)

				else
					Plater.ChangeHealthBarColor_Internal (self.healthBar, r, g, b, a) -- fallback, just in case
				end
			end
		--elseif Plater.db.profile.unit_type_coloring_enabled and (Plater.ZoneInstanceType == "party" or Plater.ZoneInstanceType == "raid") then
		--	-- reset
		--	Plater.FindAndSetNameplateColor (self)
		end
		
		if (DB_AGGRO_CHANGE_BORDER_COLOR) then
			Plater.ForceChangeBorderColor (self, r, g, b, a)
		end
		
		if (DB_AGGRO_CHANGE_NAME_COLOR) then
			self.unitName:SetTextColor (r, g, b, a)
		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)
		if IS_WOW_PROJECT_MIDNIGHT then
			threatStatus = UnitThreatSituation ("player", self.displayedUnit)
			isTanking = (threatStatus or 0) >= 2
		end
		
		--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.isGoodAggroState = false
		
		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 unit 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 UnitIsVisible(tank) and not UnitIsUnit("player", tank) then
								otherIsTanking, otherThreatStatus, otherThreatpct = UnitDetailedThreatSituation (tank, self.displayedUnit)
								if IS_WOW_PROJECT_MIDNIGHT then
									local tmpOtherThreatStatus = UnitThreatSituation (tank, self.displayedUnit)
									if not issecretvalue (tmpOtherThreatStatus) then
										otherThreatStatus = tmpOtherThreatStatus
										otherIsTanking = (otherThreatStatus or 0) >= 2
									else
										otherIsTanking = false -- well, shame
									end
								end
								if otherIsTanking then
									unitOffTank = tank
									break
								end
							end
						end

						--another tank is tanking the unit
						if (unitOffTank) then
							self.namePlateThreatOffTankIsTanking = true
							self.namePlateThreatOffTankName = UnitName(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 (threatStatus and otherThreatStatus) then
								if (threatStatus == 1 and otherThreatStatus >= 2) then
									set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.pulling_from_tank))
								else
									--self.isGoodAggroState = true
									set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.anothertank))
								end
							else
								--self.isGoodAggroState = true
								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
							self.isGoodAggroState = true
							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
					self.isGoodAggroState = true
					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
					self.isGoodAggroState = true
					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.isGoodAggroState = true
					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
							self.isGoodAggroState = true
							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
								if IS_WOW_PROJECT_MIDNIGHT then
									local hasTankAggro = false
									local inRaid = IsInRaid()
									for unit in pairs(TANK_CACHE or {}) do
										if UnitIsVisible(unit) and not UnitIsUnit (unit, "player") then
											local unitThreatLevel = UnitThreatSituation(unit, unitId)
            								if unitThreatLevel and unitThreatLevel >= 2 then
												hasTankAggro = true
												break
											end
										end
									end
									if hasTankAggro then
										set_aggro_color (self, unpack (colorToUse))
									else
										set_aggro_color (self, unpack (DB_AGGRO_DPS_COLORS.notontank))
									end			
								else
									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 tankUnit, _ in pairs (TANK_CACHE) do
											if UnitExists(tankUnit) then
												local threatStatus = UnitThreatSituation (tankUnit, 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
											self.isGoodAggroState = false
											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
								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
		if (not unitFrame) then
			return
		end
		if (not unitFrame.PlateFrame) then
			unitFrame.PlateFrame = plateFrame
		end
		local unitID = plateFrame [MEMBER_UNITID] or unitFrame [MEMBER_UNITID]
		
		local isSoftInteract = UnitIsUnit(unitID, "softinteract")
		if IS_WOW_PROJECT_MIDNIGHT and issecretvalue(isSoftInteract) then isSoftInteract = false end --MIDNIGHT!!
		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
		if (unitFrame.PlateFrame) then
			unitFrame.PlateFrame.isLooseTarget = isLooseTarget
		end
		local isSoftEnemy = UnitIsUnit(unitID, "softenemy")
		unitFrame.isSoftEnemy = isSoftEnemy
		if (unitFrame.PlateFrame) then
			unitFrame.PlateFrame.isSoftEnemy = isSoftEnemy
		end
		local isSoftFriend = UnitIsUnit(unitID, "softfriend")
		unitFrame.isSoftFriend = isSoftFriend
		if (unitFrame.PlateFrame) then
			unitFrame.PlateFrame.isSoftFriend = isSoftFriend
		end
		
		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] or unitFrame [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 IS_WOW_PROJECT_MIDNIGHT then return true end --MIDNIGHT!!
		local isFocus = UnitIsUnit ("focus", unitFrame [MEMBER_UNITID])
		if IS_WOW_PROJECT_MIDNIGHT and issecretvalue(isFocus) then isFocus = false end
		if (isFocus) 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

		local isTarget = UnitIsUnit ("target", unitFrame [MEMBER_UNITID])
		if IS_WOW_PROJECT_MIDNIGHT and issecretvalue(isTarget) then isTarget = false end
		if (isTarget) 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, false) --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.PlayerHasTarget = type(Plater.PlayerCurrentTargetGUID) ~= "nil"
		local selfTarget = UnitIsUnit("player", "target")
		if IS_WOW_PROJECT_MIDNIGHT and issecretvalue(selfTarget) then selfTarget = false end --MIDNIGHT!!
		Plater.PlayerHasTargetNonSelf = Plater.PlayerHasTarget and not selfTarget
		Plater.PlayerCurrentFocusTargetGUID = UnitGUID ("focus")
		--Plater.PlayerHasFocusTarget = Plater.PlayerCurrentFocusTargetGUID and true
		Plater.PlayerHasFocusTarget = type(Plater.PlayerCurrentFocusTargetGUID) ~= "nil"
		local selfFocus = UnitIsUnit("player", "target")
		if IS_WOW_PROJECT_MIDNIGHT and issecretvalue(selfFocus) then selfFocus = false end --MIDNIGHT!!
		Plater.PlayerHasFocusTargetNonSelf = Plater.PlayerHasFocusTarget and not selfFocus 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)
			Plater.SetTextAlignmentFromAnchor(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])
			
			--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 (IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(plateFrame.unitNameInternal) or not IS_WOW_PROJECT_MIDNIGHT) 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)
			Plater.SetTextAlignmentFromAnchor(nameString, plateConfigs.actorname_text_anchor)
			--PixelUtil.SetHeight (nameString, nameString:GetLineHeight())
		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 (IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(plateFrame.unitNameInternal) or not IS_WOW_PROJECT_MIDNIGHT) 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
				platerInternal.UpdatePercentTextLayout(lifeString, plateConfigs)
			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
	
	---update the layout of the percent text, this avoid manipulating secrets and layout in a taint path
	---@param fontString fontstring
	---@param plateConfigs table
	function platerInternal.UpdatePercentTextLayout(fontString, plateConfigs)
		DF:SetFontSize(fontString, plateConfigs.percent_text_size)
		DF:SetFontFace(fontString, plateConfigs.percent_text_font)

		Plater.SetFontOutlineAndShadow(fontString, 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(fontString, plateConfigs.percent_text_color)

		Plater.SetAnchor(fontString, plateConfigs.percent_text_anchor)
		PixelUtil.SetHeight(fontString, fontString:GetLineHeight())
		
		fontString:SetAlpha(plateConfigs.percent_text_alpha)
	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 (not plateConfigs or not self.unitFrame or not self.unitFrame.healthBar or not self.unitFrame.healthBar.lifePercent) then
			return
		end
		
		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
		Plater.StartLogPerformanceCore("Plater-Core", "Health", "UpdateLifePercentText")

		--get the cached health amount for performance
		local currentHealth, maxHealth, currentHealthMissing, currentHealthPercent = healthBar.currentHealth, healthBar.currentHealthMax, healthBar.currentHealthMissing, healthBar.currentHealthPercent
		
		
		if IS_WOW_PROJECT_MIDNIGHT then
			--TODO: MIDNIGHT!!
			--local currentAbsorb, currentAbsorbMax, currentAbsorbIsClamped = healthBar.currentAbsorb, healthBar.currentAbsorbMax, healthBar.currentAbsorbIsClamped
			
			currentHealth = AbbreviateNumbers(currentHealth)
			if (showHealthAmount or showPercentAmount) then
				healthBar.lifePercent:SetText(currentHealth)
			else
				healthBar.lifePercent:SetText("")
			end
			
			if (showHealthAmount and showPercentAmount) then
				local percent = currentHealthPercent
				if (showDecimals) then
					healthBar.lifePercent:SetText (format ("%s (%.1f%%)", currentHealth, percent))
				else
					healthBar.lifePercent:SetText (format ("%s (%d%%)", currentHealth, percent))
				end
				
			elseif (showHealthAmount) then
				healthBar.lifePercent:SetText (currentHealth)
			
			elseif (showPercentAmount) then
				local percent = currentHealthPercent
				if (showDecimals) then
					healthBar.lifePercent:SetText (format ("%.1f%%", percent))
				else
					healthBar.lifePercent:SetText (format ("%d%%", percent))
				end
			
			else
				healthBar.lifePercent:SetText ("")
			end
			
		else
			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
		Plater.EndLogPerformanceCore("Plater-Core", "Health", "UpdateLifePercentText")
	end

	-- this test if the percent life text can updated
	function Plater.CheckLifePercentText (unitFrame, forceUpdate) --private
		if (not unitFrame.actorType) then
			return
		end
		if (not forceUpdate and not unitFrame.healthBar.healthChanged) 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.unitFrame.PlaterOnScreen then
				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
	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
		
		if IS_WOW_PROJECT_MIDNIGHT then
			Plater.UpdateTextSize (spellName, nameString, maxLength, nil)
		else		
			while (nameString:GetUnboundedStringWidth() > 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
	end

	function Plater.AddGuildNameToPlayerName (plateFrame)
		local currentText = plateFrame.CurrentUnitNameString:GetText()
		if ((IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(currentText) or not IS_WOW_PROJECT_MIDNIGHT) and not currentText:find ("<")) then -- this might add more ofthen than needed
			plateFrame.CurrentUnitNameString:SetText (currentText .. "\n" .. "<" .. plateFrame.playerGuildName .. ">")
		end
	end
	
	function Plater.UpdateUnitName (plateFrame)
		local nameString = plateFrame.CurrentUnitNameString

		local plateConfig = DB_PLATE_CONFIG [plateFrame.actorType]
		local maxWidth = plateConfig.actorname_text_max_width or 0
		local wrap = plateConfig.actorname_text_wrap
		if plateFrame.IsFriendlyPlayerWithoutHealthBar or plateFrame.IsNpcWithoutHealthBar then
			maxWidth = 0
			wrap = true
		end
		local name = plateFrame [MEMBER_NAME] or plateFrame.unitFrame [MEMBER_NAME] or ""
		if IS_WOW_PROJECT_MIDNIGHT and issecretvalue(name) then
			-- do we try to refresh? is it worth?
			plateFrame.unitFrame.unitName.isRenamed = true -- force it
			nameString:ClearText()
			Plater.UpdateNameOnRenamedUnit(plateFrame)
			name = plateFrame [MEMBER_NAME] or plateFrame.unitFrame [MEMBER_NAME] or ""
		end
		Plater.UpdateTextSize (name, nameString, maxWidth, nil, wrap)
		
		--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.UpdateTextSize (text, fontString, maxWidth, maxLength, wordWrap)
		if not text then return end
		--if maxWidth and type(fontSize) == "number" then
		--	local textLength = floor(maxWidth / fontSize * 2)
		--	text = string.format("%." .. textLength .. "s", text)
		--	if fontString then
		--		fontString:SetWordWrap(false)
		--		fontString:SetNonSpaceWrap(false)
		--		fontString:SetSpacing(0)
		--		
		--		fontString:SetText (text)
		--		
		--		if not IS_WOW_PROJECT_MIDNIGHT then
		--			-- cleanup utf8...
		--			text = DF:CleanTruncateUTF8String(text)
		--			textString:SetText (text)
		--		end
		--	end
		--	
		--else
		if fontString and maxWidth then
			if maxWidth == 0 then
				fontString:SetWordWrap(wordWrap == nil or wordWrap)
				fontString:SetNonSpaceWrap(wordWrap == nil or wordWrap)
				fontString:SetSpacing(0)
			else
				fontString:SetWordWrap(wordWrap or false)
				fontString:SetNonSpaceWrap(wordWrap or false)
				fontString:SetSpacing(0)
			end
			fontString:SetWidth(maxWidth)
			fontString:SetText(text)	
			
		elseif maxLength then
			if maxLength < 100 then -- 100 -> don't limit; format max is 99
				text = string.format("%." .. maxLength .. "s", text)
			end
			if fontString then
				fontString:SetText (text)
			end
		end
		return text
	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] = (IS_WOW_PROJECT_MIDNIGHT and newNpcName or "") or lower (newNpcName)
			unitFrame [MEMBER_NAME] = newNpcName
			unitFrame [MEMBER_NAMELOWER] = plateFrame [MEMBER_NAMELOWER]
			unitFrame.unitName:SetText(newNpcName)
			unitFrame.unitName.isRenamed = true
		elseif (unitFrame.unitName.isRenamed) then
			newNpcName = UnitName(plateFrame [MEMBER_UNITID] or unitFrame [MEMBER_UNITID])
			plateFrame [MEMBER_NAME] = newNpcName
			plateFrame [MEMBER_NAMELOWER] = (IS_WOW_PROJECT_MIDNIGHT and newNpcName or "") or lower (newNpcName)
			unitFrame [MEMBER_NAME] = newNpcName
			unitFrame [MEMBER_NAMELOWER] = plateFrame [MEMBER_NAMELOWER]
			unitFrame.unitName:SetText(newNpcName)
			unitFrame.unitName.isRenamed = nil
		end
	end
	
	--Blizzard default font settings
	function Plater.UpdateBlizzardNameplateFonts(updateFont, skipSmall)
		local profile = Plater.db.profile
		if profile.blizzard_nameplate_font_override_enabled and (updateFont == true or profile.blizzard_nameplate_font == updateFont or profile.blizzard_nameplate_large_font == updateFont)then
			if IS_WOW_PROJECT_MIDNIGHT then
				for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
					DF:SetFontFace (plateFrame.UnitFrame.name, profile.blizzard_nameplate_font)
					DF:SetFontOutline (plateFrame.UnitFrame.name, profile.blizzard_nameplate_font_outline)
					DF:SetFontSize (plateFrame.UnitFrame.name, profile.blizzard_nameplate_font_size)
				end

				--set fonts once with smaller to update
				if not skipSmall then
					local font = LibSharedMedia:Fetch("font", profile.blizzard_nameplate_font, true)
					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 - 1)
					
					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 - 1)
					
					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 - 1)
					
					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 - 1)

					DF:SetFontFace (_G.SystemFont_NamePlate_Outlined, profile.blizzard_nameplate_font)
					DF:SetFontOutline (_G.SystemFont_NamePlate_Outlined, profile.blizzard_nameplate_font_outline)
					DF:SetFontSize (_G.SystemFont_NamePlate_Outlined, profile.blizzard_nameplate_font_size - 1)
					
					C_Timer.After(0, function() Plater.UpdateBlizzardNameplateFonts(true, true) end)
					return
				end
			end

			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)

			if IS_WOW_PROJECT_MIDNIGHT then
				DF:SetFontFace (_G.SystemFont_NamePlate_Outlined, profile.blizzard_nameplate_font)
				DF:SetFontOutline (_G.SystemFont_NamePlate_Outlined, profile.blizzard_nameplate_font_outline)
				DF:SetFontSize (_G.SystemFont_NamePlate_Outlined, profile.blizzard_nameplate_font_size)
			end
		end
	end

	function Plater.UpdateStackingSize(plateFrame, customWScale, customHScale)
		if not plateFrame then return end
		local unitFrame = plateFrame.unitFrame
		if unitFrame.stackSizeFrame then --TODO: MIDNIGHT!!
			unitFrame.stackSizeFrame:ClearAllPoints()
			unitFrame.stackSizeFrame:SetParent(unitFrame.PlaterOnScreen and unitFrame or plateFrame)
			plateFrame:SetStackingBoundsFrame(unitFrame.stackSizeFrame)
			--unitFrame.stackSizeFrame:SetAllPoints()
			
			local width, height = Plater.db.profile.click_space[1], Plater.db.profile.click_space[2]
			local widthScale, heightScale
			if actorType == ACTORTYPE_FRIENDLY_PLAYER or actorType == ACTORTYPE_FRIENDLY_NPC then
				widthScale, heightScale = Plater.db.profile.overlap_space_scale_friendly[1], Plater.db.profile.overlap_space_scale_friendly[2]
			else
				widthScale, heightScale = Plater.db.profile.overlap_space_scale[1], Plater.db.profile.overlap_space_scale[2]
			end
			
			--local offsetW, offsetH = (width - width * widthScale), (height - height * heightScale)
			
			unitFrame.stackSizeFrame:SetPoint("CENTER", isPlateEnabled and unitFrame or plateFrame, "CENTER", 0, 0)
			unitFrame.stackSizeFrame:SetSize(width * (customWScale or widthScale), height * (customHScale or heightScale))
			--unitFrame.stackSizeFrame:ClearAllPoints()
			--unitFrame.stackSizeFrame:SetPoint("TOPLEFT", unitFrame.PlaterOnScreen and unitFrame or plateFrame, "TOPLEFT", offsetW, -offsetH)
			--unitFrame.stackSizeFrame:SetPoint("BOTTOMRIGHT", unitFrame.PlaterOnScreen and unitFrame or plateFrame, "BOTTOMRIGHT", -offsetW, offsetH)
		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
		
		healthBar.Settings.AnimateHealth = Plater.db.profile.use_health_animation
		
		castBar.Settings.FillOnInterrupt = Plater.db.profile.cast_statusbar_spark_filloninterrupt
		castBar.Settings.HideSparkOnInterrupt = Plater.db.profile.cast_statusbar_spark_hideoninterrupt
		castBar.Settings.ShowShield = Plater.db.profile.castbar_icon_showshield

		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
		
			if IS_WOW_PROJECT_MIDNIGHT then
				if not issecretvalue(plateFrame [MEMBER_GUID]) then
					Plater.ForceFindPetOwner (plateFrame [MEMBER_GUID], unitFrame [MEMBER_UNITID])
				elseif not issecretvalue(unitFrame [MEMBER_GUID]) then
					Plater.ForceFindPetOwner (unitFrame [MEMBER_GUID], unitFrame [MEMBER_UNITID])
				end
			else
				Plater.ForceFindPetOwner (plateFrame [MEMBER_GUID], unitFrame [MEMBER_UNITID])
			end
		
			-- handle own pets separately, including nazjatar guardians
			if ((IS_WOW_PROJECT_MIDNIGHT and UnitIsUnit(unitFrame [MEMBER_UNITID], "playerpet")) or (not IS_WOW_PROJECT_MIDNIGHT and 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] or unitFrame [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 not unitFrame.IsSelf then
					nameFrame:Show()
					-- could be a pet
					if IS_WOW_PROJECT_MIDNIGHT then
						if not issecretvalue(plateFrame [MEMBER_GUID]) then
							Plater.ForceFindPetOwner (plateFrame [MEMBER_GUID], unitFrame [MEMBER_UNITID])
						elseif not issecretvalue(unitFrame [MEMBER_GUID]) then
							Plater.ForceFindPetOwner (unitFrame [MEMBER_GUID], unitFrame [MEMBER_UNITID])
						end
					else
						Plater.ForceFindPetOwner (plateFrame [MEMBER_GUID], unitFrame [MEMBER_UNITID])
					end
				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.Colors
			colors.Casting:SetColor (profile.cast_statusbar_color)
			colors.Channeling:SetColor (profile.cast_statusbar_color_channeling)
			colors.Empowered:SetColor (profile.cast_statusbar_color_empowered)
			colors.NonInterruptible:SetColor (profile.cast_statusbar_color_nointerrupt)
			colors.Interrupted:SetColor (profile.cast_statusbar_color_interrupted)
			colors.Finished:SetColor (profile.cast_statusbar_color_finished)
			if colors.Important then
				colors.Important:SetColor (profile.cast_statusbar_color_important)
			end
			
			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
			castBar.Spark:SetPoint("CENTER", castBar.barTexture, "RIGHT", castBar.Settings.SparkOffset, 0)
			
			
			--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() -- this should be fired with SetUnit
			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, true)
		
		--target indicator
		Plater.UpdateTarget (plateFrame)
		
		--stacking
		Plater.UpdateStackingSize(plateFrame)
		
		--personal player bar
		if (plateFrame.IsSelf) then
			Plater.UpdatePersonalBar (NamePlateDriverFrame)
			--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
		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", not IS_WOW_PROJECT_MIDNIGHT)
			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, a) --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, a)
			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])
		local icon = plateFrame.unitFrame.PlaterRaidTargetFrame.RaidTargetIcon
		
		if (index and not plateFrame.IsSelf) then
			if IS_WOW_PROJECT_MIDNIGHT and issecretvalue(index) then
				--Plater.RaidTargetFontString = Plater.RaidTargetFontString or UIParent:CreateFontString(nil, nil, "GameFontNormal")
				--Plater.RaidTargetFontString:SetFormattedText("Interface\\TargetingFrame\\UI-RaidTargetingIcon_%d.blp", index)
				--icon:SetTexture(Plater.RaidTargetFontString:GetText())
				SetRaidTargetIconTexture (icon, index)
			else
				SetRaidTargetIconTexture (icon, index)
			end
			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 (not IS_WOW_PROJECT_MIDNIGHT and 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 (not IS_WOW_PROJECT_MIDNIGHT and config.indicator_friendlyspec) 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_ENEMY_NPC) then
		
			--is a pet
			if IS_WOW_PROJECT_MIDNIGHT then
				local unitID = plateFrame.unitFrame [MEMBER_UNITID]
				local isPet = UnitIsMinion(unitID) or UnitIsOtherPlayersPet(unitID) or UnitIsUnit("pet", unitID)
				if not issecretvalue(isPet) and isPet then --MIDNIGHT!!
					if (config.indicator_pet) then
						Plater.AddIndicator (plateFrame, "pet")
					end
				end
			elseif (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", -1, 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, difficultyID = GetInstanceInfo()
		zoneType = (difficultyID == 208 and "party") or zoneType -- delves are party content.
		local profile = Plater.db.profile
		
		-- combat toggle
		if (profile.auto_toggle_combat_enabled and (combat ~= nil)) then
			local onlyNamesEnabled = GetCVarBool("nameplateShowOnlyNames") or GetCVarBool("nameplateShowOnlyNameForFriendlyPlayerUnits")
			--local onlyNamesEnabledRaw = GetCVar("nameplateShowOnlyNames")
			local alwaysShow = GetCVarBool("nameplateShowAll")
			
			--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
					if IS_WOW_PROJECT_MIDNIGHT then
						SetCVar("nameplateShowOnlyNameForFriendlyPlayerUnits", profile.auto_toggle_combat.blizz_healthbar_ic and CVAR_ENABLED or CVAR_DISABLED)
					else
						SetCVar("nameplateShowOnlyNames", profile.auto_toggle_combat.blizz_healthbar_ic and CVAR_ENABLED or CVAR_DISABLED)
						Plater.UpdateBaseNameplateOptions()
					end
				end
				if alwaysShow ~= profile.auto_toggle_combat.always_show_ic then
					SetCVar("nameplateShowAll", profile.auto_toggle_combat.always_show_ic and CVAR_ENABLED or CVAR_DISABLED)
				end
			else
				if onlyNamesEnabled ~= profile.auto_toggle_combat.blizz_healthbar_ooc then
					if IS_WOW_PROJECT_MIDNIGHT then
						SetCVar("nameplateShowOnlyNameForFriendlyPlayerUnits", profile.auto_toggle_combat.blizz_healthbar_ooc and CVAR_ENABLED or CVAR_DISABLED)
					else
						SetCVar("nameplateShowOnlyNames", profile.auto_toggle_combat.blizz_healthbar_ooc and CVAR_ENABLED or CVAR_DISABLED)
						--Plater.UpdateBaseNameplateOptions()
					end
				end
				if alwaysShow ~= profile.auto_toggle_combat.always_show_ooc then
					SetCVar("nameplateShowAll", profile.auto_toggle_combat.always_show_ooc and CVAR_ENABLED or CVAR_DISABLED)
				end
			end
			--NamePlateDriverFrame:RegisterEvent("CVAR_UPDATE")
			
			if combat then
				if IS_WOW_PROJECT_MIDNIGHT then
					SetCVar ("nameplateShowFriendlyPlayers",profile.auto_toggle_combat.friendly_ic and CVAR_ENABLED or CVAR_DISABLED)
				else
					SetCVar("nameplateShowFriends", profile.auto_toggle_combat.friendly_ic and CVAR_ENABLED or CVAR_DISABLED)
				end
				SetCVar("nameplateShowEnemies", profile.auto_toggle_combat.enemy_ic and CVAR_ENABLED or CVAR_DISABLED)
				return
			else
				if IS_WOW_PROJECT_MIDNIGHT then
					SetCVar("nameplateShowFriendlyPlayers", profile.auto_toggle_combat.friendly_ooc and CVAR_ENABLED or CVAR_DISABLED)
				else
					SetCVar("nameplateShowFriends", profile.auto_toggle_combat.friendly_ooc and CVAR_ENABLED or CVAR_DISABLED)
				end
				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
			local function setStacking(state, stateFriendly)
				if not IS_WOW_PROJECT_MIDNIGHT then
					SetCVar ("nameplateMotion", state and CVAR_ENABLED or CVAR_DISABLED)
				else
					if stateFriendly == nil then
						stateFriendly = C_CVar.GetCVarBitfield("nameplateStackingTypes", Enum.NamePlateStackType.Friendly)
					end
					C_CVar.SetCVarBitfield("nameplateStackingTypes", Enum.NamePlateStackType.Enemy, state and true or false)
					C_CVar.SetCVarBitfield("nameplateStackingTypes", Enum.NamePlateStackType.Friendly, stateFriendly and true or false)
				end
			end
			
			if (zoneType == "party") then
				setStacking(profile.auto_toggle_stacking ["party"])
				
			elseif (zoneType == "raid") then
				setStacking(profile.auto_toggle_stacking ["raid"])
				
			elseif (zoneType == "arena" or zoneType == "pvp") then
				setStacking(profile.auto_toggle_stacking ["arena"])
				
			else
				--if the player is resting, consider inside a major city
				if (IsResting()) then
					setStacking(profile.auto_toggle_stacking ["cities"])
				else
					setStacking(profile.auto_toggle_stacking ["world"])
				end
			end
		end
		
		--"always show" toggle: nameplateShowAll
		if (profile.auto_toggle_always_show_enabled) then
			--discover which is the map type the player is in
			if (zoneType == "party") then
				SetCVar ("nameplateShowAll", profile.auto_toggle_always_show ["party"] and CVAR_ENABLED or CVAR_DISABLED)
				
			elseif (zoneType == "raid") then
				SetCVar ("nameplateShowAll", profile.auto_toggle_always_show ["raid"] and CVAR_ENABLED or CVAR_DISABLED)
				
			elseif (zoneType == "arena" or zoneType == "pvp") then
				SetCVar ("nameplateShowAll", profile.auto_toggle_always_show ["arena"] and CVAR_ENABLED or CVAR_DISABLED)
				
			else
				--if the player is resting, consider inside a major city
				if (IsResting()) then
					SetCVar ("nameplateShowAll", profile.auto_toggle_always_show ["cities"] and CVAR_ENABLED or CVAR_DISABLED)
				else
					SetCVar ("nameplateShowAll", profile.auto_toggle_always_show ["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
				if IS_WOW_PROJECT_MIDNIGHT then
					SetCVar ("nameplateShowFriendlyPlayers", profile.auto_toggle_friendly ["party"] and CVAR_ENABLED or CVAR_DISABLED)
				else
					SetCVar ("nameplateShowFriends", profile.auto_toggle_friendly ["party"] and CVAR_ENABLED or CVAR_DISABLED)
				end
				
			elseif (zoneType == "raid") then
				if IS_WOW_PROJECT_MIDNIGHT then
					SetCVar ("nameplateShowFriendlyPlayers", profile.auto_toggle_friendly ["raid"] and CVAR_ENABLED or CVAR_DISABLED)
				else
					SetCVar ("nameplateShowFriends", profile.auto_toggle_friendly ["raid"] and CVAR_ENABLED or CVAR_DISABLED)
				end
				
			elseif (zoneType == "arena" or zoneType == "pvp") then
				if IS_WOW_PROJECT_MIDNIGHT then
					SetCVar ("nameplateShowFriendlyPlayers", profile.auto_toggle_friendly ["arena"] and CVAR_ENABLED or CVAR_DISABLED)
				else
					SetCVar ("nameplateShowFriends", profile.auto_toggle_friendly ["arena"] and CVAR_ENABLED or CVAR_DISABLED)
				end
				
			else
				--if the player is resting, consider inside a major city
				if (IsResting()) then
					if IS_WOW_PROJECT_MIDNIGHT then
						SetCVar ("nameplateShowFriendlyPlayers", profile.auto_toggle_friendly ["cities"] and CVAR_ENABLED or CVAR_DISABLED)
					else
						SetCVar ("nameplateShowFriends", profile.auto_toggle_friendly ["cities"] and CVAR_ENABLED or CVAR_DISABLED)
					end
				else
					if IS_WOW_PROJECT_MIDNIGHT then
						SetCVar ("nameplateShowFriendlyPlayers", profile.auto_toggle_friendly ["world"] and CVAR_ENABLED or CVAR_DISABLED)
					else
						SetCVar ("nameplateShowFriends", profile.auto_toggle_friendly ["world"] and CVAR_ENABLED or CVAR_DISABLED)
					end
				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 = {
		--1 topleft
		function (widget, config, attachTo, centered)
			widget:ClearAllPoints()
			local widgetRelative = centered and "bottom" or "bottomleft"
			PixelUtil.SetPoint (widget, widgetRelative, attachTo, "topleft", config.x, config.y, 0, 0)
		end,
		--2 left
		function (widget, config, attachTo, centered)
			widget:ClearAllPoints()
			local widgetRelative = centered and "center" or "right"
			PixelUtil.SetPoint (widget, widgetRelative, attachTo, "left", config.x, config.y, 0, 0)
		end,
		--3 bottomleft
		function (widget, config, attachTo, centered)
			widget:ClearAllPoints()
			local widgetRelative = centered and "top" or "topleft"
			PixelUtil.SetPoint (widget, widgetRelative, attachTo, "bottomleft", config.x, config.y, 0, 0)
		end,
		--4 bottom
		function (widget, config, attachTo, centered)
			widget:ClearAllPoints()
			PixelUtil.SetPoint (widget, "top", attachTo, "bottom", config.x, config.y, 0, 0)
		end,
		--5 bottomright
		function (widget, config, attachTo, centered)
			widget:ClearAllPoints()
			local widgetRelative = centered and "top" or "topright"
			PixelUtil.SetPoint (widget, widgetRelative, attachTo, "bottomright", config.x, config.y, 0, 0)
		end,
		--6 right
		function (widget, config, attachTo, centered)
			widget:ClearAllPoints()
			local widgetRelative = centered and "center" or "left"
			PixelUtil.SetPoint (widget, widgetRelative, attachTo, "right", config.x, config.y, 0, 0)
		end,
		--7 topright
		function (widget, config, attachTo, centered)
			widget:ClearAllPoints()
			local widgetRelative = centered and "bottom" or "bottomright"
			PixelUtil.SetPoint (widget, widgetRelative, attachTo, "topright", config.x, config.y, 0, 0)
		end,
		--8 top
		function (widget, config, attachTo, centered)
			widget:ClearAllPoints()
			PixelUtil.SetPoint (widget, "bottom", attachTo, "top", config.x, config.y, 0, 0)
		end,
		--9 center
		function (widget, config, attachTo, centered)
			widget:ClearAllPoints()
			PixelUtil.SetPoint (widget, "center", attachTo, "center", config.x, config.y, 0, 0)
		end,
		--10 inner left
		function (widget, config, attachTo, centered)
			widget:ClearAllPoints()
			PixelUtil.SetPoint (widget, "left", attachTo, "left", config.x, config.y, 0, 0)
		end,
		--11 inner right
		function (widget, config, attachTo, centered)
			widget:ClearAllPoints()
			PixelUtil.SetPoint (widget, "right", attachTo, "right", config.x, config.y, 0, 0)
		end,
		--12 inner top
		function (widget, config, attachTo, centered)
			widget:ClearAllPoints()
			PixelUtil.SetPoint (widget, "top", attachTo, "top", config.x, config.y, 0, 0)
		end,
		--13 inner bottom
		function (widget, config, attachTo, centered)
			widget:ClearAllPoints()
			PixelUtil.SetPoint (widget, "bottom", attachTo, "bottom", config.x, config.y, 0, 0)
		end,
		--14 inner topleft
		function (widget, config, attachTo, centered)
			widget:ClearAllPoints()
			PixelUtil.SetPoint (widget, "topleft", attachTo, "topleft", config.x, config.y, 0, 0)
		end,
		--15 inner bottom left
		function (widget, config, attachTo, centered)
			widget:ClearAllPoints()
			PixelUtil.SetPoint (widget, "bottomleft", attachTo, "bottomleft", config.x, config.y, 0, 0)
		end,
		--16 inner bottom right
		function (widget, config, attachTo, centered)
			widget:ClearAllPoints()
			PixelUtil.SetPoint (widget, "bottomright", attachTo, "bottomright", config.x, config.y, 0, 0)
		end,
		--17 inner top right
		function (widget, config, attachTo, centered)
			widget:ClearAllPoints()
			PixelUtil.SetPoint (widget, "topright", attachTo, "topright", 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
	
	function Plater.SetTextAlignmentFromAnchor(fontString, config)
		local sideMap = {
			[1] = "LEFT",
			[2] = "LEFT",
			[3] = "LEFT",
			[4] = "CENTER",
			[5] = "RIGHT",
			[6] = "RIGHT",
			[7] = "RIGHT",
			[8] = "CENTER",
			[9] = "CENTER",
			[10] = "LEFT",
			[11] = "RIGHT",
			[12] = "CENTER",
			[13] = "CENTER",
			[14] = "LEFT",
			[15] = "LEFT",
			[16] = "RIGHT",
			[17] = "RIGHT",
		}
		local side = config and config.side or 1
		side = (side > 0 and side <= 17) and side or 1
		fontString:SetJustifyH(sideMap[side])
	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,
		INNER_TOP_LEFT = 14,
		INNER_BOTTOM_LEFT = 15,
		INNER_BOTTOM_RIGHT = 16,
		INNER_TOP_RIGHT = 17,
	}

	--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
		local onlyDamaged = (not IS_WOW_PROJECT_MIDNIGHT and DB_PLATE_CONFIG [ACTORTYPE_FRIENDLY_PLAYER].only_damaged) or false
		if (DB_PLATE_CONFIG [ACTORTYPE_FRIENDLY_PLAYER].only_thename and not onlyDamaged) then
			if (not isFriendlyPlayerWithoutHealthBar) or force then
				Plater.HideHealthBar (plateFrame.unitFrame, true)
			end
			
		elseif (onlyDamaged) then
			local healthBar = plateFrame.unitFrame.healthBar
			if IS_WOW_PROJECT_MIDNIGHT or ((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)
		local isMouseOver = UnitIsUnit ("mouseover", self.unit)
		if IS_WOW_PROJECT_MIDNIGHT and issecretvalue(isMouseOver) then isMouseOver = false end
		if (isMouseOver) 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.HighlightTexture:SetTexture (DB_TEXTURE_HEALTHBAR)
		unitFrame.HighlightFrame.HighlightTexture:SetBlendMode ("ADD")
		unitFrame.HighlightFrame.HighlightTexture:SetAlpha (Plater.db.profile.hover_highlight_alpha)

		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 = max((self.AnimationStart - self.AnimationEnd) / self.currentHealthMax, 0.01) -- % travel, with min of 1%
		local fps = Plater.FPSData.curFPS or 60
		local calcAnimationSpeed = (distance / fps * 2 * DB_ANIMATION_TIME_DILATATION) --scale with fps
		
		self.AnimationStart = self.currentHealthMax == 0 and 1 or self.AnimationStart - (self.currentHealthMax * calcAnimationSpeed)
		
		if (self.AnimationStart-1 <= self.AnimationEnd) then
			self.AnimationStart = self.AnimationEnd
			self:SetValue (self.AnimationEnd)
			self.IsAnimating = false
			if (self.Spark) then
				self.Spark:Hide()
			end
			return
		end
		
		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
	end

	function Plater.AnimateRightWithAccel (self, deltaTime)
		if self.AnimationEnd > self.currentHealthMax then self.AnimationEnd = self.currentHealthMax end
		local distance = max((self.AnimationEnd - self.AnimationStart) / self.currentHealthMax, 0.01) -- % travel, with min of 1%
		local fps = Plater.FPSData.curFPS or 60
		local calcAnimationSpeed = (distance / fps * 2 * DB_ANIMATION_TIME_DILATATION) --scale with fps
		
		self.AnimationStart = self.AnimationStart + (self.currentHealthMax * calcAnimationSpeed)
		
		if (self.AnimationStart+1 >= self.AnimationEnd) then
			self.AnimationStart = self.AnimationEnd
			self:SetValue (self.AnimationEnd)
			--self.currentHealth = self.AnimationEnd
			self.IsAnimating = false
			return
		end
		
		self:SetValue (self.AnimationStart)
		--self.currentHealth = self.AnimationStart
	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
		if IS_WOW_PROJECT_MIDNIGHT and issecretvalue(guid) then return 0 end --MIDNIGHT!!
		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 IS_WOW_PROJECT_MIDNIGHT and issecretvalue(npcId) then return end --MIDNIGHT!!
		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 IS_WOW_PROJECT_MIDNIGHT then
			local unitID = plateFrame.unitFrame [MEMBER_UNITID]
			local isPet = UnitIsMinion(unitID) or UnitIsOtherPlayersPet(unitID) or UnitIsUnit("pet", unitID)
			if IS_WOW_PROJECT_MIDNIGHT and issecretvalue(isPet) then return "normal" end --MIDNIGHT!!
			if isPet then
				return "pet"
			end
			
		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
						Plater.SetCastBarInterruptedState(unitFrame, sourceGUID, name)
					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 IS_WOW_PROJECT_MIDNIGHT and (issecretvalue(spellID) or issecretvalue(sourceGUID)) then return end --MIDNIGHT!!
			if ((tonumber(spellID) or 0) > 0 and (not DB_CAPTURED_SPELLS[spellID] or DB_CAPTURED_SPELLS[spellID].isChanneled == nil or not DB_CAPTURED_CASTS[spellID] or DB_CAPTURED_CASTS[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 "")

						if (npcId and npcId ~= 0) then
							local isChanneled, isCasting = false, false
							if sourceGUID and UnitTokenFromGUID then -- this is the only proper way to check for channeled spells...
								local unit = UnitTokenFromGUID(sourceGUID)
								if unit then
									if UnitChannelInfo(unit) then
										isChanneled = true
									elseif UnitCastingInfo(unit) then
										isCasting = true
									end
								end
							end
							
							---@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 or isCasting) 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)
	--no longer in midnight... good bye...
	if not IS_WOW_PROJECT_MIDNIGHT then
		PlaterCLEUParser:RegisterEvent ("COMBAT_LOG_EVENT_UNFILTERED")
	end

	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, unitID) --private
		Plater.StartLogPerformanceCore("Plater-Core", "Update", "ForceFindPetOwner")
		
		local petName,text1
		local cbMode = tonumber(GetCVar("colorblindMode")) or 0
		if IS_WOW_PROJECT_MAINLINE then
			if IS_WOW_PROJECT_MIDNIGHT and issecretvalue(serial) then return end
			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 IS_WOW_PROJECT_MIDNIGHT and issecretvalue(text1) then return end
		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 IS_WOW_PROJECT_MIDNIGHT and issecretvalue(petName) then return end
		if (isPlayerPet or isOtherPet) and petName then
			local entry = {ownerGUID = IS_WOW_PROJECT_MIDNIGHT and UnitOwnerGUID(unitID) or isPlayerPet and UnitGUID("player") or ownerName and UnitExists(ownerName) and UnitGUID(ownerName) or nil, 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)
	if not IS_WOW_PROJECT_MIDNIGHT then
		--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)
	end
	
	--enable enemy minus nameplates
	SetCVar ("nameplateShowEnemyMinions", CVAR_ENABLED)
	SetCVar ("nameplateShowEnemyMinus", CVAR_ENABLED)
	
	--don't show friendly npcs
	SetCVar ("nameplateShowFriendlyNPCs", 0)

	if IS_WOW_PROJECT_MIDNIGHT then
		--disable friendly minius nameplates
		SetCVar ("nameplateShowFriendlyPlayerGuardians", CVAR_DISABLED)
		SetCVar ("nameplateShowFriendlyPlayerPets", CVAR_DISABLED)
		SetCVar ("nameplateShowFriendlyPlayerTotems", CVAR_DISABLED)
		SetCVar ("nameplateShowFriendlyPlayerMinions", CVAR_DISABLED)
	else
		--disable friendly minius nameplates
		SetCVar ("nameplateShowFriendlyGuardians", CVAR_DISABLED)
		SetCVar ("nameplateShowFriendlyPets", CVAR_DISABLED)
		SetCVar ("nameplateShowFriendlyTotems", CVAR_DISABLED)
		SetCVar ("nameplateShowFriendlyMinions", CVAR_DISABLED)
	end
	
	--> make it show the class color of players
	SetCVar ("ShowClassColorInNameplate", CVAR_ENABLED)
	
	--> lock nameplates to screen
	if not IS_WOW_PROJECT_MIDNIGHT then
		SetCVar ("nameplateOtherTopInset", "0.085")
		SetCVar ("nameplateLargeTopInset", "0.085")
	end
	SetCVar ("clampTargetNameplateToScreen", "1")
	SetCVar ("nameplateTargetRadialPosition", "1")
	SetCVar ("nameplateTargetBehindMaxDistance", "30")

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

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

	
	--> 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 guid
			if IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(plateFrame [MEMBER_GUID]) then
				guid = plateFrame [MEMBER_GUID]
			elseif IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(plateFrame.unitFrame [MEMBER_GUID]) then
				guid = plateFrame.unitFrame [MEMBER_GUID]
			elseif IS_WOW_PROJECT_MIDNIGHT then
				return
			else
				guid = plateFrame [MEMBER_GUID]
			end
			local tooltipData = C_TooltipInfo.GetHyperlink("unit:" .. (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)
		local guid
		if IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(plateFrame [MEMBER_GUID]) then
			guid = plateFrame [MEMBER_GUID]
		elseif IS_WOW_PROJECT_MIDNIGHT and not issecretvalue(plateFrame.unitFrame [MEMBER_GUID]) then
			guid = plateFrame.unitFrame [MEMBER_GUID]
		elseif IS_WOW_PROJECT_MIDNIGHT then
			return
		else
			guid = plateFrame [MEMBER_GUID]
		end
		if (not guid) then
			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] or "N/A"))
			if not ScanQuestTextCache then
				ScanQuestTextCache = {}
			end
			useQuestie = true
		else
			if IS_WOW_PROJECT_MAINLINE then
				local tooltipData = C_TooltipInfo.GetHyperlink ("unit:" .. guid)
				if tooltipData then
					for _, line in ipairs(tooltipData.lines or {}) do
						if IS_WOW_PROJECT_MIDNIGHT and issecretvalue(line.type) then break end
						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:" .. 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 GetQuestsForPlayerByMapID = C_TaskQuest.GetQuestsForPlayerByMapID or C_TaskQuest.GetQuestsOnMap
				local worldQuests = 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 or IS_WOW_PROJECT_CLASSIC_MOP 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_MIDNIGHT then return end -- secret...
	
		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)
		HEALTHCUTOFF_AT_DATA.healthCutOffActive = isExecuteEnabled
		if IS_WOW_PROJECT_MIDNIGHT then
			HEALTHCUTOFF_AT_DATA.healthCutOffValue = tonumber (healthAmountLower) or -0.1
			HEALTHCUTOFF_AT_DATA.healthCutOffValueCurve = C_CurveUtil.CreateColorCurve()
			HEALTHCUTOFF_AT_DATA.healthCutOffValueCurve:SetType(Enum.LuaCurveType.Step)
			HEALTHCUTOFF_AT_DATA.healthCutOffValueCurve:AddPoint(0  , CreateColor(0, 0, 0, Plater.db.profile.health_cutoff_alpha or 0.3)) -- Visible    0% -  19%
			HEALTHCUTOFF_AT_DATA.healthCutOffValueCurve:AddPoint(tonumber (healthAmountLower) or -0.1, CreateColor(0, 0, 0, 0)) -- Invisible 20% - 100%
			
			HEALTHCUTOFF_AT_DATA.healthCutOffUpperValue = tonumber (healthAmountUpper) or 1.1
			HEALTHCUTOFF_AT_DATA.healthCutOffUpperValueCurve = C_CurveUtil.CreateColorCurve()
			HEALTHCUTOFF_AT_DATA.healthCutOffUpperValueCurve:SetType(Enum.LuaCurveType.Step)
			HEALTHCUTOFF_AT_DATA.healthCutOffUpperValueCurve:AddPoint(1  , CreateColor(0, 0, 0, Plater.db.profile.health_cutoff_alpha or 0.3)) -- Visible    90% -  100%
			HEALTHCUTOFF_AT_DATA.healthCutOffUpperValueCurve:AddPoint(tonumber (healthAmountUpper) or 1.1, CreateColor(0, 0, 0, 0)) -- Invisible 0% - 89%
		else
			HEALTHCUTOFF_AT_DATA.healthCutOffValue = tonumber (healthAmountLower) or -0.1
			HEALTHCUTOFF_AT_DATA.healthCutOffUpperValue = tonumber (healthAmountUpper) or 1.1
		end
	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:GetUnboundedStringWidth() > maxWidth) do
			text = strsub (text, 1, #text - 1)
			fontString:SetText (text)
			if (string.len (text) <= 1) then
				break
			end
		end	
		
		if not IS_WOW_PROJECT_MIDNIGHT then
			-- cleanup utf8...
			text = DF:CleanTruncateUTF8String(text)
			fontString:SetText (text)
		end
		
	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
		platerInternal.AlignAuraFrames (buffFrame)
		--buffFrame:SetAlpha (DB_AURA_ALPHA)
		
		if (DB_AURA_SEPARATE_BUFFS) then
			buffFrame2.unit = self.unit
			platerInternal.AlignAuraFrames (buffFrame2)
			--buffFrame2:SetAlpha (DB_AURA_ALPHA)
		end
		Plater.RunScriptTriggersForAuraIcons (self)
	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
	


	--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, true)
					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.PlateFrame.PlaterAnchorFrame:ClearAllPoints()
		--unitFrame.PlateFrame.PlaterAnchorFrame:SetParent(unitFrame.healthBar)
		--unitFrame.PlateFrame.PlaterAnchorFrame:SetPoint("topright", healthBar, "topright")
		--unitFrame.PlateFrame.PlaterAnchorFrame:SetPoint("bottomleft", healthBar, "bottomleft")
		
		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
		
		--unitFrame.PlateFrame.PlaterAnchorFrame:SetParent(unitFrame)
		
		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)
		local secretError = errorMessage and (string.find(lower(errorMessage), "secret value") or string.find(errorMessage, "index is secret")) and true or false
		if secretError and errorContext.globalScriptObject then
			errorContext.globalScriptObject.tmpDisabled = true
		end
		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
		if DevTool then DevTool:AddData(errorContext) end
		local modscript = errorContext.globalScriptObject and errorContext.globalScriptObject.DBScriptObject
		local modscriptInfo = modscript and modscript.url and ("Mod/Script URL: " .. modscript.url .. "\n") or nil
		if BugGrabber then
			if not Plater.fullVersionInfoContainsName then Plater.GetVersionInfo() end
			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, globalScriptObject)
		errorContext.message = contextMessage
		errorContext.globalScriptObject = globalScriptObject
		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
				scriptInfo.GlobalScriptObject.tmpDisabled = 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), 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), 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 scriptInfo.GlobalScriptObject.tmpDisabled then return end
			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), 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)
			if scriptInfo.GlobalScriptObject.tmpDisabled then return end
			--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)
			if scriptInfo.GlobalScriptObject.tmpDisabled then return end
			--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), 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)
			if globalScriptObject.tmpDisabled then return end
			--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), 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, ...)
			if globalScriptObject.tmpDisabled then return end
			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), 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, ...)
			if scriptInfo.GlobalScriptObject.tmpDisabled then return end
			--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), 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)
			if hookInfo.tmpDisabled then return end
			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), 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
			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,
			["UpdateStackingSize"] = false,
			["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,
			["PERF_UNIT_OVERRIDES_BIT"] = {
				["AURA"] = false,
				["THREAT"] = false,
				["CAST"] = false,
			},
			["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,
		["C_TradeInfo"]		= 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 {
			ColorMixin = ColorMixin,
			Vector2DMixin = Vector2DMixin,
			Vector3DMixin = Vector3DMixin,
			ItemLocationMixin = ItemLocationMixin,
			ItemTransmogInfoMixin = ItemTransmogInfoMixin,
			TransmogPendingInfoMixin = TransmogPendingInfoMixin,
			TransmogLocationMixin = TransmogLocationMixin,
			PlayerLocationMixin = PlayerLocationMixin,
		}
		
		--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)
			table.wipe(platerInternal.Scripts.CurrentCastScripts)
			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
				SetPlaterEnvironment(compiledScript)
				
				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,
							tmpDisabled = false,
						}
						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,
					tmpDisabled = false,
				}
				
				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,
			tmpDisabled = false,
		}
		
		--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

		--add castbar scripts to the current list for previews
		if (scriptObject.ScriptType == 2) then
			local currentScripts = platerInternal.Scripts.CurrentCastScripts
			DF.table.addunique(currentScripts, scriptObject.Name)
		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,
						tmpDisabled = false,
					}

					--insert the table just created inthe the triggerCacheTable
					triggerCacheTable[triggerId] = globalScriptObject
					
				else --hot reload and update
					globalScriptObject.HotReload = globalScriptObject.HotReload + 1
					globalScriptObject.DBScriptObject = scriptObject
					globalScriptObject.tmpDisabled = false
				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

	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	




