-- actor container file
-- group members are the actors which will be shown in the window while in standard view mode, most of the times they are players in the same group as the player

	local Details = _G.Details
	local DF = _G.DetailsFramework
	local _
	local addonName, Details222 = ...

	local CONST_CLIENT_LANGUAGE = DF.ClientLanguage

	local GetSpellTexture = C_Spell.GetSpellTexture or GetSpellTexture

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--local pointers

	local _IsInInstance = IsInInstance --api local
	local UnitGUID = UnitGUID --api local
	local setmetatable = setmetatable --lua local
	local bitBand = bit.band --lua local
	local bitBor = bit.bor --lua local
	local tableSort = table.sort --lua local
	local ipairs = ipairs --lua local
	local pairs = pairs --lua local

	local AddUnique = DetailsFramework.table.addunique --framework
	local UnitGroupRolesAssigned = DetailsFramework.UnitGroupRolesAssigned --framework

	local GetNumDeclensionSets = _G.GetNumDeclensionSets
	local DeclineName = _G.DeclineName
	local pet_tooltip_frame = _G.DetailsPetOwnerFinder

	local openRaidLib = LibStub:GetLibrary("LibOpenRaid-1.0", true)

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--constants

	local actorContainer =	Details.container_combatentes

	local atributo_damage =		Details.atributo_damage
	local atributo_heal =			Details.atributo_heal
	local atributo_energy =		Details.atributo_energy
	local atributo_misc =			Details.atributo_misc

	local container_damage =		Details.container_type.CONTAINER_DAMAGE_CLASS
	local container_heal = 		Details.container_type.CONTAINER_HEAL_CLASS
	local container_energy = 		Details.container_type.CONTAINER_ENERGY_CLASS
	local container_energy_target =	Details.container_type.CONTAINER_ENERGYTARGET_CLASS
	local container_misc = 		Details.container_type.CONTAINER_MISC_CLASS
	local container_enemydebufftarget_target = Details.container_type.CONTAINER_ENEMYDEBUFFTARGET_CLASS

	---@type petcontainer
	local petContainer = Details222.PetContainer
	---@type table<guid, petdata>
	local petCache = petContainer.Pets

	--flags
	local REACTION_HOSTILE	=	0x00000040
	local IS_GROUP_OBJECT 	= 	0x00000007
	local REACTION_FRIENDLY	=	0x00000010
	local OBJECT_TYPE_MASK =	0x0000FC00
	local OBJECT_TYPE_OBJECT =	0x00004000
	local OBJECT_TYPE_PETGUARDIAN =	0x00003000
	local OBJECT_TYPE_GUARDIAN =	0x00002000
	local OBJECT_TYPE_PET =		0x00001000
	local OBJECT_TYPE_NPC =		0x00000800
	local OBJECT_TYPE_PLAYER =	0x00000400
	local OBJECT_TYPE_PETS = 	OBJECT_TYPE_PET + OBJECT_TYPE_GUARDIAN

	local SPELLID_SANGUINE_HEAL = 226510
	local sanguineActorName = Details222.GetSpellInfo(SPELLID_SANGUINE_HEAL)


---attempt to get the owner of rogue's Akaari's Soul from Secrect Technique
---@param petGUID string
---@return string|any
---@return string|any
---@return number|any
function Details222.Pets.AkaarisSoulOwner(petGUID)
	local tooltipData = C_TooltipInfo.GetHyperlink("unit:" .. petGUID)
	local args = tooltipData.args

	if (not args) then
		do
			local ownerGUID = tooltipData.guid --tooltipData.guid seems to exists on all akari soul tooltips and point to the owner guid
			if (ownerGUID) then
				if (ownerGUID:find("^P")) then
					local playerGUID = ownerGUID
					local actorObject = Details:GetActorFromCache(playerGUID) --quick cache only exists during conbat
					if (actorObject) then
						return actorObject.nome, playerGUID, actorObject.flag_original
					end

					local guidCache = Details:GetParserPlayerCache() --cache exists until the next combat starts
					local ownerName = guidCache[playerGUID]
					if (ownerName) then
						return ownerName, playerGUID, 0x514
					end
				end
			end
		end

		do
			if (tooltipData.lines) then
				for i = 1, #tooltipData.lines do
					local lineData = tooltipData.lines[i]
					if (lineData.unitToken) then --unit token seems to exists when the add belongs to the "player"
						local ownerGUID = UnitGUID(lineData.unitToken)
						if (ownerGUID and ownerGUID:find("^P")) then
							local playerGUID = ownerGUID
							local actorObject = Details:GetActorFromCache(playerGUID) --quick cache only exists during conbat
							if (actorObject) then
								return actorObject.nome, playerGUID, actorObject.flag_original
							end

							local guidCache = Details:GetParserPlayerCache() --cache exists until the next combat starts
							local ownerName = guidCache[playerGUID]
							if (ownerName) then
								return ownerName, playerGUID, 0x514
							end
						end
					end
				end
			end
		end
	end

	if (not args) then
		--new tooltip data from 10.1.0 seems to not have .args
		return
	end

	local playerGUID
	--iterate among args and find into the value field == guid and it must have guidVal
	for i = 1, #args do --this is erroring in the ptr 10.1.0, as .args doesn't seem to exists on akari soul tooltip
		local arg = args[i]
		if (arg.field == "guid") then
			playerGUID = arg.guidVal
			break
		end
	end

	if (playerGUID) then
		local actorObject = Details:GetActorFromCache(playerGUID) --quick cache only exists during conbat
		if (actorObject) then
			return actorObject.nome, playerGUID, actorObject.flag_original
		end
		local guidCache = Details:GetParserPlayerCache() --cache exists until the next combat starts
		local ownerName = guidCache[playerGUID]
		if (ownerName) then
			return ownerName, playerGUID, 0x514
		end
	end
end


---Determine if the inputted pet string contains any declension variation of the given player name.
---@param tooltipString string
---@param playerName string
---@return boolean hasDeclension
--check pet owner name with correct declension for ruRU locale (from user 'denis-kam' on github)
local find_name_declension = function(tooltipString, playerName)
	--2 - male, 3 - female
	for gender = 3, 2, -1 do
		for declensionSet = 1, GetNumDeclensionSets(playerName, gender) do
			--check genitive case of player name
			local genitive = DeclineName(playerName, gender, declensionSet)
			if tooltipString:find(genitive) then
				--print("found genitive: ", gender, declensionSet, playerName, petTooltip:find(genitive))
				return true
			end
		end
	end
	return false
end

local unitNameTitles = {
    UNITNAME_TITLE_PET,
    UNITNAME_TITLE_COMPANION,
    UNITNAME_TITLE_GUARDIAN,
    UNITNAME_TITLE_MINION,
    UNITNAME_TITLE_CHARM,
    UNITNAME_TITLE_CREATION,
    UNITNAME_TITLE_SQUIRE
}
for i=1, #unitNameTitles do
    unitNameTitles[i] = unitNameTitles[i]:gsub('%%s', '(.*)')
end

---attempt to the owner of a pet using tooltip scan, if the owner isn't found, return nil
---@param petGUID string
---@param petName string
---@return string|nil ownerName
---@return string|nil ownerGUID
---@return integer|nil ownerFlags
	function Details222.Pets.GetPetOwner(petGUID, petName) --this is under the Pets namespace, the new pet system is under the PetContainer namespace

        local ownerGUID, ownerName, lineText

		local cbMode = tonumber(GetCVar("colorblindMode")) or 0
		pet_tooltip_frame:SetOwner(WorldFrame, "ANCHOR_NONE")
		pet_tooltip_frame:SetHyperlink(("unit:" .. petGUID) or "")

            local line = _G['DetailsPetOwnerFinderTextLeft' .. (2 + cbMode)]
            lineText = line and line:GetText()

            if (not lineText or lineText == '') then
                line = _G['DetailsPetOwnerFinderTextLeft1']
                lineText = line and line:GetText()
            end
        end

        if (lineText) then
            for i=1, #unitNameTitles do
                ownerName = lineText:match(unitNameTitles[i])
                if (ownerName) then
                    break
                end
            end

            if (not ownerName) then
                return
            end

            ---@type combat
            local currentCombat = Details:GetCurrentCombat()

            if (not currentCombat) then
                return
            end

            local isInRaid = currentCombat.raid_roster[ownerName]
            if (isInRaid) then
                return UnitGUID(ownerName), ownerName, 0x514
            end
        elseif (ownerGUID and GUIDIsPlayer(ownerGUID)) then
            local playerGUID = ownerGUID
            local actorObject = Details:GetActorFromCache(playerGUID) --quick cache only exists during conbat
            if (actorObject) then
                return actorObject.nome, playerGUID, actorObject.flag_original
            end

            local guidCache = Details:GetParserPlayerCache() --cache exists until the next combat starts
            ownerName = guidCache[playerGUID]
            if (ownerName) then
                return ownerName, playerGUID, 0x514
            end

            if(Details.zone_type == 'arena') then --Attempt to find enemy pet owner
                for enemyName, enemyToken in pairs(Details.arena_enemies) do
                    if(UnitGUID(enemyToken) == ownerGUID) then
                        return enemyName, ownerGUID, 0x548
                    end
                end
            end
        end

        if true then return end

		pet_tooltip_frame:SetOwner(WorldFrame, "ANCHOR_NONE")
		pet_tooltip_frame:SetHyperlink(("unit:" .. petGUID) or "")
		---@type combat
		local currentCombat = Details:GetCurrentCombat()

		local ownerName, ownerGUID, ownerFlags
		if (not currentCombat) then return end --Should exist at all times but load. Just in case.

		for i=1,3 do --Loop through the 3 texts on the PetOwnerFinder tooltip
			local actorNameString = _G["DetailsPetOwnerFinderTextLeft"..i]
			if (actorNameString and not ownerName) then --Tooltip line exists and we haven't found a valid match yes.
				local actorName = actorNameString:GetText()
				if (actorName and type(actorName) == "string") then
					local isInRaid = currentCombat.raid_roster[actorName]
					if (isInRaid) then
						ownerGUID = UnitGUID(actorName)
						ownerName = actorName
						ownerFlags = 0x514
					else
						if (CONST_CLIENT_LANGUAGE == "ruRU") then --If russian client, then test for declensions in the string of text.
							for playerName, _ in pairs(currentCombat.raid_roster) do
								local pName = playerName
								playerName = playerName:gsub("%-.*", "") --remove realm name
								if (find_name_declension(actorName, playerName)) then
									ownerGUID = UnitGUID(pName)
									ownerName = pName
									ownerFlags = 0x514
									break
								end
							end
						else
							for playerName in actorName:gmatch("([^%s']+)") do
								playerName = playerName:gsub(",", "")
                                playerName = playerName:gsub("'s$", "")
								local playerIsOnRaidCache = currentCombat.raid_roster[playerName]
								if (playerIsOnRaidCache) then
									ownerGUID = UnitGUID(playerName)
									ownerName = playerName
									ownerFlags = 0x514
									break
								end
							end
						end
					end
				end
			end
		end

		if (ownerGUID) then
			return ownerName, ownerGUID, ownerFlags
		end
	end

	---return the actor type which is containing on this container
	---@self actorcontainer
	---@return number
	function actorContainer:GetType()
		return self.tipo
	end

	---return the actor object for a given actor name
	---@param actorName string
	---@return table|nil
	function actorContainer:GetActor(actorName)
		local index = self._NameIndexTable[actorName]
		if (index) then
			return self._ActorTable[index]
		end
	end

	---return an actor name which used the spell passed 'spellId'
	---@param spellId number
	---@return string|nil
	function actorContainer:GetSpellSource(spellId)
		local t = self._ActorTable
		for i = 1, #t do
			if (t[i].spells._ActorTable[spellId]) then
				return t[i].nome
			end
		end
	end

	---return the value stored in the 'key' for an actor, the key can be any value stored in the actor table such like 'total', 'damage_taken', 'heal', 'interrupt', etc
	---@param actorName string
	---@param key string
	---@return number
	function actorContainer:GetAmount(actorName, key)
		key = key or "total"
		local index = self._NameIndexTable[actorName]
		if (index) then
			return self._ActorTable[index][key] or 0
		else
			return 0
		end
	end

	---return the total value stored in the 'key' for all actors, the key can be any value stored in the actor table such like 'total', 'damage_taken', 'heal', 'interrupt', etc
	---@param key string
	---@return number
	function actorContainer:GetTotal(key)
		local total = 0
		key = key or "total"
		for _, actor in ipairs(self._ActorTable) do
			total = total + (actor[key] or 0)
		end
		return total
	end

	function actorContainer:GetTotalOnRaid(key, combat)
		local total = 0
		key = key or "total"
		local roster = combat.raid_roster
		for _, actor in ipairs(self._ActorTable) do
			if (roster [actor.nome]) then
				total = total + (actor[key] or 0)
			end
		end
		return total
	end

	---return an ipairs iterator for all actors stored in this Container
	---usage: for index, actorObject in container:ListActors() do
	---@return function
	function actorContainer:ListActors()
		return ipairs(self._ActorTable)
	end

	---return a table with actor[] for all actors stored in this container
	---@return table
	function actorContainer:GetActorTable()
		return self._ActorTable
	end

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--internals

	---create a new actor container, can be a damage container, heal container, resource container or utility container
	---actors can be added by using the method newContainer:GetOrCreateActor(actorGuid, actorName, actorFlags, bShouldCreateActor)
	---actors can be retrieved using the same function above
	---@param containerType number
	---@param combatObject table
	---@param combatId number
	---@return table
	function actorContainer:NovoContainer(containerType, combatObject, combatId)
		local newContainer = {
			funcao_de_criacao = actorContainer:FuncaoDeCriacao(containerType),
			tipo = containerType,
			combatId = combatId,
			---@type actor[]
			_ActorTable = {},
			---@type table<string, number>
			_NameIndexTable = {}
		}

		setmetatable(newContainer, actorContainer)
		return newContainer
	end

	--try to get the actor class from name
	local getActorClass = function(actorObject, actorName, actorFlags, actorSerial)
		--get spec
		local specId = Details.cached_specs[actorSerial]
		if (specId) then
			actorObject:SetSpecId(specId)
		end

		if (not specId and Details.track_specs) then
			local newTimer = Details:ScheduleTimer("GuessSpec", 3, {actorObject, nil, 1})
			Details222.GuessSpecSchedules.Schedules[#Details222.GuessSpecSchedules.Schedules+1] = newTimer
		end

		local engClass = Details:GetUnitClass(actorName or "")

		if (engClass) then
			actorObject.classe = engClass
			return
		end

		if (actorObject.serial and actorObject.serial ~= "") then
			local a, b = pcall(function()
				local _, englishClass = GetPlayerInfoByGUID(actorObject.serial)
				if (englishClass) then
					actorObject.classe = englishClass
					return
				end
			end)
		end

		if (actorFlags) then
			if (bitBand(actorFlags, OBJECT_TYPE_PETGUARDIAN) ~= 0) then
				actorObject.classe = "PET"
				return
			end
		end

		if (specId) then
			local specId, specName, specDescription, specIcon, specRole, specClass = DetailsFramework.GetSpecializationInfoByID(specId)
			if (specClass) then
				actorObject.classe = specClass
				return
			end
		end

		if (actorFlags) then
			--check if the actor is a player
			if (bitBand(actorFlags, OBJECT_TYPE_PLAYER) ~= 0) then
				actorObject.classe = "UNGROUPPLAYER"
				return
			end
		end

		actorObject.classe = "UNKNOW" --it's a typo, can't be changed at this point

		return true
	end

	--check if the nickname fit some minimal rules to be presented to other players
	local checkValidNickname = function(nickname, playerName)
		if (nickname and type(nickname) == "string") then
			nickname = nickname:trim()

			if (nickname == "" or nickname:len() < 2) then
				return false
			end

			if (nickname:len() > 20) then
				return false
			end

			if (not UnitIsInMyGuild(playerName) and playerName ~= Details.playername) then
				return false
			end
		else
			return false
		end

		return true
	end

	local dungeonFollowersNpcs = {}

	--read the actor flag
	local readActorFlag = function(actorObject, ownerActorObject, actorSerial, actorFlags, actorName)
		if (actorFlags) then
			local _, zoneType, instanceDifficultyId = GetInstanceInfo()

			--if the actor is a player
			if (bitBand(actorFlags, OBJECT_TYPE_PLAYER) ~= 0) then

                    if (not Details.ignore_nicktag) then
                        local actorNameAmbiguated = Ambiguate(actorName, "none")
                        local nickname = Details:GetNickname(actorNameAmbiguated, false, true)
                        if nickname then
                            if checkValidNickname(nickname, actorName) then
                                actorObject.displayName = nickname --defaults to player name
                            end
                        end
                    end

					--the actor does not have a nickname, use the character name instead
					if (not actorObject.displayName) then
						if (Details.remove_realm_from_name) then
							actorObject.displayName = actorName:gsub(("%-.*"), "")
						else
							actorObject.displayName = actorName
						end
					end

				--group attributions
					if (zoneType ~= "arena" and (Details.all_players_are_group or Details.immersion_enabled)) then
						actorObject.grupo = true
					end

					--special spells that Details! converted them in actor, add them to the group view. the list of these spells are set within the parser.lua file
					--they are added into the group view as they are considered important imformation
					local spellId = Details.SpecialSpellActorsName[actorObject.nome]
					if (spellId) then
						actorObject.grupo = true
						actorObject.spellicon = GetSpellTexture(spellId)
					end

					--check if this actor can be flagged as a unit in the player's group
					local bIsValidGroupMember = bitBand(actorFlags, IS_GROUP_OBJECT) ~= 0 and actorObject.classe ~= "UNKNOW" and actorObject.classe ~= "UNGROUPPLAYER"
					if (bIsValidGroupMember or Details:IsInCache(actorSerial)) then
						actorObject.grupo = true

						--/dump Details:GetCurrentCombat():GetActor(1, "Captain Garrick").grupo
						if (instanceDifficultyId == 205) then
							dungeonFollowersNpcs[actorName] = true
						end

						--check if this actor is a tank (player)
						if (Details:IsATank(actorSerial)) then
							actorObject.isTank = true
						end
					else
						--if this is a pvp combat and the option to show pvp players as group is enabled
						local currentCombat = Details:GetCurrentCombat()
						if (Details.pvp_as_group and currentCombat.is_pvp and Details.is_in_battleground) then
							actorObject.grupo = true
						end
					end

					--pvp duel - this functionality needs more development, the goal is to show the duel players as group members
					if (Details.duel_candidates[actorSerial]) then
						--check if is recent
						if (Details.duel_candidates[actorSerial]+20 > GetTime()) then
							actorObject.grupo = true
							actorObject.enemy = true
						end
					end

					if (zoneType == "arena") then
						--local my_team_color = GetBattlefieldArenaFaction and GetBattlefieldArenaFaction() or 0

						--my team
						if (actorObject.grupo) then
							actorObject.arena_ally = true
							actorObject.arena_team = 0 -- former my_team_color | forcing the player team to always be the same color

						--enemy team
						else
							actorObject.enemy = true
							actorObject.arena_enemy = true
							actorObject.arena_team = 1 -- former my_team_color

							Details:GuessArenaEnemyUnitId(actorName)
						end

						local playerArenaInfo = Details.arena_table[actorName]

						if (playerArenaInfo) then
							actorObject.role = playerArenaInfo.role
							if (playerArenaInfo.role == "NONE") then
								local role = UnitGroupRolesAssigned and UnitGroupRolesAssigned(actorName)
								if (role and role ~= "NONE") then
									actorObject.role = role
								end
							end
						else
							local amountOpponents = GetNumArenaOpponentSpecs and GetNumArenaOpponentSpecs() or 5
							local found = false
							for i = 1, amountOpponents do
								local name = Details:GetFullName("arena" .. i)
								if (name == actorName) then
									local spec = GetArenaOpponentSpec and GetArenaOpponentSpec(i)
									if (spec) then
										local id, name, description, icon, role, class = DetailsFramework.GetSpecializationInfoByID(spec) --thanks pas06
										actorObject.role = role
										actorObject.classe = class
										actorObject.enemy = true
										actorObject.arena_enemy = true
										found = true
									end
								end
							end

							local role = UnitGroupRolesAssigned and UnitGroupRolesAssigned(actorName)
							if (role and role ~= "NONE") then
								actorObject.role = role
								found = true
							end

							if (not found and actorName == Details.playername) then
								local role = UnitGroupRolesAssigned("player")
								if (role and role ~= "NONE") then
									actorObject.role = role
								end
							end
						end

						actorObject.grupo = true
					end

				--player custom bar color
				--at this position in the code, the color will replace colors from arena matches
				if (Details.use_self_color) then
					if (actorName == Details.playername) then
						actorObject.customColor = Details.class_colors.SELF
					end
				end

			--does this actor has an owner? (a.k.a. is a pet)
			elseif (ownerActorObject) then
				actorObject.owner = ownerActorObject
				actorObject.ownerName = ownerActorObject.nome

				if (_IsInInstance() and Details.remove_realm_from_name) then
					actorObject.displayName = actorName:gsub(("%-.*"), ">")
				else
					actorObject.displayName = actorName
				end

				--local npcId = Details:GetNpcIdFromGuid(actorSerial)
				--local petCustomname = Details222.Pets.GetPetNameFromCustomSpells(actorObject.displayName, spellId, npcId)
			else
				--anything else that isn't a player or a pet
				actorObject.displayName = actorName
			end

			--check if is hostile
			if (bitBand(actorFlags, REACTION_HOSTILE) ~= 0) then
				if (bitBand(actorFlags, OBJECT_TYPE_PLAYER) == 0) then
					--is hostile and isn't a player
					if (bitBand(actorFlags, OBJECT_TYPE_PETGUARDIAN) == 0) then
						--isn't a pet or guardian
						actorObject.monster = true
					end

					if (actorSerial and type(actorSerial) == "string") then
						local npcID = Details:GetNpcIdFromGuid(actorSerial)
						if (npcID and not Details.npcid_pool[npcID] and type(npcID) == "number") then
							Details.npcid_pool [npcID] = actorName
						end
					end
				end
			end

			if (instanceDifficultyId == 205) then
				if (dungeonFollowersNpcs[actorName]) then
					actorObject.grupo = true
				end
			end
		end
	end

	--pet black list locally for this file
	local petBlackList = {}

	local petOwnerFound = function(ownerName, petGuid, petName, petFlags, self, ownerGuid, ownerFlags)
		local ownerGuid = ownerGuid or UnitGUID(ownerName)
		if (ownerGuid) then
			-- 0xA00 is the flag for NPC controlled, NPC unit. 0x500 is the flag for Player Controlled, Player Unit.
			-- Or those together with the last 2 hex bits for reaction/affiliation to 'guess' the correct flags.
			if not ownerFlags then
				local npcControlled = bitBand(petFlags, 0x200) ~= 0
				ownerFlags = bitBor(npcControlled and 0xA00 or 0x500, bitBand(petFlags, 0xFF))
			end

			petContainer.AddPet(petGuid, petName, petFlags, ownerGuid, ownerName, ownerFlags)
			--hashName is "petName <ownerName>"
			local hashName, ownerName, ownerGUID, ownerFlags = petContainer.GetOwner(petGuid, petName)

			local petOwnerActorObject

			if (hashName and ownerName) then
				petName = hashName
				petOwnerActorObject = self:PegarCombatente(ownerGUID, ownerName, ownerFlags, true)
			end

			return petName, petOwnerActorObject
		end
	end

	---get an actor from the container, if the actor doesn't exists, and the bShouldCreateActor is true, create a new actor
	---this function is an alias for PegarCombatente which is the function name is in portuguese
	---@param actorSerial string
	---@param actorName string
	---@param actorFlags number
	---@param bShouldCreateActor boolean
	---@return actor|nil, actor|nil, actorname|nil
	function actorContainer:PegarCombatente(actorSerial, actorName, actorFlags, bShouldCreateActor)
		return self:GetOrCreateActor(actorSerial, actorName, actorFlags, bShouldCreateActor)
	end

	---@param actorSerial string
	---@param actorName string
	---@param actorFlags number
	---@param bShouldCreateActor boolean
	---@return actor|nil, actor|nil, actorname|nil
	function actorContainer:GetOrCreateActor(actorSerial, actorName, actorFlags, bShouldCreateActor)
		--need to check if the actor is a pet
		local petOwnerObject
		actorSerial = actorSerial or "ns"

		--check if this actor is a pet and the pet is in the pet cache
		if (petContainer.IsPetInCache(actorSerial)) then --this is a registered pet
			--hashName is "petName <ownerName>"
			--actorSerial: petGuid, actorName: petName
			local hashName, ownerName, ownerGuid, ownerFlag = petContainer.GetOwner(actorSerial, actorName) --hashName, ownerName, ownerGuid, ownerFlags

			if (hashName and ownerName and ownerGuid and ownerGuid ~= actorSerial and ownerFlag) then
				actorName = hashName
				petOwnerObject = self:PegarCombatente(ownerGuid, ownerName, ownerFlag, true)
			end

			if (Details222.Debug.DebugPets or Details222.Debug.DebugPlayerPets) then
				Details:Msg("DebugPets|ActorContainer|petContainer.IsPetInCache(actorSerial) = true")
				if (hashName) then
					Details:Msg("DebugPets|ActorContainer|Owner Found In Pet Cache|OwnerName:", ownerName, "Actor Hash:", hashName, "petOwnerObject:", petOwnerObject)
				else
					Details:Msg("DebugPets|ActorContainer|Pet Is Orphan|petContainer.GetOwner(", actorSerial, actorName, ") == nil")
				end
			end

		--this actor isn't in the pet cache
		elseif (not petBlackList[actorSerial]) then --check if is a pet
			--try to find the owner
			if (actorFlags and bitBand(actorFlags, OBJECT_TYPE_PETGUARDIAN) ~= 0) then
				--hashName is "petName <ownerName>"
				local hashName, ownerName, ownerGuid, ownerFlags = petContainer.GetOwner(actorSerial, actorName) --hashName, ownerName, ownerGuid, ownerFlags
				if (ownerName and ownerGuid) then
					--don't pass ownerFlags just in case the cached owner happens to be an enemy from last combat, but ally now.
					local newPetName, ownerObject = petOwnerFound(ownerName, actorSerial, actorName, actorFlags, self, ownerGuid)
					if (newPetName and ownerObject) then
						actorName, petOwnerObject = newPetName, ownerObject
					end
				end
			end

			petBlackList[actorSerial] = true
		end

		--get the actor index in the hash map
		local actorIndex = self._NameIndexTable[actorName]
		if (actorIndex) then
			return self._ActorTable[actorIndex], petOwnerObject, actorName
		end

		if (not bShouldCreateActor) then
			return
		end

		---@type actor
		local newActor = self.funcao_de_criacao(_, actorSerial, actorName)
		newActor.nome = actorName
		newActor.flag_original = actorFlags
		newActor.serial = actorSerial
		newActor.classe = "UNKNOW"

		local forceClass

		--get the aID (actor id)
		if (GUIDIsNPC(actorSerial)) then
			newActor.aID = tostring(Details:GetNpcIdFromGuid(actorSerial))

			--immersion stuff
			if (Details.immersion_special_units) then
				local shouldBeInGroup, class = Details.Immersion.IsNpcInteresting(newActor.aID)
				newActor.grupo = shouldBeInGroup
				if (class) then
					newActor.classe = class
					forceClass = newActor.classe
				end
			end

		elseif (GUIDIsPlayer(actorSerial)) then
			newActor.aID = GetPlayerUIDFromGUID(actorSerial)

		else
			newActor.aID = ""
		end

		--check ownership
		if (petOwnerObject and Details.immersion_pets_on_solo_play) then
			if (Details.playername == petOwnerObject.nome) then
				if (not Details.in_group) then
					newActor.grupo = true
				end
			end
		end

		if (self.tipo == container_damage) then --containerType damage
			local shouldScanOnce = getActorClass(newActor, actorName, actorFlags, actorSerial)
			readActorFlag(newActor, petOwnerObject, actorSerial, actorFlags, actorName)

			if (petOwnerObject) then
				AddUnique(petOwnerObject.pets, actorName)
			end

			if (newActor.grupo and Details.in_combat) then
				Details.cache_damage_group[#Details.cache_damage_group+1] = newActor
			end

			if (newActor.isTank) then
				newActor.avoidance = Details:CreateActorAvoidanceTable()
			end

		elseif (self.tipo == container_heal) then --containerType healing
			local shouldScanOnce = getActorClass(newActor, actorName, actorFlags, actorSerial)
			readActorFlag(newActor, petOwnerObject, actorSerial, actorFlags, actorName)

			if (petOwnerObject) then
				AddUnique(petOwnerObject.pets, actorName)
			end

			if (newActor.grupo and Details.in_combat) then
				Details.cache_healing_group[#Details.cache_healing_group+1] = newActor
			end

		elseif (self.tipo == container_energy) then --containerType resources
			local shouldScanOnce = getActorClass(newActor, actorName, actorFlags, actorSerial)
			readActorFlag(newActor, petOwnerObject, actorSerial, actorFlags, actorName)

			if (petOwnerObject) then
				AddUnique(petOwnerObject.pets, actorName)
			end

		elseif (self.tipo == container_misc) then --containerType utility
			local shouldScanOnce = getActorClass(newActor, actorName, actorFlags, actorSerial)
			readActorFlag(newActor, petOwnerObject, actorSerial, actorFlags, actorName)

			if (petOwnerObject) then
				AddUnique(petOwnerObject.pets, actorName)
			end
		end

		--sanguine affix
		if (actorName == sanguineActorName) then
			newActor.grupo = true
		end

		--enemy player
		if (Details.zone_type == "pvp") then
			if (bitBand(actorFlags, REACTION_HOSTILE) ~= 0) then --is hostile
				newActor.enemy = true
			end
		end

		if (newActor.classe == "UNGROUPPLAYER") then --is a player
			if (bitBand(actorFlags, REACTION_HOSTILE) ~= 0) then --is hostile
				newActor.enemy = true
			end
		end

		--battleground
		if (Details.is_in_battleground or Details.is_in_arena) then
			newActor.pvp = true
		end

		local nextActorIndex = #self._ActorTable+1
		self._ActorTable[nextActorIndex] = newActor
		self._NameIndexTable[actorName] = nextActorIndex

		--only happens with npcs from immersion feature
		if (forceClass) then
			newActor.classe = forceClass
		end

		return newActor, petOwnerObject, actorName
	end

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--core
	function Details:AddToNpcIdCache(actor) --not called anywhere
		if (flag and serial) then
			if (bitBand (flag, REACTION_HOSTILE) ~= 0 and bitBand (flag, OBJECT_TYPE_NPC) ~= 0 and bitBand (flag, OBJECT_TYPE_PETGUARDIAN) == 0) then
				local npc_id = Details:GetNpcIdFromGuid (serial)
				if (npc_id) then
					Details.cache_npc_ids [npc_id] = nome
				end
			end
		end
	end

	function Details:ClearCCPetsBlackList()
		Details:Destroy(petBlackList)
	end

	function actorContainer:FuncaoDeCriacao(tipo)
		if (tipo == container_damage) then
			return atributo_damage.NovaTabela

		elseif (tipo == container_heal) then
			return atributo_heal.NovaTabela

		elseif (tipo == container_energy) then
			return atributo_energy.NovaTabela

		elseif (tipo == container_misc) then
			return atributo_misc.NovaTabela
		end
	end

	local bykey
	local sort = function(t1, t2)
		return (t1 [bykey] or 0) > (t2 [bykey] or 0)
	end

	function actorContainer:SortByKey(key)
		assert(type(key) == "string", "Container:SortByKey() expects a keyname on parameter 1.")
		bykey = key
		tableSort(self._ActorTable, sort)
		self:remapear()
	end

	---remove an actor from the container, by removing this way, the container does not need to be remapped
	---@param self actorcontainer
	---@param actorObject actor
	function actorContainer:RemoveActor(actorObject)
		local nameMap = self._NameIndexTable
		local actorList = self._ActorTable

		local actorName = actorObject.nome
		if (actorName) then
			local actorIndex = nameMap[actorObject.nome]
			nameMap[actorObject.nome] = nil
			if (actorObject == actorList[actorIndex]) then
				table.remove(actorList, actorIndex)
			end
		end

		self:Remap()
	end

	---remove all destroyed actors from the container, must to be called after a possible DestroyActor() call
	function actorContainer:Cleanup()
		local actorList = self._ActorTable
		for i = #actorList, 1, -1 do
			local actorObject = actorList[i]
			if (actorObject.__destroyed) then
				table.remove(actorList, i)
			end
		end
		self:Remap()
	end

	function actorContainer:Remap() --alias
		return self:remapear()
	end

	function actorContainer:remapear()
		local namingMap = self._NameIndexTable or {}
		Details:Destroy(namingMap)

		local actorList = self._ActorTable
		for i = 1, #actorList do
			local playerName = actorList[i].nome
			if (playerName) then
				namingMap[playerName] = i
			else
				Details:Msg("actorContainer:Remap() found an actor without a name, playerName:", playerName, "__destroyed:", actorList[i].__destroyed)
			end
		end

		self._NameIndexTable = namingMap
	end

	function Details.refresh:r_container_combatentes(container) --runs on login from meta.lua
		--set the metatable, __index and the function which will create new actors in the container
		setmetatable(container, Details.container_combatentes)
		container.__index = Details.container_combatentes
		container.funcao_de_criacao = actorContainer:FuncaoDeCriacao(container.tipo)

		--rebuild the actor map
		container:Remap()
	end

	function Details.clear:c_container_combatentes(container)
		container.__index = nil
		container.need_refresh = nil
		container.funcao_de_criacao = nil
	end

	function Details.clear:c_container_combatentes_index(container)
		container._NameIndexTable = nil
	end
