<commit_before>--[[--------------------------------------------------------------------
    Ovale Spell Priority
    Copyright (C) 2012 Sidoine, <NAME>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License in the LICENSE
    file accompanying this program.
----------------------------------------------------------------------]]

OvaleCondition = {}

--<private-static-properties>

local LBCT = LibStub("LibBabble-CreatureType-3.0"):GetLookupTable()
local LRC = LibStub("LibRangeCheck-2.0", true)
local runes = {}
local runesCD = {}
		
local runeType = 
{
	blood = 1,
	unholy = 2,
	frost = 3,
	death = 4
}	

local totemType =
{
	ghoul = 1,
	fire = 1,
	earth = 2,
	water = 3,
	air = 4
}

local lastSaved = {}
local savedHealth = {}
local targetGUID = {}
local lastSPD = {}

--</private-static-properties>

--<private-static-methods>
local function isDebuffInList(list)
	local i=1;
	while (true) do
		local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId =  UnitDebuff("player", i);
		if (not name) then
			break
		end
		if (list[spellId]) then
			return true
		end
		i = i +1
	end
	return false
end

local function avecHate(temps, hate)
	if not temps then
		temps = 0
	end
	if (not hate) then
		return temps
	elseif (hate == "spell") then
		return temps/OvaleAura.spellHaste
	elseif (hate == "melee") then
		return temps/OvaleAura.meleeHaste
	else
		return temps
	end
end

local function compare(a, comparison, b)
	if not comparison then
		return 0, nil, a, 0, 0 -- this is not a compare, returns the value a
	elseif comparison == "more" then
		if (not b or (a~=nil and a>b)) then
			return 0
		else
			return nil
		end
	elseif comparison == "equal" then
		if b == a then
			return 0
		else
			return nil
		end
	elseif comparison == "less" then
		if (not a or (b~=nil and a<b)) then
			return 0
		else
			return nil
		end
	else
		Ovale:Error("unknown compare term "..comparison.." (should be more, equal, or less)")
	end
end

local function testbool(a, condition)
	if (condition == "yes" or not condition) then
		if (a) then
			return 0
		else
			return nil
		end
	else
		if (not a) then
			return 0
		else
			return nil
		end
	end
end

local function getTarget(condition)
	if (not condition) then
		return "player"
	elseif condition == "target" then
		return OvaleCondition.defaultTarget
	else	
		return condition
	end
end

local function addTime(time1, duration)
	if not time1 then
		return nil
	else
		return time1 + duration
	end
end

--Return time2-time1
local function diffTime(time1, time2)
	if not time1 then
		return 0
	end
	if not time2 then
		return nil
	end
	return time2 - time1
end

local function addOrSubTime(time1, operator, duration)
	if operator == "more" then
		return addTime(time1, -duration)
	else
		return addTime(time1, duration)
	end
end

local function nilstring(text)
	if text == nil then
		return "nil"
	else
		return text
	end
end

-- Get the expiration time of a debuff
-- that can be on any unit except the target
-- Returns the first to expires, the last to expires
-- Returns nil if the debuff is not present
local function getOtherAura(spellId, suppTime, excludingTarget)
	if excludingTarget then
		excludingTarget = UnitGUID(excludingTarget)
	end
	return OvaleState:GetExpirationTimeOnAnyTarget(spellId, excludingTarget)
end

local function GetRuneCount(type, death)
	local ret = 0
	local atTime = nil
	local rate = nil
	type = runeType[type]
	for i=1,6 do
		local rune = OvaleState.state.rune[i]
		if rune and (rune.type == type or (rune.type == 4 and death==1)) then
			if rune.cd > OvaleState.currentTime then
				onCd = true
				if not atTime or rune.cd < atTime then
					atTime = rune.cd
					rate = 1/rune.duration
				end
			else
				ret = ret + 1
			end
		end
	end
	if atTime then
		return ret + 1, atTime, rate
	else
		return ret, 0, 0
	end
end

local function GetRune(condition)
	local nombre = 0
	local nombreCD = 0
	local maxCD = nil
	
	for i=1,4 do
		runes[i] = 0
		runesCD[i] = 0
	end
	
	local k=1
	while true do
		local type = runeType[condition[k*2-1]]
		if not type then
			break
		end
		local howMany = condition[k*2]
		runes[type] = runes[type] + howMany
		k = k + 1 
	end
	
	for i=1,6 do
		local rune = OvaleState.state.rune[i]
		if rune then
			if runes[rune.type] > 0 then
				runes[rune.type] = runes[rune.type] - 1
				if rune.cd > runesCD[rune.type] then
					runesCD[rune.type] = rune.cd
				end
			elseif rune.cd < runesCD[rune.type] then
				runesCD[rune.type] = rune.cd
			end
		end
	end
	
	if not condition.nodeath then
		for i=1,6 do
			local rune = OvaleState.state.rune[i]
			if rune and rune.type == 4 then
				for j=1,3 do
					if runes[j]>0 then
						runes[j] = runes[j] - 1
						if rune.cd > runesCD[j] then
							runesCD[j] = rune.cd
						end
						break
					elseif rune.cd < runesCD[j] then
						runesCD[j] = rune.cd
						break
					end
				end
			end
		end
	end
	
	for i=1,4 do
		if runes[i]> 0 then
			return nil
		end
		if not maxCD or runesCD[i]>maxCD then
			maxCD = runesCD[i]
		end
	end
	return maxCD
end

local lastEnergyValue = nil
local lastEnergyTime

local function testValue(comparator, limit, value, atTime, rate)
	if not value or not atTime then
		return nil
	elseif not comparator then
		return 0, nil, value, atTime, rate
	else
		if rate == 0 then
			if comparator == "more" then
				if value > limit then return 0 else return nil end
			elseif comparator == "less" then
				if value < limit then return 0 else return nil end
			else
				Ovale:Error("Unknown operator "..comparator)
			end
		elseif comparator == "more" then
			return (limit-value)/rate + atTime
		elseif comparator == "less" then
			return 0, (limit-value)/rate + atTime
		else
			Ovale:Error("Unknown operator "..comparator)
		end
	end
end

-- Recherche un aura sur la cible et récupère sa durée et le nombre de stacks
-- return start, ending, stacks, spellHaste
local function GetTargetAura(condition, target)
	if (not target) then
		target=condition.target
		if (not target) then
			target="target"
		end
	end
	local stacks = condition.stacks
	if not stacks then
		stacks = 1
	end
	local spellId = condition[1]
	
	local mine = true
	if condition.any then
		mine = false
	end
	
	local aura
	if type(spellId) == "number" then
		aura = OvaleState:GetAura(target, spellId, mine)
	elseif OvaleData.buffSpellList[spellId] then
		for k,v in pairs(OvaleData.buffSpellList[spellId]) do
			local newAura = OvaleState:GetAura(target, v, mine)
			if newAura and (not aura or newAura.stacks>aura.stacks) then
				aura = newAura
			end
		end
	elseif spellId == "Magic" or spellId == "Disease" or spellId=="Curse" or spellId=="Poison" then
		aura = OvaleState:GetAura(target, spellId, mine)
	else
		Ovale:Error("unknown buff "..spellId)
		return 0,0,0,0
	end
	
	if not aura then
		Ovale:Log("Aura "..spellId.." not found")
		return 0,0,0,0
	end	
	
	if Ovale.trace then
		Ovale:Print("GetTargetAura = start=".. nilstring(aura.start) .. " end="..nilstring(aura.ending).." stacks=" ..nilstring(aura.stacks).."/"..stacks)
	end
		
	if (not condition.mine or (aura.mine and condition.mine==1) or (not aura.mine and condition.mine==0)) and aura.stacks>=stacks then
		local ending
		if condition.forceduration then
			--TODO: this is incorrect.
			if OvaleData.spellInfo[spellId] and OvaleData.spellInfo[spellId].duration then
				ending = aura.start + OvaleData.spellInfo[spellId].duration
			else
				ending = aura.start + condition.forceduration
			end
		else
			ending = aura.ending
		end
		return aura.start, ending, aura.stacks, aura.spellHaste
	else
		return 0,0,0,0
	end
end

local function getTargetDead(target)
	local second = math.floor(OvaleState.maintenant)
	if targetGUID[target] ~=UnitGUID(target) then
		lastSaved[target] = nil
		targetGUID[target] = UnitGUID(target)
		savedHealth[target] = {}
	end
	local newHealth = UnitHealth(target)
	if newHealth then
		Ovale:Log("newHealth = " .. newHealth)
	end
	if UnitHealthMax(target)==1 then
		Ovale:Log("Dummy, return in the future")
		return OvaleState.currentTime + 3600
	end
	if second~=lastSaved[target] and targetGUID[target] then
		lastSaved[target] = second
		local mod10 = second % 10
		local prevHealth = savedHealth[target][mod10]
		savedHealth[target][mod10] = newHealth
		if prevHealth and prevHealth>newHealth then
			lastSPD[target] = 10/(prevHealth-newHealth)
			if lastSPD[target] > 0 then
				Ovale:Log("dps = " .. (1/lastSPD[target]))
			end
		end
	end
	if not lastSPD[target] or lastSPD[target]<=0 then
		return nil
	end
	-- Rough estimation
	local duration = newHealth * lastSPD[target]
	if duration < 10000 then
		return OvaleState.maintenant + duration
	else
		return nil
	end
end

local function isSameSpell(spellIdA, spellIdB, spellNameB)
	if spellIdB then
		return spellIdA == spellIdB
	elseif spellIdA and spellNameB then
		return GetSpellInfo(spellIdA) == spellNameB
	else
		return false
	end
end
--</private-static-methods>

--<public-static-properties>
OvaleCondition.conditions=
{
	-- Test if a white hit just occured
	-- 1 : maximum time after a white hit
	-- Not useful anymore. No widely used spell reset swing timer anyway
	--[[AfterWhiteHit = function(condition)
		local debut = OvaleSwing.starttime
		local fin = OvaleSwing.duration + debut
		local maintenant = GetTime()
		if (maintenant-debut<condition[1]) then
			return 0
		elseif (maintenant<fin-0.1) then
			return fin-maintenant
		else 
			return 0.1
		end 
	end,]]
--- Get how many pieces of an armor set, e.g., Tier 14 set, are equipped by the player.
-- @name ArmorSetParts
-- @paramsig
-- @param setName The name of the armor set. Valid values are: T11, T12, T13.
-- @param operator (Optional) Comparison operator: equal, less, more.
-- @param number (Optional) The number to compare against.
-- @returns A number or a boolean value.
-- @usage ArmorSetParts(setName [operator number])
--
-- if ArmorSetParts(T13) >=2 and target.HealthPercent() <60 Spell(ferocious_bite)
-- if ArmorSetParts(T13 more 1) and TargetHealthPercent(less 60) Spell(ferocious_bite)
	armorsetparts = function(condition)
		local nombre = 0
		if OvaleEquipement.nombre[condition[1]] then
			nombre = OvaleEquipement.nombre[condition[1]]
		end
		return compare(nombre, condition[2], condition[3])
	end,
	-- Get the attack power
	-- returns : bool or number
	attackpower = function(condition)
		local base, posBuff, negBuff = UnitAttackPower("player")
		return compare(base + posBuff + negBuff, condition[1], condition[2])
	end,
	buffcount = function(condition)
		return OvaleState:GetExpirationTimeOnAnyTarget(condition[1]), 0, 0
	end,<commit_msg>Final pass at template for LuaDoc for conditions.<commit_after>--[[--------------------------------------------------------------------
    Ovale Spell Priority
    Copyright (C) 2012 Sidoine, <NAME>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License in the LICENSE
    file accompanying this program.
----------------------------------------------------------------------]]

OvaleCondition = {}

--<private-static-properties>

local LBCT = LibStub("LibBabble-CreatureType-3.0"):GetLookupTable()
local LRC = LibStub("LibRangeCheck-2.0", true)
local runes = {}
local runesCD = {}
		
local runeType = 
{
	blood = 1,
	unholy = 2,
	frost = 3,
	death = 4
}	

local totemType =
{
	ghoul = 1,
	fire = 1,
	earth = 2,
	water = 3,
	air = 4
}

local lastSaved = {}
local savedHealth = {}
local targetGUID = {}
local lastSPD = {}

--</private-static-properties>

--<private-static-methods>
local function isDebuffInList(list)
	local i=1;
	while (true) do
		local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId =  UnitDebuff("player", i);
		if (not name) then
			break
		end
		if (list[spellId]) then
			return true
		end
		i = i +1
	end
	return false
end

local function avecHate(temps, hate)
	if not temps then
		temps = 0
	end
	if (not hate) then
		return temps
	elseif (hate == "spell") then
		return temps/OvaleAura.spellHaste
	elseif (hate == "melee") then
		return temps/OvaleAura.meleeHaste
	else
		return temps
	end
end

local function compare(a, comparison, b)
	if not comparison then
		return 0, nil, a, 0, 0 -- this is not a compare, returns the value a
	elseif comparison == "more" then
		if (not b or (a~=nil and a>b)) then
			return 0
		else
			return nil
		end
	elseif comparison == "equal" then
		if b == a then
			return 0
		else
			return nil
		end
	elseif comparison == "less" then
		if (not a or (b~=nil and a<b)) then
			return 0
		else
			return nil
		end
	else
		Ovale:Error("unknown compare term "..comparison.." (should be more, equal, or less)")
	end
end

local function testbool(a, condition)
	if (condition == "yes" or not condition) then
		if (a) then
			return 0
		else
			return nil
		end
	else
		if (not a) then
			return 0
		else
			return nil
		end
	end
end

local function getTarget(condition)
	if (not condition) then
		return "player"
	elseif condition == "target" then
		return OvaleCondition.defaultTarget
	else	
		return condition
	end
end

local function addTime(time1, duration)
	if not time1 then
		return nil
	else
		return time1 + duration
	end
end

--Return time2-time1
local function diffTime(time1, time2)
	if not time1 then
		return 0
	end
	if not time2 then
		return nil
	end
	return time2 - time1
end

local function addOrSubTime(time1, operator, duration)
	if operator == "more" then
		return addTime(time1, -duration)
	else
		return addTime(time1, duration)
	end
end

local function nilstring(text)
	if text == nil then
		return "nil"
	else
		return text
	end
end

-- Get the expiration time of a debuff
-- that can be on any unit except the target
-- Returns the first to expires, the last to expires
-- Returns nil if the debuff is not present
local function getOtherAura(spellId, suppTime, excludingTarget)
	if excludingTarget then
		excludingTarget = UnitGUID(excludingTarget)
	end
	return OvaleState:GetExpirationTimeOnAnyTarget(spellId, excludingTarget)
end

local function GetRuneCount(type, death)
	local ret = 0
	local atTime = nil
	local rate = nil
	type = runeType[type]
	for i=1,6 do
		local rune = OvaleState.state.rune[i]
		if rune and (rune.type == type or (rune.type == 4 and death==1)) then
			if rune.cd > OvaleState.currentTime then
				onCd = true
				if not atTime or rune.cd < atTime then
					atTime = rune.cd
					rate = 1/rune.duration
				end
			else
				ret = ret + 1
			end
		end
	end
	if atTime then
		return ret + 1, atTime, rate
	else
		return ret, 0, 0
	end
end

local function GetRune(condition)
	local nombre = 0
	local nombreCD = 0
	local maxCD = nil
	
	for i=1,4 do
		runes[i] = 0
		runesCD[i] = 0
	end
	
	local k=1
	while true do
		local type = runeType[condition[k*2-1]]
		if not type then
			break
		end
		local howMany = condition[k*2]
		runes[type] = runes[type] + howMany
		k = k + 1 
	end
	
	for i=1,6 do
		local rune = OvaleState.state.rune[i]
		if rune then
			if runes[rune.type] > 0 then
				runes[rune.type] = runes[rune.type] - 1
				if rune.cd > runesCD[rune.type] then
					runesCD[rune.type] = rune.cd
				end
			elseif rune.cd < runesCD[rune.type] then
				runesCD[rune.type] = rune.cd
			end
		end
	end
	
	if not condition.nodeath then
		for i=1,6 do
			local rune = OvaleState.state.rune[i]
			if rune and rune.type == 4 then
				for j=1,3 do
					if runes[j]>0 then
						runes[j] = runes[j] - 1
						if rune.cd > runesCD[j] then
							runesCD[j] = rune.cd
						end
						break
					elseif rune.cd < runesCD[j] then
						runesCD[j] = rune.cd
						break
					end
				end
			end
		end
	end
	
	for i=1,4 do
		if runes[i]> 0 then
			return nil
		end
		if not maxCD or runesCD[i]>maxCD then
			maxCD = runesCD[i]
		end
	end
	return maxCD
end

local lastEnergyValue = nil
local lastEnergyTime

local function testValue(comparator, limit, value, atTime, rate)
	if not value or not atTime then
		return nil
	elseif not comparator then
		return 0, nil, value, atTime, rate
	else
		if rate == 0 then
			if comparator == "more" then
				if value > limit then return 0 else return nil end
			elseif comparator == "less" then
				if value < limit then return 0 else return nil end
			else
				Ovale:Error("Unknown operator "..comparator)
			end
		elseif comparator == "more" then
			return (limit-value)/rate + atTime
		elseif comparator == "less" then
			return 0, (limit-value)/rate + atTime
		else
			Ovale:Error("Unknown operator "..comparator)
		end
	end
end

-- Recherche un aura sur la cible et récupère sa durée et le nombre de stacks
-- return start, ending, stacks, spellHaste
local function GetTargetAura(condition, target)
	if (not target) then
		target=condition.target
		if (not target) then
			target="target"
		end
	end
	local stacks = condition.stacks
	if not stacks then
		stacks = 1
	end
	local spellId = condition[1]
	
	local mine = true
	if condition.any then
		mine = false
	end
	
	local aura
	if type(spellId) == "number" then
		aura = OvaleState:GetAura(target, spellId, mine)
	elseif OvaleData.buffSpellList[spellId] then
		for k,v in pairs(OvaleData.buffSpellList[spellId]) do
			local newAura = OvaleState:GetAura(target, v, mine)
			if newAura and (not aura or newAura.stacks>aura.stacks) then
				aura = newAura
			end
		end
	elseif spellId == "Magic" or spellId == "Disease" or spellId=="Curse" or spellId=="Poison" then
		aura = OvaleState:GetAura(target, spellId, mine)
	else
		Ovale:Error("unknown buff "..spellId)
		return 0,0,0,0
	end
	
	if not aura then
		Ovale:Log("Aura "..spellId.." not found")
		return 0,0,0,0
	end	
	
	if Ovale.trace then
		Ovale:Print("GetTargetAura = start=".. nilstring(aura.start) .. " end="..nilstring(aura.ending).." stacks=" ..nilstring(aura.stacks).."/"..stacks)
	end
		
	if (not condition.mine or (aura.mine and condition.mine==1) or (not aura.mine and condition.mine==0)) and aura.stacks>=stacks then
		local ending
		if condition.forceduration then
			--TODO: this is incorrect.
			if OvaleData.spellInfo[spellId] and OvaleData.spellInfo[spellId].duration then
				ending = aura.start + OvaleData.spellInfo[spellId].duration
			else
				ending = aura.start + condition.forceduration
			end
		else
			ending = aura.ending
		end
		return aura.start, ending, aura.stacks, aura.spellHaste
	else
		return 0,0,0,0
	end
end

local function getTargetDead(target)
	local second = math.floor(OvaleState.maintenant)
	if targetGUID[target] ~=UnitGUID(target) then
		lastSaved[target] = nil
		targetGUID[target] = UnitGUID(target)
		savedHealth[target] = {}
	end
	local newHealth = UnitHealth(target)
	if newHealth then
		Ovale:Log("newHealth = " .. newHealth)
	end
	if UnitHealthMax(target)==1 then
		Ovale:Log("Dummy, return in the future")
		return OvaleState.currentTime + 3600
	end
	if second~=lastSaved[target] and targetGUID[target] then
		lastSaved[target] = second
		local mod10 = second % 10
		local prevHealth = savedHealth[target][mod10]
		savedHealth[target][mod10] = newHealth
		if prevHealth and prevHealth>newHealth then
			lastSPD[target] = 10/(prevHealth-newHealth)
			if lastSPD[target] > 0 then
				Ovale:Log("dps = " .. (1/lastSPD[target]))
			end
		end
	end
	if not lastSPD[target] or lastSPD[target]<=0 then
		return nil
	end
	-- Rough estimation
	local duration = newHealth * lastSPD[target]
	if duration < 10000 then
		return OvaleState.maintenant + duration
	else
		return nil
	end
end

local function isSameSpell(spellIdA, spellIdB, spellNameB)
	if spellIdB then
		return spellIdA == spellIdB
	elseif spellIdA and spellNameB then
		return GetSpellInfo(spellIdA) == spellNameB
	else
		return false
	end
end
--</private-static-methods>

--<public-static-properties>
OvaleCondition.conditions=
{
	-- Test if a white hit just occured
	-- 1 : maximum time after a white hit
	-- Not useful anymore. No widely used spell reset swing timer anyway
	--[[AfterWhiteHit = function(condition)
		local debut = OvaleSwing.starttime
		local fin = OvaleSwing.duration + debut
		local maintenant = GetTime()
		if (maintenant-debut<condition[1]) then
			return 0
		elseif (maintenant<fin-0.1) then
			return fin-maintenant
		else 
			return 0.1
		end 
	end,]]
--- Get how many pieces of an armor set, e.g., Tier 14 set, are equipped by the player.
-- @name ArmorSetParts
-- @paramsig ArmorSetParts(setName [operator number])
-- @param setName The name of the armor set. Valid values are: T11, T12, T13.
-- @param operator (Optional) Comparison operator: equal, less, more.
-- @param number (Optional) The number to compare against.
-- @returns A number or a boolean value.
-- @usage
-- if ArmorSetParts(T13) >=2 and target.HealthPercent() <60
--	Spell(ferocious_bite)
-- if ArmorSetParts(T13 more 1) and TargetHealthPercent(less 60)
--	Spell(ferocious_bite)
	armorsetparts = function(condition)
		local nombre = 0
		if OvaleEquipement.nombre[condition[1]] then
			nombre = OvaleEquipement.nombre[condition[1]]
		end
		return compare(nombre, condition[2], condition[3])
	end,
	-- Get the attack power
	-- returns : bool or number
	attackpower = function(condition)
		local base, posBuff, negBuff = UnitAttackPower("player")
		return compare(base + posBuff + negBuff, condition[1], condition[2])
	end,
	buffcount = function(condition)
		return OvaleState:GetExpirationTimeOnAnyTarget(condition[1]), 0, 0
	end,
{'fraction_non_alphanumeric': 0.10745193432193478, 'fraction_numerical': 0.014688684099733357, 'mean_word_length': 3.2841683366733467, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4395153', 'n_tokens_mistral': 8340, 'n_tokens_neox': 7441, 'n_words': 2420}