
local Details = _G.Details
local tocName, Details222 = ...

local _
local pairs = pairs --lua local
local ipairs = ipairs --lua local
local rawget = rawget --lua local
local setmetatable = setmetatable --lua local
local _table_remove = table.remove --lua local
local _bit_band = bit.band --lua local
local _time = time --lua local

local InCombatLockdown = InCombatLockdown --wow api local

local classDamage =	Details.atributo_damage --details local
local classHeal =		Details.atributo_heal --details local
local classEnergy =		Details.atributo_energy --details local
local classUtility =		Details.atributo_misc --details local

local classTypeDamage = Details.atributos.dano
local classTypeHeal = Details.atributos.cura
local classTypeEnergy = Details.atributos.e_energy
local classTypeUtility = Details.atributos.misc

	--restore actor containers indexes e metatables
	function Details:RestoreOverallMetatables()
		local bIsInInstance = select(1, IsInInstance())

		---@type combat
		local combatObjectOverall = Details.tabela_overall
		combatObjectOverall.overall_refreshed = true
		combatObjectOverall.__call = Details.call_combate

		Details.refresh:r_combate(combatObjectOverall)

		Details.refresh:r_container_combatentes(combatObjectOverall[classTypeDamage])
		Details.refresh:r_container_combatentes(combatObjectOverall[classTypeHeal])
		Details.refresh:r_container_combatentes(combatObjectOverall[classTypeEnergy])
		Details.refresh:r_container_combatentes(combatObjectOverall[classTypeUtility])
		Details.refresh:r_container_combatentes(combatObjectOverall[5]) --ghost container

		local todos_atributos = {
			combatObjectOverall[classTypeDamage]._ActorTable,
			combatObjectOverall[classTypeHeal]._ActorTable,
			combatObjectOverall[classTypeEnergy]._ActorTable,
			combatObjectOverall[classTypeUtility]._ActorTable
		}

		for classType = 1, DETAILS_COMBAT_AMOUNT_CONTAINERS do
			local actorContainer = combatObjectOverall[classType]
			local actorTable = actorContainer._ActorTable
			for i = 1, #actorTable do
				---@type actor
				local thisActor = actorTable[i]
				local actorName = thisActor.nome

				if (bIsInInstance and Details.remove_realm_from_name) then
					thisActor.displayName = actorName:gsub(("%-.*"), "")
				elseif (Details.remove_realm_from_name) then
					thisActor.displayName = actorName:gsub(("%-.*"), "") --"%*"
				else
					thisActor.displayName = actorName
				end

				if (classType == classTypeDamage) then
					Details.refresh:r_atributo_damage(thisActor)

				elseif (classType == classTypeHeal) then
					Details.refresh:r_atributo_heal(thisActor)

				elseif (classType == classTypeEnergy) then
					Details.refresh:r_atributo_energy(thisActor)

				elseif (classType == classTypeUtility) then
					Details.refresh:r_atributo_misc(thisActor)
				end

				if (thisActor.ownerName) then
					thisActor.owner = combatObjectOverall(classType, thisActor.ownerName)
					if (not thisActor.owner) then
						Details:Msg("found orphan pet (overall), owner not found: ", thisActor.ownerName, " - ", thisActor.nome)
					end
				end
			end
		end
	end

	--restore actor containers indexes e metatables
	function Details:RestoreMetatables() --called from Details222.LoadSavedVariables.CombatSegments()
		--pet table
		setmetatable(Details.tabela_pets, Details.container_pets)

		--segment container
		setmetatable(Details.tabela_historico, Details.historico)

		---@type combat
		local overallCombatObject = Details.tabela_overall

		---@type combat[]
		local segmentsTable = Details:GetCombatSegments()

		--retore the call "combat()" functionality
		for _, combatObject in ipairs(segmentsTable) do
			combatObject.__call = Details.call_combate
		end

		--true if the overall data was saved and restored
		local bHadOverallDataSaved = overallCombatObject.overall_refreshed

		if (not bHadOverallDataSaved) then
			overallCombatObject.start_time = GetTime()
			overallCombatObject.end_time = GetTime()
		end

		overallCombatObject.segments_added = overallCombatObject.segments_added or {}

		local bIsInInstance = IsInInstance()

		--inicia a recupera��o das tabelas e montagem do overall
		if (#segmentsTable > 0) then
			for index, thisCombatObject in ipairs(segmentsTable) do
				---@cast thisCombatObject combat

				--set the metatable, __call and __index
				Details.refresh:r_combate(thisCombatObject)

				--related to overall data
				if (not bHadOverallDataSaved and thisCombatObject.overall_added) then
					--overall data endTime
					if (thisCombatObject.end_time and thisCombatObject.start_time) then
						overallCombatObject.start_time = overallCombatObject.start_time - (thisCombatObject.end_time - thisCombatObject.start_time)
					end

					--overall data startTime
					if (overallCombatObject.data_inicio == 0) then
						overallCombatObject.data_inicio = thisCombatObject.data_inicio or 0
					end

					--overall data finished time
					overallCombatObject.data_fim = thisCombatObject.data_fim or overallCombatObject.data_fim

					--overall data enemy name
					if (not Details.tabela_overall.overall_enemy_name) then
						Details.tabela_overall.overall_enemy_name = thisCombatObject.is_boss and thisCombatObject.is_boss.name or thisCombatObject.enemy
					else
						if (Details.tabela_overall.overall_enemy_name ~= (thisCombatObject.is_boss and thisCombatObject.is_boss.name or thisCombatObject.enemy)) then
							Details.tabela_overall.overall_enemy_name = "-- x -- x --"
						end
					end

					--overall data segments added
					local dateStart, dateEnd = thisCombatObject:GetDate()
					table.insert(overallCombatObject.segments_added, {name = thisCombatObject:GetCombatName(true), elapsed = thisCombatObject:GetCombatTime(), clock = dateStart})
				end

				--ghost container (container for custom displays, this is not a real container)
				if (thisCombatObject[5]) then
					Details.refresh:r_container_combatentes(thisCombatObject[5])
				end

				local damageActorContainer = thisCombatObject[classTypeDamage]
				local healActorContainer = thisCombatObject[classTypeHeal]
				local resourcesActorContainer = thisCombatObject[classTypeEnergy]
				local utilityActorContainer = thisCombatObject[classTypeUtility]

				--recupera a meta e indexes dos 4 container
				Details.refresh:r_container_combatentes(damageActorContainer)
				Details.refresh:r_container_combatentes(healActorContainer)
				Details.refresh:r_container_combatentes(resourcesActorContainer)
				Details.refresh:r_container_combatentes(utilityActorContainer)

				for classType = 1, DETAILS_COMBAT_AMOUNT_CONTAINERS do
					local actorContainer = thisCombatObject[classType]
					local actorTable = actorContainer._ActorTable
					for i = 1, #actorTable do
						---@type actor
						local actorObject = actorTable[i]
						local actorName = actorObject.nome

						--set back the display name (isn't saved with the object)
						if (bIsInInstance and Details.remove_realm_from_name) then
							actorObject.displayName = actorName:gsub(("%-.*"), "")
						elseif (Details.remove_realm_from_name) then
							actorObject.displayName = actorName:gsub(("%-.*"), "")
						else
							actorObject.displayName = actorName
						end

						if (classType == classTypeDamage) then
							if (thisCombatObject.overall_added and not bHadOverallDataSaved) then
								--add the actorObject into another combat, if does not exists there, create it, if exists sum the values
								local bRefreshActor = true
								classDamage:AddToCombat(actorObject, bRefreshActor, overallCombatObject)
							else
								Details.refresh:r_atributo_damage(actorObject)
							end

						elseif (classType == classTypeHeal) then
							if (thisCombatObject.overall_added and not bHadOverallDataSaved) then
								local bRefreshActor = true
								classHeal:AddToCombat(actorObject, bRefreshActor, overallCombatObject)
							else
								Details.refresh:r_atributo_heal(actorObject)
							end

						elseif (classType == classTypeEnergy) then
							if (thisCombatObject.overall_added and not bHadOverallDataSaved) then
								classEnergy:r_connect_shadow (actorObject)
							else
								classEnergy:r_onlyrefresh_shadow (actorObject)
							end

						elseif (classType == classTypeUtility) then
							if (thisCombatObject.overall_added and not bHadOverallDataSaved) then
								classUtility:r_connect_shadow (actorObject)
							else
								classUtility:r_onlyrefresh_shadow (actorObject)
							end
						end
					end
				end

				--link pets to owners
				for class_type = 1, DETAILS_COMBAT_AMOUNT_CONTAINERS do
					local actorContainer = thisCombatObject[class_type]
					local actorTable = actorContainer._ActorTable
					for i = 1, #actorTable do
						---@type actor
						local actorObject = actorTable[i]
						if (actorObject.ownerName) then --name of the pet owner
							actorObject.owner = thisCombatObject(class_type, actorObject.ownerName)
							--technically, if the owner isn't found, this is an orphan and it could be removed from the combat
						end
					end
				end
			end
		end
	end

	function Details:DoInstanceCleanup()
		for _, instanceObject in ipairs(Details.tabela_instancias) do
			---@cast instanceObject instance

			if (instanceObject.StatusBar.left) then
				instanceObject.StatusBarSaved = {
					["left"] = instanceObject.StatusBar.left.real_name or "NONE",
					["center"] = instanceObject.StatusBar.center.real_name or "NONE",
					["right"] = instanceObject.StatusBar.right.real_name or "NONE",
				}
				instanceObject.StatusBarSaved.options = {
					[instanceObject.StatusBarSaved.left] = instanceObject.StatusBar.left.options,
					[instanceObject.StatusBarSaved.center] = instanceObject.StatusBar.center.options,
					[instanceObject.StatusBarSaved.right] = instanceObject.StatusBar.right.options
				}
			end

			--erase all widgets frames
			instanceObject.scroll = nil
			instanceObject.baseframe = nil
			instanceObject.bgframe = nil
			instanceObject.bgdisplay = nil
			instanceObject.freeze_icon = nil
			instanceObject.freeze_texto = nil
			instanceObject.barras = nil
			instanceObject.showing = nil
			instanceObject.agrupada_a = nil
			instanceObject.grupada_pos = nil
			instanceObject.agrupado = nil
			instanceObject._version = nil
			instanceObject.h_baixo = nil
			instanceObject.h_esquerda = nil
			instanceObject.h_direita = nil
			instanceObject.h_cima = nil
			instanceObject.break_snap_button = nil
			instanceObject.alert = nil
			instanceObject.StatusBar = nil
			instanceObject.consolidateFrame = nil
			instanceObject.consolidateButtonTexture = nil
			instanceObject.consolidateButton = nil
			instanceObject.lastIcon = nil
			instanceObject.firstIcon = nil
			instanceObject.menu_attribute_string = nil
			instanceObject.wait_for_plugin_created = nil
			instanceObject.waiting_raid_plugin = nil
			instanceObject.waiting_pid = nil
		end

		--unused instances
		for _, instanceObject in ipairs(Details.unused_instances) do
			---@cast instanceObject instance

			if (instanceObject.StatusBar.left) then
				instanceObject.StatusBarSaved = {
					["left"] = instanceObject.StatusBar.left.real_name or "NONE",
					["center"] = instanceObject.StatusBar.center.real_name or "NONE",
					["right"] = instanceObject.StatusBar.right.real_name or "NONE",
				}
				instanceObject.StatusBarSaved.options = {
					[instanceObject.StatusBarSaved.left] = instanceObject.StatusBar.left.options,
					[instanceObject.StatusBarSaved.center] = instanceObject.StatusBar.center.options,
					[instanceObject.StatusBarSaved.right] = instanceObject.StatusBar.right.options
				}
			end

			--erase all widgets frames
			instanceObject.scroll = nil
			instanceObject.baseframe = nil
			instanceObject.bgframe = nil
			instanceObject.bgdisplay = nil
			instanceObject.freeze_icon = nil
			instanceObject.freeze_texto = nil
			instanceObject.barras = nil
			instanceObject.showing = nil
			instanceObject.agrupada_a = nil
			instanceObject.grupada_pos = nil
			instanceObject.agrupado = nil
			instanceObject._version = nil
			instanceObject.h_baixo = nil
			instanceObject.h_esquerda = nil
			instanceObject.h_direita = nil
			instanceObject.h_cima = nil
			instanceObject.break_snap_button = nil
			instanceObject.alert = nil
			instanceObject.StatusBar = nil
			instanceObject.consolidateFrame = nil
			instanceObject.consolidateButtonTexture = nil
			instanceObject.consolidateButton = nil
			instanceObject.lastIcon = nil
			instanceObject.firstIcon = nil
			instanceObject.menu_attribute_string = nil
			instanceObject.wait_for_plugin_created = nil
			instanceObject.waiting_raid_plugin = nil
			instanceObject.waiting_pid = nil
		end
	end

	---remove all .owner references from actors, this unlink pets from owners but still leave the actor.ownerName member to rebuild later
	function Details:RemoveOwnerFromPets()
		---@type combat[]
		local segmentsTable = Details:GetCombatSegments() or {}

		local bOverallAdded
		if (not Details.overall_clear_logout) then
			table.insert(segmentsTable, Details.tabela_overall)
			bOverallAdded = true
		end

		for _, combatObject in ipairs(segmentsTable) do
			---@cast combatObject combat
			for _, actorContainer in ipairs(combatObject) do
				---@cast actorContainer actorcontainer
				for _, actorObject in ipairs(actorContainer._ActorTable) do
					---@cast actorObject actor
					actorObject.owner = nil
				end
			end
		end

		if (bOverallAdded) then
			table.remove(segmentsTable, #segmentsTable)
		end
	end

	function Details:DoClassesCleanup()
		---@type combat[]
		local segmentsTable = Details:GetCombatSegments() or {}
		local bOverallAdded = false
		if (not Details.overall_clear_logout) then
			--add the overall segment to the cleanup within the other segments
			--it is removed after the cleanup
			table.insert(segmentsTable, Details.tabela_overall)
			bOverallAdded = true
		end

		for index, combatObject in ipairs(segmentsTable) do
			---@cast combatObject combat
			for classType, actorContainer in ipairs(combatObject) do
				---@cast actorContainer actorcontainer
				for _, actorObject in ipairs(actorContainer._ActorTable) do --low level loop for performance
					---@cast actorObject actor

					actorObject.displayName = nil
					actorObject.minha_barra = nil

					if (classType == classTypeDamage) then
						Details.clear:c_atributo_damage(actorObject)

					elseif (classType == classTypeHeal) then
						Details.clear:c_atributo_heal(actorObject)

					elseif (classType == classTypeEnergy) then
						Details.clear:c_atributo_energy(actorObject)

					elseif (classType == classTypeUtility) then
						Details.clear:c_atributo_misc(actorObject)
					end
				end
			end
		end

		if (bOverallAdded) then
			--remove the overall segment from the regular segments
			table.remove(segmentsTable, #segmentsTable)
		end
	end

	function Details:DoContainerCleanup()
		---@type combat[]
		local segmentsTable = Details:GetCombatSegments() or {}
		local bOverallAdded
		if (not Details.overall_clear_logout) then
			table.insert(segmentsTable, Details.tabela_overall)
			bOverallAdded = true
		end

		for _, combatObject in ipairs(segmentsTable) do
			---@cast combatObject combat
			Details.clear:c_combate(combatObject)
			for _, actorContainer in ipairs(combatObject) do
				---@cast actorContainer actorcontainer
				Details.clear:c_container_combatentes(actorContainer)
			end
		end

		if (bOverallAdded) then
			table.remove(segmentsTable, #segmentsTable)
		end
	end

	function Details:DoContainerIndexCleanup()
		---@type combat[]
		local segmentsTable = Details:GetCombatSegments() or {}
		local bOverallAdded
		if (not Details.overall_clear_logout) then
			table.insert(segmentsTable, Details.tabela_overall)
			bOverallAdded = true
		end

		for _, combatObject in ipairs(segmentsTable) do
			for _, actorContainer in ipairs(combatObject) do
				Details.clear:c_container_combatentes_index(actorContainer)
			end
		end

		if (bOverallAdded) then
			table.remove(segmentsTable, #segmentsTable)
		end
	end

	--limpa indexes e metatables
	function Details:PrepareTablesForSave()
		Details.clear_ungrouped = true

		--clear instances
		Details:DoInstanceCleanup()
		Details:DoClassesCleanup()
		Details:DoContainerCleanup()

		--clear combats
		---@type combat[]
		local combatTables = {}
		---@type combat[]
		local segmentsTable = Details:GetCombatSegments() or {}

		for i = #segmentsTable, 1, -1  do
			---@type combat
			local combatObject = segmentsTable[i]
			if (combatObject.__destroyed) then
				table.remove(segmentsTable, i)
			end
		end

		--remove segments marked as 'trash'
		for i = #segmentsTable, 1, -1  do
			---@type combat
			local combatObject = segmentsTable[i]
			if (combatObject:IsTrash()) then
				table.remove(segmentsTable, i)
			end
		end

		segmentsTable = Details:GetCombatSegments() or {}

		--remove segments > of the segment limit to save
		if (Details.segments_amount_to_save and Details.segments_amount_to_save < Details.segments_amount) then
			for i = Details.segments_amount, Details.segments_amount_to_save + 1, -1  do
				if (segmentsTable[i]) then
					table.remove(segmentsTable, i)
				end
			end
		end

		--clear overall segment
		if (Details.overall_clear_logout) then
			Details.tabela_overall = nil
			_detalhes_database.tabela_overall = nil
		else
			---@type combat
			local overallCombatObject = Details.tabela_overall

			--this is a cleanup for overall data (overall)
			if (Details.clear_ungrouped) then
				--deal with actor which could potentially be removed from the database
				for containerId = 1, DETAILS_COMBAT_AMOUNT_CONTAINERS do
					local actorContainer = overallCombatObject:GetContainer(containerId)
					local actorTable = actorContainer:GetActorTable()
					for actorIndex = #actorTable, 1, -1 do
						---@type actor
						local actorObject = actorTable[actorIndex]

						for funcName in pairs(Details222.Mixins.ActorMixin) do
							actorObject[funcName] = nil
						end

						if (Details222.Actors.IsDisposable(actorObject) and not actorObject.owner) then
							Details222.SaveVariables.LogEvent("actor removed " .. actorObject.nome .. " (disposable)")
							Details:DestroyActor(actorObject, actorContainer, overallCombatObject)
						end
					end

					actorContainer:Cleanup()
				end
			end

			--find orphans, finding orphans should be done when deleting an actor, it should iterate among the actor pets and delete them as well
			--now deal with pets without owners (overall)
			for containerId = 1, DETAILS_COMBAT_AMOUNT_CONTAINERS do
				local actorContainer = overallCombatObject:GetContainer(containerId)
				local actorTable = actorContainer:GetActorTable()
				for actorIndex = #actorTable, 1, -1 do
					---@type actor
					local actorObject = actorTable[actorIndex]
					if (actorObject.owner) then
						--does this pet owner got removed from the database?
						if (not actorObject.owner.serial) then
							Details222.SaveVariables.LogEvent("actor removed " .. actorObject.nome .. " (owner not found)")
							Details:DestroyActor(actorObject, actorContainer, overallCombatObject)
						end
					end
				end

				actorContainer:Cleanup()
			end
		end

		for i, combatObject in ipairs(segmentsTable) do
			---@cast combatObject combat
			combatTables[#combatTables+1] = combatObject
		end

		--this is a cleanup for combat stored in the segment list
		for combatIndex, combatObject in ipairs(combatTables) do
			---@cast combatObject combat

			--clear the time data (chart data) - if the option to cleanup on logout is enabled
			if (Details.clear_graphic) then
				Details:Destroy(combatObject.TimeData)
				combatObject.TimeData = {}
			end

			local bIsBossEncounter = combatObject.is_boss
			if (bIsBossEncounter) then
				if (combatObject.pvp) then
					bIsBossEncounter = false
				end
			end

			if (not combatObject.is_mythic_dungeon_segment and Details.clear_ungrouped) then
				for i = 1, DETAILS_COMBAT_AMOUNT_CONTAINERS do
					---@type actorcontainer
					local actorContainer = combatObject:GetContainer(i)
					if (actorContainer) then
						local actorTable = actorContainer:GetActorTable()
						for o = #actorTable, 1, -1 do
							---@type actor
							local actorObject = actorTable[o]

							for funcName in pairs(Details222.Mixins.ActorMixin) do
								actorObject[funcName] = nil
							end

							if (not actorObject.owner and not actorObject.grupo and not actorObject.boss and not actorObject.boss_fight_component and not bIsBossEncounter and not actorObject.pvp_component and not actorObject.fight_component) then
								Details222.SaveVariables.LogEvent("actor removed " .. actorObject.nome .. " (ungrouped)")
								Details:DestroyActor(actorObject, actorContainer, combatObject)
							end
						end
						actorContainer:Cleanup()

						--find orphans
						for o = #actorTable, 1, -1 do
							---@type actor
							local actorObject = actorTable[o]
							if (actorObject.owner) then
								--does this pet owner got removed from the database?
								if (not actorObject.owner.serial) then
									Details222.SaveVariables.LogEvent("actor removed " .. actorObject.nome .. " (orphan)")
									Details:DestroyActor(actorObject, actorContainer, combatObject)
								end
							end
						end

						actorContainer:Cleanup()
					end
				end
			end
		end

		--panic mode (in case the player disconnets during a boss encounter, drop all tables to speedup the login and login back process)
		if (Details.segments_panic_mode and Details.can_panic_mode) then
			if (Details.tabela_vigente.is_boss) then
				Details.tabela_historico = Details.historico:CreateNewSegmentDatabase()
			end
		end

		--clear all segments on logoff
		if (Details.data_cleanup_logout) then
			Details.tabela_historico = Details.historico:CreateNewSegmentDatabase()
			Details.tabela_overall = nil
			_detalhes_database.tabela_overall = nil
		end

		--clear customs
		Details.clear:c_atributo_custom()

		--clear owners
		Details:RemoveOwnerFromPets()

		--clear container indexes
		Details:DoContainerIndexCleanup()
	end

	function Details:reset_window(instancia)
		if (instancia.segmento == -1) then
			instancia.showing[instancia.atributo].need_refresh = true
			instancia.v_barras = true
			instancia:ResetaGump()
			instancia:RefreshMainWindow(true)
		end
	end

	---start/restart the internal garbage collector runtime ~garbage
	---@param bShouldForceCollect boolean if true, the garbage collector will run regardless of the time interval
	---@param lastEvent unixtime no call is passing lastEvent at the moment
	function Details222.GarbageCollector.RestartInternalGarbageCollector(bShouldForceCollect, lastEvent)
		--print("d! debug: running garbage collector...")
		if (not bShouldForceCollect) then
			local thisTime = Details222.GarbageCollector.lastCollectTime + Details222.GarbageCollector.intervalTime
			if (thisTime > Details._tempo + 1)  then
				return

			elseif (Details.in_combat or InCombatLockdown() or Details:IsInInstance()) then
				Details.Schedules.After(5, Details222.GarbageCollector.RestartInternalGarbageCollector, false, lastEvent)
				return
			end
		else
			if (type(bShouldForceCollect) ~= "boolean") then
				if (bShouldForceCollect == 1) then
					if (Details.in_combat or InCombatLockdown()) then
						Details.Schedules.After(5, Details222.GarbageCollector.RestartInternalGarbageCollector, bShouldForceCollect, lastEvent)
						return
					end
				end
			end
		end

		if (Details.debug) then
			if (bShouldForceCollect) then
				Details:Msg("(debug) collecting garbage with forced state:", bShouldForceCollect)
			else
				Details:Msg("(debug) collecting garbage.")
			end
		end

		--cleanup all the parser caches
		Details:ClearParserCache()

		--cleanup lines which isn't shown but has an actor attached to
		for instanceId, instanceObject in Details:ListInstances() do
			if (instanceObject.barras and instanceObject.barras[1]) then
				for i, lineRow in ipairs(instanceObject.barras) do
					if (not lineRow:IsShown()) then
						lineRow.minha_tabela = nil
					end
				end
			end
		end

		--print("d! debug: RunGarbageCollector() Start")
		---@type number
		local amountActorRemoved = Details222.GarbageCollector.RunGarbageCollector(lastEvent)
		--print("d! debug: RunGarbageCollector() Ended, cleanup:", amountActorRemoved, "actors.") --139 actor removed, but don't remove anything (/reload it remove again)
		--UpdateAddOnMemoryUsage()
		--local memoryUsage = GetAddOnMemoryUsage("Details")
		--print("Memory:", floor(memoryUsage)/1000, "MBytes")

		--refresh nas janelas
		if (amountActorRemoved > 0) then
			Details:InstanceCallDetailsFunc(Details.reset_window)
		end

		--cleanup backlisted pets within the handler of actor containers
		Details222.Pets.PetContainerCleanup()
		Details:ClearCCPetsBlackList()

		--cleanup spec cache
		Details:ResetSpecCache()

		--cleanup the shield cache
		Details:Destroy(Details.ShieldCache)

		--set the time of the last run
		Details222.GarbageCollector.lastCollectTime = Details._tempo

		if (Details.debug) then
			Details:Msg("(debug) executing: collectgarbage().")
			collectgarbage()
		end
	end

	---check all the actors and remove the ones which are not in use
	---@param combatObject combat
	---@param overriteInterval unixtime
	---@return integer
	local collectGarbage = function(combatObject, overriteInterval)
		--amount of actors removed
		local amountCleaned = 0

		--do not collect things in a mythic+ dungeon segment
		if (combatObject.is_mythic_dungeon_trash or combatObject.is_mythic_dungeon_run_id or combatObject.is_mythic_dungeon_segment) then
			return amountCleaned
		end

		if (combatObject.__destroyed) then
			Details:Msg("a deleted combat object was found on g2.collector, please report this bug on discord:")
			Details:Msg("combat destroyed by:", combatObject.__destroyedBy)
			return 0
		end

		---@type number
		local _tempo = _time()

		---@type number
		for containerId = 1, 4 do
			---@type actorcontainer
			local actorContainer = combatObject:GetContainer(containerId)
			---@type table<number, actor>
			local actorList = actorContainer:GetActorTable()

			for actorIndex = #actorList, 1, -1 do
				---@type actor
				local actorObject = actorList[actorIndex]

				if (Details222.Actors.IsDisposable(actorObject) and not actorObject.owner) then
					local canCollect = false

					--check the time of the last seen event coming from the actor
					---@type unixtime
					local lastSeenEventTime = actorObject.last_event

					---@type number
					local nextGarbageCollection

					if (overriteInterval) then
						nextGarbageCollection = lastSeenEventTime + overriteInterval
					else
						nextGarbageCollection = lastSeenEventTime + Details222.GarbageCollector.intervalTime
					end

					if (nextGarbageCollection - 1 < _tempo) then
						canCollect = true
					end

					if (canCollect) then
						amountCleaned = amountCleaned + 1

						if (containerId == 1 or containerId == 2) then --damage or healing
							Details222.TimeMachine.RemoveActor(actorObject)
						end

						--remove the actor from the container
						Details:DestroyActor(actorObject, actorContainer, combatObject) --a window showing 'Auras & Void Zones' did not refreshed and had an actor pointing to here
					end
				end
			end

			actorContainer:Cleanup()

			if (amountCleaned > 0) then
				--destroy orphans
				local orphansCleaned = 0
				for actorIndex = #actorList, 1, -1 do
					---@type actor
					local actorObject = actorList[actorIndex]
					if (actorObject.owner and not actorObject.owner.serial) then
						Details:DestroyActor(actorObject, actorContainer, combatObject)
						orphansCleaned = orphansCleaned + 1
					end
				end

				actorContainer:Cleanup()

				--refresh the breakdown window
				if (Details.BreakdownWindowFrame:IsShown()) then
					Details222.BreakdownWindow.RefreshPlayerScroll()
				end
			end

			actorContainer.need_refresh = true
		end --end of containerId loop

		return amountCleaned
	end --end of collectGarbage function

	---run the garbage collector
	---@param overriteLastEvent unixtime
	function Details222.GarbageCollector.RunGarbageCollector(overriteLastEvent)
		---@type number
		local amountRemoved = 0

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

		--create a list of all combats except the current one
		---@type table<number, combat>
		local segmentsTable = Details:GetCombatSegments()

		--collect destroyed combat objects
		local bGotSegmentsRemoved = false
		for i = #segmentsTable, 1, -1 do
			local combatObject = segmentsTable[i]
			if (combatObject ~= currentCombat) then
				if (combatObject.__destroyed) then
					table.remove(segmentsTable, i)
					bGotSegmentsRemoved = true
				end
			end
		end

		if (bGotSegmentsRemoved) then
			Details:SendEvent("DETAILS_DATA_SEGMENTREMOVED")
		end

		---@type table
		local segmentsList = {}

		--add all segments except the current one
		for _, combatObject in ipairs(segmentsTable) do
			if (combatObject ~= currentCombat) then
				segmentsList[#segmentsList+1] = combatObject
			end
		end
		--add the current segment at the end of the list
		segmentsList[#segmentsList+1] = currentCombat

		--collect the garbage
		for i, combatObject in ipairs(segmentsList) do
			if (combatObject.__destroyed) then
				Details:Msg("a deleted combat object was found by the g.collector, please report this bug on discord:")
				Details:Msg("combat destroyed by:", combatObject.__destroyedBy)
			end

			local removedActors = collectGarbage(combatObject, overriteLastEvent)
			if (i == #segmentsList) then
				--print("current segment removed:", removedActors, "actors.")
			end
			amountRemoved = amountRemoved + removedActors
		end

		---@type combat
		local overallCombatObject = Details.tabela_overall
		amountRemoved = amountRemoved + collectGarbage(overallCombatObject, overriteLastEvent)

		if (amountRemoved > 0) then
			Details:InstanceCallDetailsFunc(Details.ScheduleUpdate)
			Details:RefreshMainWindow(-1)
		end

		return amountRemoved
	end

	---return true if the actor is disposable, in other words, if it can be removed from the combat without affecting the results
	---@param actor actor
	---@return boolean
	function Details222.Actors.IsDisposable(actor)
		if (not actor.grupo and not actor.boss and not actor.boss_fight_component and not actor.fight_component and not actor.pvp_component and not actor.arena_enemy and not actor.enemy) then
			return true
		else
			return false
		end
	end