
local me = { name = "pet"}
local mod = thismod
mod[me.name] = me

--[[
Pet.lua

Adds Hunter and Warlock pets to the raid threat list.

Credit to Ghost for doing most of the work for me, also BlackArrow0203 and Namsar.
]]

--[[
----------------------------------------------------------------------------
					Special Methods from Framework\Core.lua
----------------------------------------------------------------------------
]]

-- Register polled methods. e.g. <me.redospellranks> will be called every 1 second.
me.myonupdates = 
{
	updatespellranks = 1.0,
	updateincombat = 1.0,
	updatethreat = 0.5,
}

-- Parser for pet abilities. See Framework\Regex.lua for more details.
me.myparsers =
{
	-- Warlock Pet Taunt Spell
	{"spell", "SPELLCASTGOOTHERTARGETTED", "CHAT_MSG_SPELL_PET_DAMAGE"}, -- "%s casts %s on %s."
	-- Hunter Pet Taunt Spell
	{"spell", "SIMPLECASTOTHEROTHER", "CHAT_MSG_SPELL_PET_DAMAGE"}, -- "%s casts %s on %s."

	{"whitedamage", "COMBATHITCRITOTHEROTHER", "CHAT_MSG_COMBAT_PET_HITS"}, -- "%s crits %s for %d."
	{"whitedamage", "COMBATHITOTHEROTHER", "CHAT_MSG_COMBAT_PET_HITS"}, -- "%s hits %s for %d."

	-- Warlock Pet Damage Spell
	{"magicdamage", "SPELLLOGCRITSCHOOLOTHEROTHER", "CHAT_MSG_SPELL_PET_DAMAGE"},	-- "%s's %s crits %s for %d %s damage."
	{"magicdamage", "SPELLLOGSCHOOLOTHEROTHER", "CHAT_MSG_SPELL_PET_DAMAGE"}, -- "%s's %s hits %s for %d %s damage."
	
	-- Hunter Pet Damage Spell
	{"physicaldamage", "SPELLLOGCRITOTHEROTHER", "CHAT_MSG_SPELL_PET_DAMAGE"}, -- "%s's %s crits %s for %d."
	{"physicaldamage", "SPELLLOGOTHEROTHER", "CHAT_MSG_SPELL_PET_DAMAGE"}, -- "%s's %s hits %s for %d."
}

me.onparse = function(identifier, ...)
		
	-- no special modifiers on autoattacks
	if identifier == "whitedamage" then
		me.reportattack(select(3, ...), select(3, ...))
		
	-- all known specials that cause damage have normal threat too
	elseif (identifier == "physicaldamage") or (identifier == "magicdamage") then
		me.reportattack(select(4, ...), select(4, ...))
	
	-- spells include tauntlike abilities like growl.
	elseif identifier == "spell" then
		
		-- this unlocalises the abilitiy, e.g. "Heroic Strike" --> "heroicstrike"
		local abilityid = mod.string.unlocalise("spell", select(2, ...))
		
		-- all known abilities are described in <me.data>
		if me.data[abilityid] then
			me.reportspell(abilityid)
		end
	end
	
end

--[[
----------------------------------------------------------------------------
					Determining the Spell Ranks Available
----------------------------------------------------------------------------
]]

me.spellranks = { }

--[[
me.updatespellranks()
	Finds the rank of your pet's special spells, putting the values in <me.spellranks>.
	This method is called periodically by Core.lua. Most pets only have about 5 abilities, so we can afford to call it fairly often without taking a processor hit.
	The method is essentially the same as <mod.my.updatespellranks>.
]]
me.updatespellranks = function()
	
	local index = 0
	local name, rankstring, rank, spellid
	local rankpattern = mod.string.get("misc", "spellrank") -- e.g. "Rank %d" in english.
		
	while true do
		index = index + 1
		name, rankstring = GetSpellName(index, "pet")
		
		if name == nil then
			break
		end
		
		-- get the internal spell ID
		spellid = mod.string.unlocalise("spell", name)

		if spellid and me.data[spellid] then
			rank = 0
			
			_, _, rank = string.find(rankstring, rankpattern)
			
			if rank then
				me.spellranks[spellid] = tonumber(rank)
			end
		end
	end
	
	-- also add intimidation manually (it's a player ability, not a pet ability)
	me.spellranks.intimidation = 1
	
end

--[[
----------------------------------------------------------------------------
						Pet Spell Database
----------------------------------------------------------------------------
]]

-- The data is arranged by ranks. The first value is for rank1, the second for rank 2, etc.
me.data = 
{
	torment = { 45, 75, 125, 215, 300, 395, 632, },
	suffering = { 150, 300, 450, 600, 645, 885, },
	growl = { 50, 65, 110, 170, 240, 320, 415, 664, },
	intimidation = { 580, },
	cower = { -30, -55, -85, -125, -175, -225, -360, },
	soothingkiss = { -45, -75, -127, -165, -275, },
	anguish = { 300, 395, 632 },
}

--[[
----------------------------------------------------------------------------
						Handling New Damage and Threat
----------------------------------------------------------------------------
]]

--[[
me.reportspell(abilityid)
Handles a pet spell event. <abilitiyid> will be one of the keys in <me.data>. We find the correct rank of the spell, then apply any talents that may affect it. 
]]
me.reportspell = function(abilityid)
	
	if abilityid == "growl" then
		me.reportattack(me.getgrowlthreat(), 0)
		return
	end
	
	local data = me.data[abilityid]
	local rank = me.spellranks[abilityid]
	
	local value = data[rank]
	
	-- imp voidwalker for suffering
	if abilityid == suffering then
		value = value * mod.my.mods.warlock.voidwalker
	
	elseif abilityid == soothingkiss then
		value = value * mod.my.mods.warlock.succubus
	end 
	
	me.reportattack(value, 0)

end

--[[
scale factor = 5.7 threat per AP
minimum AP = ax + b,
x = level
a = 28.14
b = 1235.6

credit to Kathucka and Dhirken at Curse Gaming for finding these numbers.
]]
me.getgrowlthreat = function()

	-- 1) assume max rank, get flat value
	local rank = me.spellranks.growl
	local level = UnitLevel("pet")
	local petap1, petap2 = UnitAttackPower("pet")
	local petap = petap1 + petap2
	local basevalue, mixfactor
	
	-- if you're level 1, just use growl 1.
	if level == 1 then
		basevalue = me.data.growl[1]
	
	-- if you are using a rank that is far below your level, use the next rank instead (capped at the maximum known rank)
	elseif level >= rank * 10 then
		basevalue = me.data.growl[math.min(rank + 1, #me.data.growl)]
	
	-- if you are using max rank growl, we can't improve the estimate
	elseif rank == #me.data.growl then
		basevalue = me.data.growl[#me.data.growl]
		
	-- otherwise, you are a few levels over the original level for this rank
	else
			
		-- e.g. mixfactor is 0.9 at level 51, 0.8 at level 52, ..., 0.1 at level 59.
		mixfactor = 1 - (math.fmod(level, 10) / 10)
		
		basevalue = mixfactor * me.data.growl[rank] + (1 - mixfactor) * me.data.growl[rank + 1]
	end
		
	-- now find minimum AP value (only for >= 60 pets)
	if level > 59 then
		
		local minap = 28.14 * level - 1235.6
		
		if petap > minap then
			basevalue = basevalue + 5.7 * (petap - minap)
		end
	end
	
	--mod.print(string.format("rank = %s, level = %s, petap = %s, basevalue = %s, mixfactor = %s.",rank, level, petap, basevalue, mixfactor)) 
	
	return basevalue
	
end

-- This is our pet's threat
me.petthreat = 0

--[[
me.reportattack(threat, damage)
Handles a pet damage event. This could be called by <me.reportspell>, or directly from the OnEvent handler from a simple event like an autoattack.
<threat>	number; the threat caused
<damage>	integer; the damage done.
]]
me.reportattack = function(threat, damage)
	
	local petname = UnitName("pet")
	
	-- Sometimes we get pet events from totems and shit that aren't really pets. So filter them out:
	if petname == nil then
		return
	end
	
	-- add
	me.petthreat = me.petthreat + threat
		
	-- set personal
	if mod.table.mydata[petname] == nil then
		mod.table.mydata[petname] = mod.table.newdatastruct()
	end
	
	mod.table.mydata[petname].threat = me.petthreat
	mod.table.mydata[petname].hits = mod.table.mydata[petname].hits + 1
	mod.table.mydata[petname].damage = mod.table.mydata[petname].damage + damage
	
end

--[[
----------------------------------------------------------------------------
					Monitoring the In Combat Status of the Pet
----------------------------------------------------------------------------

Basically when the pet goes out of combat we want him to rezero his threat.
]]

me.petincombat = false

--[[
me.updateincombat()
Remove the pet's threat when it goes out of combat.
This method is called periodically from Core.lua
]]
me.updateincombat = function()
	
	if (UnitExists("pet") == nil) or (UnitName("pet") == nil) then
		me.petincombat = false
		me.petthreat = 0
		return
	end
	
	local combatnow = UnitAffectingCombat("pet") or UnitAffectingCombat("player")
	
	if combatnow == nil then
		me.petthreat = 0	
	end
	
	me.petincombat = (combatnow ~= nil)
	
end

-- called every 0.5 seconds
me.updatethreat = function()
	
	if UnitName("pet") == nil then
		return
	end
	
	mod.netin.messagein(UnitName("pet"), "t " .. me.petthreat, 1)
	
end