--------------------------------------------------------------------------------
-- File: AoUMeter.lua
-- Desc: All classes used to store data
-- Original: DarkDPSMeter.lua from DarkDPSMeter2
-- Mod by: Resha
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- You can set here the mode you want to keep
--------------------------------------------------------------------------------
Global("ModeDPS", true)					-- enable DPS mode
Global("ModeHPS", true)					-- enable HPS mode
Global("ModeDEF", true)					-- enable DEF mode
Global("ModeDPSn", false)				-- not yet implemented
Global("KeepHistory", false)				-- not yet implemented
Global("DPSMeterMode", enumMode.Dps)	-- default mode: enumMode.Dps, enumMode.Dps, enumMode.Def

--------------------------------------------------------------------------------
Global("onEvent", {})
Global("onReaction", {})
--------------------------------------------------------------------------------
Global("ShowHideBtn", nil)
Global("MainPanel", nil)
Global("SpellsPanel", nil)
Global("FightPanel", nil)
Global("ModePanel", nil)
--------------------------------------------------------------------------------
Global("TotalPanel", nil)
Global("PlayerPanelList", {})
Global("SpellPanelList", {})
Global("SpellInfoPanelList", {})
Global("SpellBlockPanelList", {})
Global("ExtraInfoPanelList", {})
--------------------------------------------------------------------------------
Global("DPSMeter", {})
Global("SelectedCombatantName" , "")
Global("SelectedPlayerPanelID" , nil)

--==============================================================================
--==============================================================================
--================= PLAYER LIST ================================================
--==============================================================================
--==============================================================================

--------------------------------------------------------------------------------
-- Hide player panel in the player list
--------------------------------------------------------------------------------
function HideAllPlayerPanel()
	TotalPanel:Hide()
	for i = 1, MAXCOMBATANTS do
		PlayerPanelList[i]:Hide()
	end
end

--------------------------------------------------------------------------------
-- Update the panel "Total" in the player list
--------------------------------------------------------------------------------
function SetValuesToTotalPanel(Fight, Mode)	
	TotalPanel:Show()
	
	local Bar = TotalPanel:GetChildByName("PlayerInfoBar")
	local TotalNameTextView = TotalPanel:GetChildByName("TotalInfoTextViewName")
	local StatsTextView = TotalPanel:GetChildByName("PlayerInfoTextViewStats")
	local minute, second
	local FightTime = Fight.Timer:GetElapsedTime()
	if FightTime == 0 then
		minute, second = 0, 0
	else
		minute, second = GetMinSec(FightTime)
	end
	
	Bar:SetWidth(230)
	
	-- Total(0:00)
	local Total = "Total ("  .. minute .. ":" .. userMods.FromWString(common.FormatInt(second , "%02d")) .. ")"
	TotalNameTextView.Widget:SetVal("Name", userMods.ToWString(Total))
	StatsTextView.Widget:SetVal("DamageDone", common.FormatFloat(Fight.Data[Mode].Amount, "%f3K5"))
	StatsTextView.Widget:SetVal("DPS", common.FormatFloat(Fight.Data[Mode].AmountPerSec, "%f3K5"))
	
	if DPSMeter.bCollectData then
		Bar:SetColor(TotalColorInFight)
	else
		Bar:SetColor(TotalColor)
	end
end
--------------------------------------------------------------------------------
-- Update values for a combatant in the player list
--------------------------------------------------------------------------------
function SetValuesToPlayerPanel(index, Fight, Mode)
	local PlayerPanel = PlayerPanelList[index]
	local Combatant = Fight:GetCombatantByIndex(index)
	if not Combatant then return end
	
	PlayerPanel:Show()
	
	local Bar = PlayerPanel:GetChildByName("PlayerInfoBar")
	local NameTextView = PlayerPanel:GetChildByName("PlayerInfoTextViewName")
	local StatsTextView = PlayerPanel:GetChildByName("PlayerInfoTextViewStats")
	local PercentageTextView = PlayerPanel:GetChildByName("PlayerInfoTextViewPercentage")
	
	Bar:SetWidth(230 * (Combatant.Data[Mode].LeaderPercentage / 100))
	NameTextView.Widget:SetVal("Index", common.FormatInt(index , "%d"))
	NameTextView.Widget:SetVal("Name", Combatant.Name)
	StatsTextView.Widget:SetVal("DamageDone", common.FormatFloat(Combatant.Data[Mode].Amount, "%f3K5"))
	StatsTextView.Widget:SetVal("DPS", common.FormatFloat(Combatant.Data[Mode].AmountPerSec, "%f3K5"))
	PercentageTextView.Widget:SetVal("Percentage", common.FormatInt(Combatant.Data[Mode].Percentage, "%d"))
	
	Bar:SetColor(ClassColors[Combatant.Class])
end
--------------------------------------------------------------------------------
-- Update the whole player list
--------------------------------------------------------------------------------
function SetPlayerListValues(Mode, FightId)
	if not MainPanel.Widget:IsVisible() then return end
	local Fight = DPSMeter:GetFight(FightId)
	if not Fight then return end
	Fight:RecalculateCombatantsData(Mode) -- Important

	local CombatantCount = math.min(Fight:GetCombatantCount(), MAXCOMBATANTS)
	
	HideAllPlayerPanel()
	MainPanel:SetHeight(47 + (CombatantCount + 1) * 24)
	
	SetValuesToTotalPanel(Fight, Mode)
	for i = 1, CombatantCount do
		SetValuesToPlayerPanel(i, Fight, Mode)
	end
end

--==============================================================================
--==============================================================================
--================= SPELL PANEL - Spell list ===================================
--==============================================================================
--==============================================================================

--------------------------------------------------------------------------------
-- Update a spell line in the spell panel
--------------------------------------------------------------------------------
function SetSpellValuesToSpellPanel(CombatantData, index, Mode)
	local SpellData = CombatantData:GetSpellByIndex(index, Mode)
	local SpellPanel = SpellPanelList[index]

	if SpellData then
		SpellPanel:Show()
		
		local Bar = SpellPanel:GetChildByName("SpellBar")
		local TextViewName = SpellPanel:GetChildByName("SpellTextViewName")
		local TextViewDamage = SpellPanel:GetChildByName("SpellTextViewStats")
		local TextViewPercentage = SpellPanel:GetChildByName("SpellTextViewPercentage")
		
		Bar:SetColor(DamageTypeColors[SpellData.Type])
		Bar:SetWidth(356 * (SpellData.Percentage / 100))
		
		TextViewName.Widget:SetVal("Index", common.FormatInt(index , "%d"))
		TextViewName.Widget:SetVal("Name", SpellData.Name)
		TextViewDamage.Widget:SetVal("DamageDone", common.FormatFloat(SpellData.Amount , "%f3K5"))
		TextViewDamage.Widget:SetVal("DPS", common.FormatFloat(SpellData.AmountPerSec , "%f3K5"))
		TextViewDamage.Widget:SetVal("DamageBlock", common.FormatFloat(SpellData.ResistPercentage , "%d"))
		TextViewPercentage.Widget:SetVal("Percentage", common.FormatInt(SpellData.Percentage , "%d"))
	end
end
--------------------------------------------------------------------------------
-- Fill the spell panel
--------------------------------------------------------------------------------
function SetValueDetails(Mode, FightId)
	if not SpellsPanel.Widget:IsVisible() then return end
	
	local SelectedFight = DPSMeter:GetFight(FightId)
	if not SelectedFight then return end
	local SelectedCombatantData = SelectedFight:GetCombatantByName(SelectedCombatantName)
	
	SpellsPanel:HideAllChild()
	local CloseSpellsPanelBtn = SpellsPanel:GetChildByName("CloseBtn")
	local TextViewName = SpellsPanel:GetChildByName("SpellPlayerNameTextViewName")
	CloseSpellsPanelBtn:Show()
	
	if (SelectedCombatantData) then
		SelectedCombatantData:CalculateSpell(SelectedFight.Timer:GetElapsedTime(), Mode) -- Important.
		
		local Title = { [DPSMeter.Fight.Previous] = "PrevFight",
					[DPSMeter.Fight.Current] = "CurrentFight",
					[DPSMeter.Fight.Total] = "OverallData" }
	
		TextViewName.Widget:SetVal("Name", SelectedCombatantData.Name)
		TextViewName:Show()
		if SelectedCombatantData.Amount ~= 0 then
			SpellsPanel:GetChildByName("SpellHeaderPanel"):Show()
		end
		for i = 1, MAXSPELLS do
			SetSpellValuesToSpellPanel(SelectedCombatantData, i, Mode)
		end
	end
end

--------------------------------------------------------------------------------
-- Update values in player list & spell panel
--------------------------------------------------------------------------------
function UpdateValues()
	SetPlayerListValues(DPSMeter.ActiveMode, DPSMeter.Fight.Active)
	SetValueDetails(DPSMeter.ActiveMode, DPSMeter.Fight.Active)
end

--==============================================================================
--==============================================================================
--================= SPELL PANEL - details list + details resist list ===========
--==============================================================================
--==============================================================================

--------------------------------------------------------------------------------
-- Update a spell info line in the spell info panel
--------------------------------------------------------------------------------
function SetSpellInfoValuesToSpellInfoPanel(SpellData, index, dmgtype, Mode)
	local SpellInfoData = SpellData.DetailsList[dmgtype]
	local SpellInfoPanel = SpellInfoPanelList[index]
	
	if (SpellInfoData) and (SpellInfoData.Count > 0) then
		SpellInfoPanel:Show()
		
		local Bar = SpellInfoPanel:GetChildByName("SpellDetailBar")
		local TextViewName = SpellInfoPanel:GetChildByName("SpellDetailTextViewName")
		local TextViewCount = SpellInfoPanel:GetChildByName("SpellDetailTextViewCount")
		local TextViewDamage = SpellInfoPanel:GetChildByName("SpellDetailTextViewStats")
		local TextViewPercentage = SpellInfoPanel:GetChildByName("SpellDetailTextViewPercentage")
		
		Bar:SetColor(HitTypeColors[SpellInfoData.ID])
		Bar:SetWidth(356 * (SpellInfoData.Percentage / 100))
		
		local Title = { "Normal", "Critical", "Glancing", "Dodge", "Miss" }
		
		TextViewName.Widget:SetVal("Name", GetTextLocalized(Title[SpellInfoData.ID]))
		TextViewCount.Widget:SetVal("Count", common.FormatInt(SpellInfoData.Count , "%d"))
		TextViewDamage.Widget:SetVal("Min", common.FormatFloat(SpellInfoData.Min , "%f3K5"))
		--SpellInfoData.Avg = math.ceil(SpellInfoData.Amount / SpellInfoData.Count)
		TextViewDamage.Widget:SetVal("Avg", common.FormatFloat(SpellInfoData.Avg , "%f3K5"))
		TextViewDamage.Widget:SetVal("Max", common.FormatFloat(SpellInfoData.Max , "%f3K5"))
		TextViewPercentage.Widget:SetVal("Percentage", common.FormatInt(SpellInfoData.Percentage , "%d"))
	end
end

--------------------------------------------------------------------------------
-- Update a spell info resist line in the spell info panel
--------------------------------------------------------------------------------
function SetSpellInfoResistValuesToSpellInfoPanel(SpellData, index, blockdmgtype, Mode)
	local SpellInfoData = SpellData.ResistDetailsList[blockdmgtype]
	local SpellInfoPanel = SpellBlockPanelList[index]
	
	if (SpellInfoData) and (SpellInfoData.Count > 0) then
		SpellInfoPanel:Show()
		
		local Bar = SpellInfoPanel:GetChildByName("SpellDetailBar")
		local TextViewName = SpellInfoPanel:GetChildByName("SpellDetailTextViewName")
		local TextViewCount = SpellInfoPanel:GetChildByName("SpellDetailTextViewCount")
		local TextViewDamage = SpellInfoPanel:GetChildByName("SpellDetailTextViewStats")
		local TextViewPercentage = SpellInfoPanel:GetChildByName("SpellDetailTextViewPercentage")
		
		Bar:SetColor(HitTypeColors[SpellInfoData.ID])
		Bar:SetWidth(356 * (SpellInfoData.Percentage / 100))
		
		local Title
		if Mode == enumMode.Dps or Mode == enumMode.Def or Mode == enumMode.Dpsn then
			Title = { "Block", "Parry", "Barrier", "Resist", "Absorb", "RunesAbsorb", "MultAbsorb" }
		elseif Mode == enumMode.Hps then
			Title = { "HealResisted", "HealRuneAbsorbed", "HealAbsorbed", "HealOverload" }
		end
		
		TextViewName.Widget:SetVal("Name", GetTextLocalized(Title[SpellInfoData.ID]))
		TextViewCount.Widget:SetVal("Count", common.FormatInt(SpellInfoData.Count , "%d"))
		TextViewDamage.Widget:SetVal("Min", common.FormatFloat(SpellInfoData.Min , "%f3K5"))
		--SpellInfoData.Avg = math.ceil(SpellInfoData.Amount / SpellInfoData.Count)
		TextViewDamage.Widget:SetVal("Avg", common.FormatFloat(SpellInfoData.Avg , "%f3K5"))
		TextViewDamage.Widget:SetVal("Max", common.FormatFloat(SpellInfoData.Max , "%f3K5"))
		TextViewPercentage.Widget:SetVal("Percentage", common.FormatInt(SpellInfoData.Percentage , "%d"))
	end
end

--------------------------------------------------------------------------------
-- Update a spell extra info line in the spell info panel
--------------------------------------------------------------------------------
function SetSpellExtraInfoValuesToSpellInfoPanel(SpellData, index, extrainfotype, Mode)
	local SpellInfoData = SpellData.ExtraInfoList[extrainfotype]
	local SpellInfoPanel = ExtraInfoPanelList[index]
	
	if (SpellInfoData) and (SpellInfoData.Count > 0) then
		SpellInfoPanel:Show()
		local Bar = SpellInfoPanel:GetChildByName("SpellDetailBar")
		local TextViewName = SpellInfoPanel:GetChildByName("SpellDetailTextViewName")
		local TextViewCount = SpellInfoPanel:GetChildByName("SpellDetailTextViewCount")
		local TextViewDamage = SpellInfoPanel:GetChildByName("SpellDetailTextViewStats")
		local TextViewPercentage = SpellInfoPanel:GetChildByName("SpellDetailTextViewPercentage")
		
		Bar:SetColor(HitTypeColors[SpellInfoData.ID])
		Bar:SetWidth(356 * (SpellInfoData.Percentage / 100))
		
		local Title = { "Lethality" }
		
		TextViewName.Widget:SetVal("Name", GetTextLocalized(Title[SpellInfoData.ID]))
		TextViewCount.Widget:SetVal("Count", common.FormatInt(SpellInfoData.Count , "%d"))
		TextViewDamage.Widget:SetVal("Min", common.FormatFloat(SpellInfoData.Min , "%f3K5"))
		--SpellInfoData.Avg = math.ceil(SpellInfoData.Amount / SpellInfoData.Count)
		TextViewDamage.Widget:SetVal("Avg", common.FormatFloat(SpellInfoData.Avg , "%f3K5"))
		TextViewDamage.Widget:SetVal("Max", common.FormatFloat(SpellInfoData.Max , "%f3K5"))
		TextViewPercentage.Widget:SetVal("Percentage", common.FormatInt(SpellInfoData.Percentage , "%d"))
	end
end

--------------------------------------------------------------------------------
-- Hide all spell info line
--------------------------------------------------------------------------------
function HideAllSpellInfoPanel()
	for i = 1, DMGTYPES do
		SpellInfoPanelList[i]:Hide()
	end
	for i = 1, BLOCKDMGTYPES do
		SpellBlockPanelList[i]:Hide()
	end
	for i = 1, EXTRATYPES do
		ExtraInfoPanelList[i]:Hide()
	end
end

--==============================================================================
--==============================================================================
--=========================== EVENTS ===========================================
--==============================================================================
--==============================================================================

function ScanRune(CombatantId)
	if DPSMeter.Collecting[enumMode.Dpsn] then
		local SumOffLevel = 0
		for ite = 0, 2 do
			local RuneId = unit.GetEquipmentItemId (CombatantId, DRESS_SLOT_OFFENSIVERUNE1 + ite, ITEM_CONT_EQUIPMENT) or nil
			local Rune = RuneId and avatar.GetItemInfo(RuneId) or nil
			if Rune and Rune.runeInfo then
				SumOffLevel = SumOffLevel + Rune.runeInfo.runeLevel
			end
		end
		DPSMeter:UpdateCombatantRune(CombatantId, SumOffLevel)
	end
end

--------------------------------------------------------------------------------
-- Event: EVENT_GROUP_CHANGED
--
-- params fields: no
--------------------------------------------------------------------------------
onEvent["EVENT_INSPECT_STARTED"] = function()
	if avatar.IsExist() and DPSMeter.Collecting[enumMode.Dpsn] then
		local InspectInfo = avatar.GetInspectInfo()
		local InspectedId = InspectInfo and InspectInfo.playerId or nil

		if InspectedId then
			ScanRune(InspectedId) -- Scan the rune of inspectd player
			-- If the inspected player was in DPSMeter.ScannedUnitId then
			-- then close the inspection
			for i, ID in DPSMeter.ScannedUnitId do
				if ID == InspectedId then
					table.remove(DPSMeter.ScannedUnitId, i)
					avatar.EndInspect()
					break
				end
			end
			
			if DPSMeter.ScannedUnitId[1] then
				avatar.StartInspect(DPSMeter.ScannedUnitId[1])
			end
		end
	end
end

--------------------------------------------------------------------------------
-- Event: EVENT_GROUP_CHANGED
--
-- params fields: 
--  spawned: table of ObjectId - indexed with 0 symbol table appearing units
--  despawned: table of ObjectId - indexed with 0 symbol table missing units
--------------------------------------------------------------------------------
onEvent["EVENT_GROUP_CHANGED"] = function(params)
	if group.GetMembers() or raid.IsExist() then
		
		local PartyMembersInfoList = DPSMeter:GetPartyMembers()
		
		for i, member in PartyMembersInfoList do
			if not DPSMeter:GetFight(DPSMeter.Fight.Current):GetCombatantByName(member.name) then
				DPSMeter:AddNewCombatantByName(member.name)
			end

			DPSMeter:GetFight(DPSMeter.Fight.Current):GetCombatantByName(member.name):UpdateCombatantDataByID(member.id)
			DPSMeter:GetFight(DPSMeter.Fight.Total):GetCombatantByName(member.name):UpdateCombatantDataByID(member.id)

			if member.state == GROUP_MEMBER_STATE_NEAR or
				member.state == GROUP_MEMBER_STATE_MERC or 
				member.state == RAID_MEMBER_STATE_NEAR then
				
				DPSMeter:SetCombatantStatusByName(member.name, true)
			else
				DPSMeter:SetCombatantStatusByName(member.name, false)
			end
			
			if DPSMeter.Collecting[enumMode.Dpsn] then
				local Combatant = DPSMeter:GetFight(DPSMeter.Fight.Current):GetCombatantByID(member.id)
				if Combatant and Combatant.ID and unit.IsPlayer(Combatant.ID) and not (Combatant.ID == avatar.GetId())
					and Combatant.NormalizedCoef == 1 then
					table.insert(DPSMeter.ScannedUnitId, member.id)
				end
			end
		end
		
		if raid.IsExist() then
			for i, Combatant in DPSMeter:GetFight(DPSMeter.Fight.Current).CombatantsList do
				local bInvalid = true
				if IsThisStringValue (Combatant.Name, object.GetName(avatar.GetId())) then
					bInvalid = false
				else
					for j, member in PartyMembersInfoList do
						if IsThisStringValue (Combatant.Name, member.name) then
							bInvalid = false
						end
					end
				end
				if bInvalid then
					DPSMeter:RemoveCombatantByName(Combatant.Name)
				end
			end
		end
		
		if DPSMeter.ScannedUnitId[1] then
			avatar.StartInspect(DPSMeter.ScannedUnitId[1])
		end
	end
	
	UpdateValues()
end
--------------------------------------------------------------------------------
-- Event: EVENT_RAID_CHANGED = EVENT_GROUP_CHANGED
--------------------------------------------------------------------------------
onEvent["EVENT_RAID_CHANGED"] = onEvent["EVENT_GROUP_CHANGED"]
--------------------------------------------------------------------------------
-- Event: EVENT_RAID_MEMBER_STATUS_CHANGED
--
-- params fields:
--	name: WString - the name of the avatar, whose state has changed
--------------------------------------------------------------------------------
onEvent["EVENT_RAID_MEMBER_STATUS_CHANGED"] = function(params)
	if group.GetLeaderIndex() ~= -1 then
		local PartyMembersInfoList = DPSMeter:GetPartyMembers()
		for i, member in PartyMembersInfoList do
			if member.id then
				local Combatant = DPSMeter:GetFight(DPSMeter.Fight.Current):GetCombatantByName(member.name)
				if Combatant then Combatant:UpdateCombatantDataByID(member.id) end
				
				Combatant = DPSMeter:GetFight(DPSMeter.Fight.Total):GetCombatantByName(member.name)
				if Combatant then Combatant:UpdateCombatantDataByID(member.id) end
			end

			if member.state == GROUP_MEMBER_STATE_NEAR or member.state == RAID_MEMBER_STATE_NEAR then
				DPSMeter:SetCombatantStatusByName(member.name, true)
			else
				DPSMeter:SetCombatantStatusByName(member.name, false)
			end
		end	
	end
end
--------------------------------------------------------------------------------
-- Event: EVENT_GROUP_MEMBER_STATUS_CHANGED = EVENT_RAID_MEMBER_STATUS_CHANGED
--------------------------------------------------------------------------------
onEvent["EVENT_GROUP_MEMBER_STATUS_CHANGED"] = onEvent["EVENT_RAID_MEMBER_STATUS_CHANGED"]
--------------------------------------------------------------------------------
-- Event: EVENT_GROUP_DISAPPEARED
--
-- params fields: no
--------------------------------------------------------------------------------
onEvent["EVENT_GROUP_DISAPPEARED"] = function()
	DPSMeter:ResetAllFights()
	
	DPSMeter:AddNewCombatantByID(avatar.GetId())
	SelectedCombatantName = object.GetName(avatar.GetId())
	UpdateValues()
end
--------------------------------------------------------------------------------
-- Event: EVENT_RAID_DISAPPEARED = EVENT_GROUP_DISAPPEARED
--------------------------------------------------------------------------------
onEvent["EVENT_RAID_DISAPPEARED"] =  onEvent["EVENT_GROUP_DISAPPEARED"]
--------------------------------------------------------------------------------
-- Event: EVENT_GROUP_MEMBER_REMOVED
--
-- params fields:
--	name: WString - name of the avatar who quit the group
--------------------------------------------------------------------------------
onEvent["EVENT_GROUP_MEMBER_REMOVED"] = function(params)
	DPSMeter:RemoveCombatantByName(params.name)
	UpdateValues()
end
--------------------------------------------------------------------------------
-- Event: EVENT_SECOND_TIMER
--
-- params fields:
--	elapsedMs: number (integer) - number of milliseconds that have passed since the last event
--------------------------------------------------------------------------------
onEvent["EVENT_SECOND_TIMER"] = function(params)
	if not avatar.IsExist() then return end
	
	if (DPSMeter.bCollectData) then
		if not DPSMeter:IsCombatantsInCombat() and not DPSMeter:IsEnemiesInCombat() then
			DPSMeter:Stop()
		end
		
		DPSMeter:UpdateFightsTime()
		UpdateValues()
	elseif (DPSMeter.OffBattleTime) then
		
		if (DPSMeter.OffBattleTime >= CLEANUP_DELAY) then
			DPSMeter:CleanupOpponents()
		else
			DPSMeter.OffBattleTime = DPSMeter.OffBattleTime + 1
		end
	end
end
--------------------------------------------------------------------------------
-- Event: EVENT_UNIT_DAMAGE_RECEIVED
--
-- params fields:
--	source: ObjectId (or nil) - ID of the person who causes damage
--	sourceName: WString - the name of the person who causes damage
--	target: ObjectId (not nil) - ID of the person who suffers damage
--	amount: number - the amount of damage to the
--	ability: WString - the name of the spell or ability
--	sysSubElement: string (enum "ENUM_SubElement_ ...") - the type of damage
--
--	lethal: boolean / nil - this was the final blow. Unit died.
--
--	sysActionSourceType: string - source of the damage (see box impactProducerUIInfos in \ data \ Client \ GMUIHelper \ GMUIHelper.xdb and fields for uiInfos ImpactProducer - specific spells, abilities, etc.)
--
-- Depending on what was damaged, one of the following:
--	spellId: SpellId / nil - Id spell if hit spell.
--	abilityId: AbilityId / nil - Id ability, if they beat abilities.
--	buffId: BuffId / nil - Id buffa if buffa beaten.
--	mapModifierId: MapModifierId / nil - damage modifier cards
--	isFall: boolean - damage from falling
--	isExploit: boolean - damage exploit
--
-- The following fields are filled if the trip was the fact that the appropriate modifier:
--
--	isDodge: boolean / nil - dodged the goal of a stroke
--	isMiss: boolean / nil - whether the fault
--	isCritical: boolean / nil - whether caused Critical Damage (increased damage)
--	isGlancing: boolean / nil - whether caused glancing damage (damage reduction)
--
--	shieldBlock: number (int) - the value of a locked damage if a Shield Block, otherwise 0
--	parry: number (int) - the value of parry damage if a Parry, otherwise 0
--	barrier: number (int) - the value of damage, absorption barrier, if the latter worked, otherwise 0
--	resist: number (int) - the value zarezistennogo damage if a resist, otherwise 0
--	(3.0.05 API) absorb: number (int) - the value of damage absorbed when absorption was otherwise 0
--
--  (4.0.01 API) multipliersAbsorb: number (int) - the amount by which reduce damage due to factors of damage on the target (can be negative if the damage increased)
--  (4.0.01 API) runesAbsorb: number (int) - a non-negative amount of damage absorbed by the protective runes
--  (4.0.01 API) absorb: number (int) - a non-negative amount of damage absorbed buffs
onEvent["EVENT_UNIT_DAMAGE_RECEIVED"] = function(params)
	if not params then return end

	if userMods.FromWString(params.ability) == "Природное равновесие" then return; end
	if userMods.FromWString(params.ability) == "Последний смех" then return; end
	
	DPSMeter:CollectDamageData(params)
	
	if DPSMeter:GetFight(DPSMeter.Fight.Current):GetCombatantCount() <= HEAVYMODE_MAXCOMBATANTS then
		UpdateValues()
	end
end
--------------------------------------------------------------------------------
-- Event: EVENT_HEALING_RECEIVED
--
-- params fields:
--	unitId: ObjectId (not nil) - ID cured unit
--	healerId: ObjectId (or nil) - ID of the doctor, if the information on it is still available
--	heal: number (integer) - how many units unit was cured
--	healthLevel: number (integer) - the level of treatment
--	isCritical: boolean - whether the treatment of critical
--	isGlancing: boolean - whether treatment attenuated
--	resisted: number (int) - the value of treatment, which is absorbed due to the complexity of wounds
--	runeResisted: number (int) - the value of treatment that is absorbed by Runic complexity of wounds
--	absorbed: number (int) - the value of treatment, which is absorbed by other things (for example, debuffami).
--  (4.0.01 API) overload: number (int) - the value of overtreatment, which has not been objective, as found itself in the health limit
--  (4.0.01 API) lethality: number (float) - the value of the wounds at the time of treatment goals
--
-- Depending on what was Pokhil unit (one of three):
--	spellId: SpellId / nil - Id spell treated if treated spell.
--	abilityId: AbilityId / nil - Id abilities, which were treated, if treated abilities.
--	buffId: BuffId / nil - Id buffa treated if treated Buff.
--	isFall: boolean - true, if health is changed due to the fall
--
--	sysActionSourceType: string - the source of treatment
onEvent["EVENT_HEALING_RECEIVED"] = function(params)
	if not params then return end
	
	DPSMeter:CollectHealData(params)
end 
--------------------------------------------------------------------------------
-- Event: EVENT_HEALING_RECEIVED
--
-- params fields:
--	id: ObjectId (not nil)
--------------------------------------------------------------------------------
onEvent["EVENT_AVATAR_CREATED"] = function(params)
	-- Since AO 2.0.03, localization must be applied here
	localization = GetGameLocalization()
	if not common.GetAddonRelatedTextGroup(localization) then
		localization = "eng"
	end
	onEvent["SCRIPT_ADDON_INFO_REQUEST"]({ target = common.GetAddonName() })
	
	strPet = userMods.FromWString(GetTextLocalized("Pet"))
	SpellsPanel.Widget:GetChildChecked("SpellHeaderTextViewName",  true):SetVal("Name", GetTextLocalized("Ability"))
	SpellsPanel.Widget:GetChildChecked("SpellHeaderTextViewStats", true):SetVal("DamageDone", GetTextLocalized("Damage"))
	SpellsPanel.Widget:GetChildChecked("SpellHeaderTextViewStats", true):SetVal("Absorbed", GetTextLocalized("Abs"))
	SpellsPanel.Widget:GetChildChecked("SpellDetailHeaderTextViewName",  true):SetVal("Name", GetTextLocalized("Type"))
	SpellsPanel.Widget:GetChildChecked("SpellDetailHeaderTextViewStats", true):SetVal("Min",  GetTextLocalized("Min"))
	SpellsPanel.Widget:GetChildChecked("SpellDetailHeaderTextViewStats", true):SetVal("Avg",  GetTextLocalized("Avg"))
	SpellsPanel.Widget:GetChildChecked("SpellDetailHeaderTextViewStats", true):SetVal("Max",  GetTextLocalized("Max"))
	
	local Title = { [enumMode.Dps] = "DPS",
					[enumMode.Hps] = "HPS",
					[enumMode.Def] = "DEF",
					[enumMode.Dpsn] = "DPSn",
					}
	
	-- Update the mode in the fight panel (at the top of the player list)
	ModePanel:GetChildByName("ModeNameTextView").Widget:SetVal("Name", GetTextLocalized(Title[DPSMeter.ActiveMode]))
	
	-- Update the mode in the title of the spell panel
	SpellsPanel:GetChildByName("SpellPlayerNameTextViewName").Widget:SetVal("Mode", GetTextLocalized(Title[DPSMeter.ActiveMode]))
	
	-- Update the mode in the header of the spell panel
	SpellsPanel.Widget:GetChildChecked("SpellHeaderTextViewStats", true):SetVal("DPS", GetTextLocalized(Title[DPSMeter.ActiveMode]))
	
	local Title = { [DPSMeter.Fight.Previous] = "PrevFight",
					[DPSMeter.Fight.Current] = "CurrentFight",
					[DPSMeter.Fight.Total] = "OverallData" }
	
	-- Update the fight in the fight panel (at the top of the player list)
	FightPanel:GetChildByName("FightNameTextView").Widget:SetVal("Name", GetTextLocalized(Title[DPSMeter.Fight.Active]))
	
	-- Update the fight in the title of the spell panel
	SpellsPanel:GetChildByName("SpellPlayerNameTextViewName").Widget:SetVal("Fight", GetTextLocalized(Title[DPSMeter.Fight.Active]))
	
	DPSMeter:AddNewCombatantByID(avatar.GetId())
	SelectedCombatantName = object.GetName(avatar.GetId())
	ScanRune(avatar.GetId())
	
	UpdateValues()
end

--==============================================================================
--==============================================================================
--=========================== EVENTS - Addon manager ===========================
--==============================================================================
--==============================================================================

--------------------------------------------------------------------------------
onEvent["SCRIPT_ADDON_INFO_REQUEST"] = function(params)
	if params.target == common.GetAddonName() then
		userMods.SendEvent("SCRIPT_ADDON_INFO_RESPONSE", {
			sender = params.target,
			desc = userMods.FromWString(GetTextLocalized("Description")),
			showDNDButton = true,
			showHideButton = true,
			showSettingsButton = true,
		})
	end
end
--------------------------------------------------------------------------------
onEvent["SCRIPT_ADDON_MEM_REQUEST"] = function(params)
	if params.target == common.GetAddonName() then
		userMods.SendEvent("SCRIPT_ADDON_MEM_RESPONSE", { sender = params.target, memUsage = gcinfo() })
	end
end
--------------------------------------------------------------------------------
onEvent["SCRIPT_SHOW_SETTINGS"] = function(params)
	if not MainPanel.Widget:IsVisible() then
		onReaction["ShowHideBtnReaction"]()
	end
end
--------------------------------------------------------------------------------
onEvent["SCRIPT_TOGGLE_VISIBILITY"] = function(params)
	if params.target == common.GetAddonName() then
		if params.state then
			ShowHideBtn:Show()
		else
			ShowHideBtn:Hide()
		end
	end
end
--------------------------------------------------------------------------------
onEvent["SCRIPT_TOGGLE_DND"] = function(params)
	if params.target == common.GetAddonName() then
		ShowHideBtn:DragNDrop(params.state)
	end
end
--------------------------------------------------------------------------------
onEvent["SCRIPT_TOGGLE_UI"] = function(params)
	mainForm:Show(params.visible)
end

--==============================================================================
--==============================================================================
--=========================== REACTIONS ========================================
--==============================================================================
--==============================================================================

--------------------------------------------------------------------------------
-- Get the index of a spell in the spell panel
--------------------------------------------------------------------------------
function GetSpellPanelIndex(reaction)
	--string.len("SpellPanel") + 1 = 11
	return tonumber(string.sub(reaction.sender, 11)) or -1
end
--------------------------------------------------------------------------------
-- Get the index of a spell in the spell panel
--------------------------------------------------------------------------------
function GetPlayerPanelIndex(reaction)
	--string.len("PlayerPanel") + 1 = 12
	return tonumber(string.sub(reaction.sender, 12)) or -1
end
--------------------------------------------------------------------------------
-- occurred when the player point a spell in the spell panel
--------------------------------------------------------------------------------
onReaction["SpellPanelOnPointing"] = function (reaction)
	
	HideAllSpellInfoPanel()
	local HeaderPanel = SpellsPanel:GetChildByName("SpellDetailHeaderPanel")
	
	if reaction.active then
		local SpellPanelIndex = GetSpellPanelIndex(reaction)
		local SelectedCombatantData = DPSMeter:GetFight(DPSMeter.Fight.Active):GetCombatantByName(SelectedCombatantName)
		
		HeaderPanel:Show()
		
		if (SelectedCombatantData) then
			local i
			local SpellData = SelectedCombatantData:GetSpellByIndex(SpellPanelIndex, DPSMeter.ActiveMode)
			
			if SpellData then
				i = 1
				for dmgtype in SpellData:SortSpellDetailsByType() do
					SetSpellInfoValuesToSpellInfoPanel(SpellData, i, dmgtype, DPSMeter.ActiveMode)
					i = i + 1
				end
				
				i = 1
				for blockdmgtype in SpellData:SortSpellResistDetailsByResist() do
					SetSpellInfoResistValuesToSpellInfoPanel(SpellData, i, blockdmgtype, DPSMeter.ActiveMode)
					i = i + 1
				end
				
				i = 1
				for extrainfotype in SpellData:SortSpellExtraInfo() do
					SetSpellExtraInfoValuesToSpellInfoPanel(SpellData, i, extrainfotype, DPSMeter.ActiveMode)
					i = i + 1
				end
			end
		end
	else
		HeaderPanel:Hide()
	end
end
--------------------------------------------------------------------------------
-- occurred when the player press the reset button
--------------------------------------------------------------------------------
onReaction["ResetBtnReaction"] = function(reaction)
	onEvent["EVENT_GROUP_DISAPPEARED"]()
	onEvent["EVENT_GROUP_CHANGED"]()
end
--------------------------------------------------------------------------------
-- occurred when the player press the close button in the main panel
--------------------------------------------------------------------------------
onReaction["CloseMainPanelBtnReaction"] = function(reaction)
	MainPanel:Hide()
	SpellsPanel:Hide()
end
--------------------------------------------------------------------------------
-- occurred when the player point a spell in the spell panel
--------------------------------------------------------------------------------
onReaction["PlayerPanelOnPointing"] = function(reaction)
	SelectedPlayerPanelID = reaction.active and GetPlayerPanelIndex(reaction) or -1
end
--------------------------------------------------------------------------------
-- occurred when the player click a player in the player list
--------------------------------------------------------------------------------
onReaction["PlayerInfoButtonDown"] = function(reaction)
	SelectedCombatantName = DPSMeter:GetFight(DPSMeter.Fight.Active):GetCombatantNameByIndex(SelectedPlayerPanelID)
	SpellsPanel:Show()
	UpdateValues()
end
--------------------------------------------------------------------------------
-- occurred when the player press the close button of the spell panel
--------------------------------------------------------------------------------
onReaction["CloseSpellInfoPanelBtnReaction"] = function(reaction)
	SpellsPanel:Hide()
end
--------------------------------------------------------------------------------
-- occurred when the player click on the fight dropdown button in the main panel
--------------------------------------------------------------------------------
onReaction["GetFightBtnReaction"] = function(reaction)
	local rotateMode = { [DPSMeter.Fight.Total] = DPSMeter.Fight.Current,
						 [DPSMeter.Fight.Current] = DPSMeter.Fight.Previous,
						 [DPSMeter.Fight.Previous] = DPSMeter.Fight.Total }
	
	DPSMeter.Fight.Active = rotateMode[DPSMeter.Fight.Active]
	local Title = { [DPSMeter.Fight.Previous] = "PrevFight",
					[DPSMeter.Fight.Current] = "CurrentFight",
					[DPSMeter.Fight.Total] = "OverallData" }
	
	-- Update the fight in the fight panel (at the top of the player list)
	FightPanel:GetChildByName("FightNameTextView").Widget:SetVal("Name", GetTextLocalized(Title[DPSMeter.Fight.Active]))
	
	-- Update the fight in the title of the spell panel
	SpellsPanel:GetChildByName("SpellPlayerNameTextViewName").Widget:SetVal("Fight", GetTextLocalized(Title[DPSMeter.Fight.Active]))
	
	UpdateValues()
end
--------------------------------------------------------------------------------
-- occurred when the player click on the mode dropdown button in the main panel
--------------------------------------------------------------------------------
onReaction["GetModeBtnReaction"] = function(reaction)
	local rotateMode = { [enumMode.Dps] = DPSMeter.Collecting[enumMode.Hps] and enumMode.Hps or DPSMeter.Collecting[enumMode.Def] and enumMode.Def or DPSMeter.Collecting[enumMode.Dpsn] and enumMode.Dpsn or enumMode.Dps,
						 [enumMode.Hps] = DPSMeter.Collecting[enumMode.Def] and enumMode.Def or DPSMeter.Collecting[enumMode.Dpsn] and enumMode.Dpsn or DPSMeter.Collecting[enumMode.Dps] and enumMode.Dps or enumMode.Hps,
						 [enumMode.Def] = DPSMeter.Collecting[enumMode.Dpsn] and enumMode.Dpsn or DPSMeter.Collecting[enumMode.Dps] and enumMode.Dps or DPSMeter.Collecting[enumMode.Hps] and enumMode.Hps or enumMode.Def,
						 [enumMode.Dpsn] = DPSMeter.Collecting[enumMode.Dps] and enumMode.Dps or DPSMeter.Collecting[enumMode.Hps] and enumMode.Hps or DPSMeter.Collecting[enumMode.Def] and enumMode.Def or enumMode.Dpsn }
	DPSMeter.ActiveMode = rotateMode[DPSMeter.ActiveMode]
	
	local Title = { [enumMode.Dps] = "DPS",
					[enumMode.Hps] = "HPS",
					[enumMode.Def] = "DEF",
					[enumMode.Dpsn] = "DPSn"}
	
	-- Update the mode in the fight panel (at the top of the player list)
	ModePanel:GetChildByName("ModeNameTextView").Widget:SetVal("Name", GetTextLocalized(Title[DPSMeter.ActiveMode]))
	
	-- Update the mode in the title of the spell panel
	SpellsPanel:GetChildByName("SpellPlayerNameTextViewName").Widget:SetVal("Mode", GetTextLocalized(Title[DPSMeter.ActiveMode]))
	
	-- Update the mode in the header of the spell panel
	SpellsPanel.Widget:GetChildChecked("SpellHeaderTextViewStats", true):SetVal("DPS", GetTextLocalized(Title[DPSMeter.ActiveMode]))
	
	UpdateValues()
end
--------------------------------------------------------------------------------
-- occurred when the player click on the "D" button in order to show or hide the main panel
--------------------------------------------------------------------------------
onReaction["ShowHideBtnReaction"] = function(reaction)
	if DnD:IsDragging() then return end
	if MainPanel.Widget:IsVisible() then
		MainPanel:Hide()
		SpellsPanel:Hide()
	else
		MainPanel:Show()
		if avatar.IsExist() then
			UpdateValues()
		end
	end
end

--==============================================================================
--==============================================================================
--=========================== INIT =============================================
--==============================================================================
--==============================================================================

function Init()
	RegisterEventHandlers(onEvent)
	RegisterReactionHandlers(onReaction)
	
	ShowHideBtn = TWidget:CreateNewObject("ShowHideBtn")
	ShowHideBtn:DragNDrop(true, 687, nil, true)
	
	MainPanel = TWidget:CreateNewObject("MainPanel")
	MainPanel:DragNDrop(true, 686, nil, true, false)
	
	SpellsPanel = TWidget:CreateNewObject("SpellInfoPanel")
	SpellsPanel:DragNDrop(true, 688, nil, true)
	
	FightPanel = MainPanel:GetChildByName("FightPanel")
	ModePanel = MainPanel:GetChildByName("ModePanel")
	
	local TotalPanelDesc = MainPanel:GetChildByName("TotalInfoPanel"):GetDesc()
	MainPanel:GetChildByName("TotalInfoPanel"):Destroy()
	TotalPanel = TWidget:CreateNewObjectByDesc("TotalPanel", TotalPanelDesc, MainPanel)
	TotalPanel:SetPosition(20, 47)

	local PlayerPanelDesc = MainPanel:GetChildByName("PlayerInfoPanel"):GetDesc()
	MainPanel:GetChildByName("PlayerInfoPanel"):Destroy()

	for i = 1, MAXCOMBATANTS do
		local wtName = "PlayerPanel" .. i
		PlayerPanelList[i] = TWidget:CreateNewObjectByDesc(wtName, PlayerPanelDesc, MainPanel)
		PlayerPanelList[i]:SetPosition(20, 47 + i * 24)
	end
	
	local SpellPanelDesc = SpellsPanel:GetChildByName("SpellPanel"):GetDesc()
	SpellsPanel:GetChildByName("SpellPanel"):Destroy()
	local SpellListOffset = 54
	for i = 1, MAXSPELLS do
		local wtName = "SpellPanel" .. i
		SpellPanelList[i] = TWidget:CreateNewObjectByDesc(wtName, SpellPanelDesc, SpellsPanel)
		SpellPanelList[i]:SetPosition(22, SpellListOffset + (i-1) * 18)
	end
	
	local SpellInfoPanelDesc = SpellsPanel:GetChildByName("SpellDetailPanel"):GetDesc()
	SpellsPanel:GetChildByName("SpellDetailPanel"):Destroy()
	local DamageOffset = SpellListOffset + MAXSPELLS * 18 + 35
	for i = 1, DMGTYPES do
		local wtName = "SpellInfoPanel" .. i
		SpellInfoPanelList[i] = TWidget:CreateNewObjectByDesc(wtName, SpellInfoPanelDesc, SpellsPanel)
		SpellInfoPanelList[i]:SetPosition(22, DamageOffset + (i-1) * 18)
	end
	
	local BlockDamageOffset = DamageOffset + DMGTYPES * 18 + 5
	for i = 1, BLOCKDMGTYPES do
		local wtName = "SpellBlockPanel" .. i
		SpellBlockPanelList[i] = TWidget:CreateNewObjectByDesc(wtName, SpellInfoPanelDesc, SpellsPanel)
		SpellBlockPanelList[i]:SetPosition(22, BlockDamageOffset + (i-1) * 18)
	end
	
	local ExtraInfoOffset = BlockDamageOffset + BLOCKDMGTYPES * 18 + 5
	for i = 1, EXTRATYPES do
		local wtName = "ExtraInfoPanel" .. i
		ExtraInfoPanelList[i] = TWidget:CreateNewObjectByDesc(wtName, SpellInfoPanelDesc, SpellsPanel)
		ExtraInfoPanelList[i]:SetPosition(22, ExtraInfoOffset + (i-1) * 18)
	end
	
	DPSMeter = TUMeter:CreateNewObject()
	DPSMeter:ResetAllFights()
	
	-- Enable here the modes you want
	DPSMeter.Collecting[enumMode.Dps] = ModeDPS
	DPSMeter.Collecting[enumMode.Hps] = ModeHPS
	DPSMeter.Collecting[enumMode.Def] = ModeDEF
	DPSMeter.Collecting[enumMode.Dpsn] = false -- ModeDPSn -- not completely implemented
	DPSMeter.KeepHistory = KeepHistory
	DPSMeter.ActiveMode = DPSMeterMode
	
	DPSMeter.Fight.Active = DPSMeter.Fight.Current -- User-selected view.
	
	-- Backward compatibility with pre-1.1.04 versions:
	if not social.GetFriendInfo then
		mainForm:SetPriority(1000)
	end
	
	if avatar.IsExist() then
		onEvent["EVENT_AVATAR_CREATED"]()
		onEvent["EVENT_GROUP_CHANGED"]()
	end
end

Init()
