-- combat class object

	local _detalhes = 		_G._detalhes
	local Loc = LibStub ("AceLocale-3.0"):GetLocale ( "Details" )
	local _
	
--[[global]] DETAILS_TOTALS_ONLYGROUP = true

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> local pointers

	local _setmetatable = setmetatable -- lua local
	local _ipairs = ipairs -- lua local
	local _pairs = pairs -- lua local
	local _bit_band = bit.band -- lua local
	local _date = date -- lua local
	local _table_remove = table.remove -- lua local
	local _rawget = rawget
	local _math_max = math.max
	local _math_floor = math.floor
	local _GetTime = GetTime

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> constants

	local combate 	=	_detalhes.combate
	local container_combatentes = _detalhes.container_combatentes
	local class_type_dano 	= _detalhes.atributos.dano
	local class_type_cura		= _detalhes.atributos.cura
	local class_type_e_energy 	= _detalhes.atributos.e_energy
	local class_type_misc 	= _detalhes.atributos.misc
	
	local REACTION_HOSTILE =	0x00000040
	local CONTROL_PLAYER =		0x00000100

	--local _tempo = time()
	local _tempo = _GetTime()
	
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> api functions

	--combat (container type, actor name)
	_detalhes.call_combate = function (self, class_type, name)
		local container = self[class_type]
		local index_mapa = container._NameIndexTable [name]
		local actor = container._ActorTable [index_mapa]
		return actor
	end
	combate.__call = _detalhes.call_combate

	--get the start date and end date
	function combate:GetDate()
		return self.data_inicio, self.data_fim
	end
	
	--set the combat date
	function combate:SetDate (started, ended)
		if (started and type (started) == "string") then
			self.data_inicio = started
		end
		if (ended and type (ended) == "string") then
			self.data_fim = ended
		end		
	end
	
	--return data for charts
	function combate:GetTimeData (name)
		return self.TimeData [name]
	end
	
	function combate:GetContainer (attribute)
		return self [attribute]
	end
	
	function combate:GetRoster()
		return self.raid_roster
	end
	
	function combate:InstanceType()
		return _rawget (self, "instance_type")
	end
	
	function combate:IsTrash()
		return _rawget (self, "is_trash")
	end
	
	function combate:GetDifficulty()
		return self.is_boss and self.is_boss.diff
	end
	
	function combate:GetBossInfo()
		return self.is_boss
	end
	
	function combate:GetPhases()
		return self.PhaseData
	end
	
	function combate:GetPvPInfo()
		return self.is_pvp
	end
	
	function combate:GetMythicDungeonInfo()
		return self.is_mythic_dungeon
	end

	function combate:GetMythicDungeonTrashInfo()
		return self.is_mythic_dungeon_trash
	end
	
	function combate:IsMythicDungeon()
		local is_segment = self.is_mythic_dungeon_segment
		local run_id = self.is_mythic_dungeon_run_id
		return is_segment, run_id
	end
	
	function combate:IsMythicDungeonOverall()
		return self.is_mythic_dungeon and self.is_mythic_dungeon.OverallSegment
	end
	
	function combate:GetArenaInfo()
		return self.is_arena
	end
	
	function combate:GetDeaths()
		return self.last_events_tables
	end
	
	function combate:GetCombatId()
		return self.combat_id
	end
	
	function combate:GetCombatNumber()
		return self.combat_counter
	end
	
	function combate:GetAlteranatePower()
		return self.alternate_power
	end
	
	--return the name of the encounter or enemy
	function combate:GetCombatName (try_find)
		if (self.is_pvp) then
			return self.is_pvp.name
			
		elseif (self.is_boss) then
			return self.is_boss.encounter
			
		elseif (self.is_mythic_dungeon_trash) then
			return self.is_mythic_dungeon_trash.ZoneName .. " (" .. Loc ["STRING_SEGMENTS_LIST_TRASH"] .. ")"
			
		elseif (_rawget (self, "is_trash")) then
			return Loc ["STRING_SEGMENT_TRASH"]
			
		else
			if (self.enemy) then
				return self.enemy
			end
			if (try_find) then
				return _detalhes:FindEnemy()
			end
		end
		return Loc ["STRING_UNKNOW"]
	end
	
	--[[global]] DETAILS_SEGMENTID_OVERALL = -1
	--[[global]] DETAILS_SEGMENTID_CURRENT = 0
	
	--enum segments type
	--[[global]] DETAILS_SEGMENTTYPE_GENERIC = 0
	
	--[[global]] DETAILS_SEGMENTTYPE_OVERALL = 1
	
	--[[global]] DETAILS_SEGMENTTYPE_DUNGEON_TRASH = 5
	--[[global]] DETAILS_SEGMENTTYPE_DUNGEON_BOSS = 6
	
	--[[global]] DETAILS_SEGMENTTYPE_RAID_TRASH = 7
	--[[global]] DETAILS_SEGMENTTYPE_RAID_BOSS = 8
	
	--[[global]] DETAILS_SEGMENTTYPE_MYTHICDUNGEON_GENERIC = 10
	--[[global]] DETAILS_SEGMENTTYPE_MYTHICDUNGEON_TRASH = 11
	--[[global]] DETAILS_SEGMENTTYPE_MYTHICDUNGEON_OVERALL = 12
	--[[global]] DETAILS_SEGMENTTYPE_MYTHICDUNGEON_TRASHOVERALL = 13
	--[[global]] DETAILS_SEGMENTTYPE_MYTHICDUNGEON_BOSS = 14
	
	--[[global]] DETAILS_SEGMENTTYPE_PVP_ARENA = 20
	--[[global]] DETAILS_SEGMENTTYPE_PVP_BATTLEGROUND = 21

	function combate:GetCombatType()
		--> mythic dungeon
		local isMythicDungeon = is_mythic_dungeon_segment
		if (isMythicDungeon) then
			local isMythicDungeonTrash = self.is_mythic_dungeon_trash
			if (isMythicDungeonTrash) then
				return DETAILS_SEGMENTTYPE_MYTHICDUNGEON_TRASH
			else
				local isMythicDungeonOverall = self.is_mythic_dungeon and self.is_mythic_dungeon.OverallSegment
				local isMythicDungeonTrashOverall = self.is_mythic_dungeon and self.is_mythic_dungeon.TrashOverallSegment
				if (isMythicDungeonOverall) then
					return DETAILS_SEGMENTTYPE_MYTHICDUNGEON_OVERALL
				elseif (isMythicDungeonTrashOverall) then
					return DETAILS_SEGMENTTYPE_MYTHICDUNGEON_TRASHOVERALL
				end
				
				local bossEncounter =  self.is_boss
				if (bossEncounter) then
					return DETAILS_SEGMENTTYPE_MYTHICDUNGEON_BOSS
				end
				
				return DETAILS_SEGMENTTYPE_MYTHICDUNGEON_GENERIC
			end
		end
		
		--> arena
		local arenaInfo = self.is_arena
		if (arenaInfo) then
			return DETAILS_SEGMENTTYPE_PVP_ARENA
		end
		
		--> battleground
		local battlegroundInfo = self.is_pvp
		if (battlegroundInfo) then
			return DETAILS_SEGMENTTYPE_PVP_BATTLEGROUND
		end
		
		--> dungeon or raid
		local instanceType = self.instance_type
		
		if (instanceType == "party") then
			local bossEncounter =  self.is_boss
			if (bossEncounter) then
				return DETAILS_SEGMENTTYPE_DUNGEON_BOSS
			else
				return DETAILS_SEGMENTTYPE_DUNGEON_TRASH
			end
			
		elseif (instanceType == "raid") then
			local bossEncounter =  self.is_boss
			if (bossEncounter) then
				return DETAILS_SEGMENTTYPE_RAID_BOSS
			else
				return DETAILS_SEGMENTTYPE_RAID_TRASH
			end
		end
		
		--> overall data
		if (self == _detalhes.tabela_overall) then
			return DETAILS_SEGMENTTYPE_OVERALL
		end
		
		return DETAILS_SEGMENTTYPE_GENERIC
	end

	--return a numeric table with all actors on the specific containter
	function combate:GetActorList (container)
		return self [container]._ActorTable
	end

	function combate:GetActor (container, name)
		local index = self [container] and self [container]._NameIndexTable [name]
		if (index) then
			return self [container]._ActorTable [index]
		end
		return nil
	end
	
	--return the combat time in seconds
	function combate:GetFormatedCombatTime()
		local time = self:GetCombatTime()
		local m, s = _math_floor (time/60), _math_floor (time%60)
		return m, s
	end
	
	function combate:GetCombatTime()
		if (self.end_time) then
			return _math_max (self.end_time - self.start_time, 0.1)
		elseif (self.start_time and _detalhes.in_combat and self ~= _detalhes.tabela_overall) then
			return _math_max (_GetTime() - self.start_time, 0.1)
		else
			return 0.1
		end
	end
	
	function combate:GetStartTime()
		return self.start_time
	end
	function combate:SetStartTime (t)
		self.start_time = t
	end
	
	function combate:GetEndTime()
		return self.end_time
	end
	function combate:SetEndTime (t)
		self.end_time = t
	end

	--return the total of a specific attribute
	local power_table = {0, 1, 3, 6, 0, "alternatepower"}
	
	function combate:GetTotal (attribute, subAttribute, onlyGroup)
		if (attribute == 1 or attribute == 2) then
			if (onlyGroup) then
				return self.totals_grupo [attribute]
			else
				return self.totals [attribute]
			end
			
		elseif (attribute == 3) then
			if (subAttribute == 5) then --> resources
				return self.totals.resources or 0
			end
			if (onlyGroup) then
				return self.totals_grupo [attribute] [power_table [subAttribute]]
			else
				return self.totals [attribute] [power_table [subAttribute]]
			end
			
		elseif (attribute == 4) then
			local subName = _detalhes:GetInternalSubAttributeName (attribute, subAttribute)
			if (onlyGroup) then
				return self.totals_grupo [attribute] [subName]
			else
				return self.totals [attribute] [subName]
			end
		end
		
		return 0
	end
	
	function combate:CreateAlternatePowerTable (actorName)
		local t = {last = 0, total = 0}
		self.alternate_power [actorName] = t
		return t
	end

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> internals

	function combate:CreateNewCombatTable()
		return combate:NovaTabela()
	end

	--class constructor
	function combate:NovaTabela (iniciada, _tabela_overall, combatId, ...)

		local esta_tabela = {true, true, true, true, true}
		
		esta_tabela [1] = container_combatentes:NovoContainer (_detalhes.container_type.CONTAINER_DAMAGE_CLASS, esta_tabela, combatId) --> Damage
		esta_tabela [2] = container_combatentes:NovoContainer (_detalhes.container_type.CONTAINER_HEAL_CLASS, esta_tabela, combatId) --> Healing
		esta_tabela [3] = container_combatentes:NovoContainer (_detalhes.container_type.CONTAINER_ENERGY_CLASS, esta_tabela, combatId) --> Energies
		esta_tabela [4] = container_combatentes:NovoContainer (_detalhes.container_type.CONTAINER_MISC_CLASS, esta_tabela, combatId) --> Misc
		esta_tabela [5] = container_combatentes:NovoContainer (_detalhes.container_type.CONTAINER_DAMAGE_CLASS, esta_tabela, combatId) --> place holder for customs
		
		_setmetatable (esta_tabela, combate)
		
		_detalhes.combat_counter = _detalhes.combat_counter + 1
		esta_tabela.combat_counter = _detalhes.combat_counter
		
		--> try discover if is a pvp combat
		local who_serial, who_name, who_flags, alvo_serial, alvo_name, alvo_flags = ...
		if (who_serial) then --> aqui ir� identificar o boss ou o oponente
			if (alvo_name and _bit_band (alvo_flags, REACTION_HOSTILE) ~= 0) then --> tentando pegar o inimigo pelo alvo
				esta_tabela.contra = alvo_name
				if (_bit_band (alvo_flags, CONTROL_PLAYER) ~= 0) then
					esta_tabela.pvp = true --> o alvo � da fac��o oposta ou foi dado mind control
				end
			elseif (who_name and _bit_band (who_flags, REACTION_HOSTILE) ~= 0) then --> tentando pegar o inimigo pelo who caso o mob � quem deu o primeiro hit
				esta_tabela.contra = who_name
				if (_bit_band (who_flags, CONTROL_PLAYER) ~= 0) then
					esta_tabela.pvp = true --> o who � da fac��o oposta ou foi dado mind control
				end
			else
				esta_tabela.pvp = true --> se ambos s�o friendly, seria isso um PVP entre jogadores da mesma fac��o?
			end
		end

		--> start/end time (duration)
		esta_tabela.data_fim = 0
		esta_tabela.data_inicio = 0
		esta_tabela.tempo_start = _tempo
		
		--> record deaths
		esta_tabela.last_events_tables = {}
		
		--> last events from players
		esta_tabela.player_last_events = {}
		
		--> players in the raid
		esta_tabela.raid_roster = {}
		
		--> frags
		esta_tabela.frags = {}
		esta_tabela.frags_need_refresh = false
		
		--> alternate power
		esta_tabela.alternate_power = {}
		
		--> time data container
		esta_tabela.TimeData = _detalhes:TimeDataCreateCombatTables()
		esta_tabela.PhaseData = {{1, 1}, damage = {}, heal = {}, damage_section = {}, heal_section = {}} --[1] phase number [2] phase started
		
		--> for external plugin usage, these tables are guaranteed to be saved with the combat
		esta_tabela.spells_cast_timeline = {}
		esta_tabela.aura_timeline = {}
		esta_tabela.cleu_timeline = {}
		
		--> cleu events
		esta_tabela.cleu_events = {
			n = 1 --event counter
		}
		
		--> Skill cache (not used)
		esta_tabela.CombatSkillCache = {}

		-- a tabela sem o tempo de inicio � a tabela descartavel do inicio do addon
		if (iniciada) then
			--esta_tabela.start_time = _tempo
			esta_tabela.start_time = _GetTime()
			esta_tabela.end_time = nil
		else
			esta_tabela.start_time = 0
			esta_tabela.end_time = nil
		end

		-- o container ir� armazenar as classes de dano -- cria um novo container de indexes de seriais de jogadores --par�metro 1 classe armazenada no container, par�metro 2 = flag da classe
		esta_tabela[1].need_refresh = true
		esta_tabela[2].need_refresh = true
		esta_tabela[3].need_refresh = true
		esta_tabela[4].need_refresh = true
		esta_tabela[5].need_refresh = true
		
		if (_tabela_overall) then --> link � a tabela de combate do overall
			esta_tabela[1].shadow = _tabela_overall[1]
			esta_tabela[2].shadow = _tabela_overall[2]
			esta_tabela[3].shadow = _tabela_overall[3]
			esta_tabela[4].shadow = _tabela_overall[4]
		end

		esta_tabela.totals = {
			0, --> dano
			0, --> cura
			{--> e_energy
				[0] = 0, --> mana
				[1] = 0, --> rage
				[3] = 0, --> energy (rogues cat)
				[6] = 0, --> runepower (dk)
				alternatepower = 0,
			},
			{--> misc
				cc_break = 0, --> armazena quantas quebras de CC
				ress = 0, --> armazena quantos pessoas ele reviveu
				interrupt = 0, --> armazena quantos interrupt a pessoa deu
				dispell = 0, --> armazena quantos dispell esta pessoa recebeu
				dead = 0, --> armazena quantas vezes essa pessia morreu
				cooldowns_defensive = 0, --> armazena quantos cooldowns a raid usou
				buff_uptime = 0, --> armazena quantos cooldowns a raid usou
				debuff_uptime = 0 --> armazena quantos cooldowns a raid usou
			},
			
			--> avoid using this values bellow, they aren't updated by the parser, only on demand by a user interaction.
				voidzone_damage = 0,
				frags_total = 0,
			--> end
		}
		
		esta_tabela.totals_grupo = {
			0, --> dano
			0, --> cura
			{--> e_energy
				[0] = 0, --> mana
				[1] = 0, --> rage
				[3] = 0, --> energy (rogues cat)
				[6] = 0, --> runepower (dk)
				alternatepower = 0,
			}, 
			{--> misc
				cc_break = 0, --> armazena quantas quebras de CC
				ress = 0, --> armazena quantos pessoas ele reviveu
				interrupt = 0, --> armazena quantos interrupt a pessoa deu
				dispell = 0, --> armazena quantos dispell esta pessoa recebeu
				dead = 0, --> armazena quantas vezes essa oessia morreu		
				cooldowns_defensive = 0, --> armazena quantos cooldowns a raid usou
				buff_uptime = 0,
				debuff_uptime = 0
			}
		}

		return esta_tabela
	end

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> core

	function combate:CreateLastEventsTable (player_name)
		local t = {}
		for i = 1, _detalhes.deadlog_events do
			t [i] = {}
		end
		t.n = 1
		self.player_last_events [player_name] = t
		return t
	end

	--trava o tempo dos jogadores ap�s o t�rmino do combate.
	function combate:TravarTempos()
		if (self [1]) then
			for _, jogador in _ipairs (self [1]._ActorTable) do --> damage
				if (jogador:Iniciar()) then -- retorna se ele esta com o dps ativo
					jogador:TerminarTempo()
					jogador:Iniciar (false) --trava o dps do jogador
				else
					if (jogador.start_time == 0) then
						jogador.start_time = _tempo
					end
					if (not jogador.end_time) then
						jogador.end_time = _tempo
					end
				end
			end
		end
		if (self [2]) then
			for _, jogador in _ipairs (self [2]._ActorTable) do --> healing
				if (jogador:Iniciar()) then -- retorna se ele esta com o dps ativo
					jogador:TerminarTempo()
					jogador:Iniciar (false) --trava o dps do jogador
				else
					if (jogador.start_time == 0) then
						jogador.start_time = _tempo
					end
					if (not jogador.end_time) then
						jogador.end_time = _tempo
					end
				end
			end
		end
	end

	function combate:seta_data (tipo)
		if (tipo == _detalhes._detalhes_props.DATA_TYPE_START) then
			self.data_inicio = _date ("%H:%M:%S")
		elseif (tipo == _detalhes._detalhes_props.DATA_TYPE_END) then
			self.data_fim = _date ("%H:%M:%S")
		end
	end

	function combate:seta_tempo_decorrido()
		--self.end_time = _tempo
		self.end_time = _GetTime()
	end

	function _detalhes.refresh:r_combate (tabela_combate, shadow)
		_setmetatable (tabela_combate, _detalhes.combate)
		tabela_combate.__index = _detalhes.combate
		tabela_combate.shadow = shadow
	end

	function _detalhes.clear:c_combate (tabela_combate)
		--tabela_combate.__index = {}
		tabela_combate.__index = nil
		tabela_combate.__call = {}
		tabela_combate._combat_table = nil
		tabela_combate.shadow = nil
	end

	combate.__sub = function (combate1, combate2)

		if (combate1 ~= _detalhes.tabela_overall) then
			return
		end

		--> sub dano
			for index, actor_T2 in _ipairs (combate2[1]._ActorTable) do
				local actor_T1 = combate1[1]:PegarCombatente (actor_T2.serial, actor_T2.nome, actor_T2.flag_original, true)
				actor_T1 = actor_T1 - actor_T2
				actor_T2:subtract_total (combate1)
			end
			combate1 [1].need_refresh = true
			
		--> sub heal
			for index, actor_T2 in _ipairs (combate2[2]._ActorTable) do
				local actor_T1 = combate1[2]:PegarCombatente (actor_T2.serial, actor_T2.nome, actor_T2.flag_original, true)
				actor_T1 = actor_T1 - actor_T2
				actor_T2:subtract_total (combate1)
			end
			combate1 [2].need_refresh = true
			
		--> sub energy
			for index, actor_T2 in _ipairs (combate2[3]._ActorTable) do
				local actor_T1 = combate1[3]:PegarCombatente (actor_T2.serial, actor_T2.nome, actor_T2.flag_original, true)
				actor_T1 = actor_T1 - actor_T2
				actor_T2:subtract_total (combate1)
			end
			combate1 [3].need_refresh = true
			
		--> sub misc
			for index, actor_T2 in _ipairs (combate2[4]._ActorTable) do
				local actor_T1 = combate1[4]:PegarCombatente (actor_T2.serial, actor_T2.nome, actor_T2.flag_original, true)
				actor_T1 = actor_T1 - actor_T2
				actor_T2:subtract_total (combate1)
			end
			combate1 [4].need_refresh = true

		--> reduz o tempo 
			combate1.start_time = combate1.start_time + combate2:GetCombatTime()
			
		--> apaga as mortes da luta diminuida
			local amt_mortes =  #combate2.last_events_tables --> quantas mortes teve nessa luta
			if (amt_mortes > 0) then
				for i = #combate1.last_events_tables, #combate1.last_events_tables-amt_mortes, -1 do 
					_table_remove (combate1.last_events_tables, #combate1.last_events_tables)
				end
			end
			
		--> frags
			for fragName, fragAmount in pairs (combate2.frags) do 
				if (fragAmount) then
					if (combate1.frags [fragName]) then
						combate1.frags [fragName] = combate1.frags [fragName] - fragAmount
					else
						combate1.frags [fragName] = fragAmount
					end
				end
			end
			combate1.frags_need_refresh = true
		
		--> alternate power
			local overallPowerTable = combate1.alternate_power
			for actorName, powerTable in pairs (combate2.alternate_power) do 
				local power = overallPowerTable [actorName]
				if (power) then
					power.total = power.total - powerTable.total
				end
				combate2.alternate_power [actorName].last = 0
			end
		
		return combate1
		
	end

	combate.__add = function (combate1, combate2)

		local all_containers = {combate2 [class_type_dano]._ActorTable, combate2 [class_type_cura]._ActorTable, combate2 [class_type_e_energy]._ActorTable, combate2 [class_type_misc]._ActorTable}
		local custom_combat
		if (combate1 ~= _detalhes.tabela_overall) then
			custom_combat = combate1
		end
		
		for class_type, actor_container in ipairs (all_containers) do
			for _, actor in ipairs (actor_container) do
				local shadow
				
				if (class_type == class_type_dano) then
					shadow = _detalhes.atributo_damage:r_connect_shadow (actor, true, custom_combat)
				elseif (class_type == class_type_cura) then
					shadow = _detalhes.atributo_heal:r_connect_shadow (actor, true, custom_combat)
				elseif (class_type == class_type_e_energy) then
					shadow = _detalhes.atributo_energy:r_connect_shadow (actor, true, custom_combat)
				elseif (class_type == class_type_misc) then
					shadow = _detalhes.atributo_misc:r_connect_shadow (actor, true, custom_combat)
				end
				
				shadow.boss_fight_component = actor.boss_fight_component or shadow.boss_fight_component
				shadow.fight_component = actor.fight_component or shadow.fight_component
				shadow.grupo = actor.grupo or shadow.grupo
			end
		end
		
		--> alternate power
			local overallPowerTable = combate1.alternate_power
			for actorName, powerTable in pairs (combate2.alternate_power) do 
				local power = overallPowerTable [actorName]
				if (not power) then
					power = combate1:CreateAlternatePowerTable (actorName)
				end
				power.total = power.total + powerTable.total
				combate2.alternate_power [actorName].last = 0
			end

		return combate1
	end

	function _detalhes:UpdateCombat()
		_tempo = _detalhes._tempo
	end
