Quantcast

- code refactoring

Sidoine De Wispelaere [08-13-12 - 20:46]
- code refactoring
- added target option to AddIcon
- better support for casting on focus

git-svn-id: svn://svn.curseforge.net/wow/ovale/mainline/trunk@484 d5049fe3-3747-40f7-a4b5-f36d6801af5f
Filename
Condition.lua
Locale-esES.lua
Ovale.lua
Ovale.toc
OvaleActionBar.lua
OvaleAura.lua
OvaleBestAction.lua
OvaleCompile.lua
OvaleCondition.lua
OvaleData.lua
OvaleEnemies.lua
OvaleEquipement.lua
OvaleFrame.lua
OvaleFuture.lua
OvaleGUID.lua
OvaleIcone.lua
OvaleIcone.xml
OvaleOptions.lua
OvaleSpellDamage.lua
OvaleState.lua
OvaleSwing.lua
compiler.pl
defaut/Pretre.lua
diff --git a/Condition.lua b/Condition.lua
deleted file mode 100644
index e976bd0..0000000
--- a/Condition.lua
+++ /dev/null
@@ -1,1234 +0,0 @@
-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 fearSpellList = nil
-local stunSpellList = nil
-local incapacitateSpellList = nil
-local rootSpellList = nil
-
-local function buildRootSpellList()
-	if (rootSpellList) then
-		return
-	end
-	rootSpellList = {}
-	for k, v in pairs(Ovale.buffSpellList.fear) do
-		rootSpellList[v] = true
-	end
-end
-
-local function buildStunSpellList()
-	if (stunSpellList) then
-		return
-	end
-	stunSpellList = {}
-	for k, v in pairs(Ovale.buffSpellList.stun) do
-		stunListList[v] = true
-	end
-end
-
-local function buildIncapacitateSpellList()
-	if (incapacitateSpellList) then
-		return
-	end
-	incapacitateSpellList = {}
-	for k, v in pairs(Ovale.buffSpellList.incapacitate) do
-		incapacitateSpellList[v] = true
-	end
-end
-
-local function buildFearSpellList()
-	if (fearSpellList) then
-		return
-	end
-	fearSpellList = {}
-	for k, v in pairs(Ovale.buffSpellList.fear) do
-		fearSpellList[v] = true
-	end
-end
-
-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/Ovale.spellHaste
-	elseif (hate == "melee") then
-		return temps/Ovale.meleeHaste
-	else
-		return temps
-	end
-end
-
-local function compare(a, comparison, b)
-	if (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
-	else
-		if (not a or (b~=nil and a<b)) then
-			return 0
-		else
-			return nil
-		end
-	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"
-	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)
-	Ovale:EnableOtherAuras()
-	local otherAura = Ovale.otherAura[spellId]
-	if otherAura then
-		Ovale:Log("otherAura")
-
-		local maxTime = 0
-		local minTime = nil
-		suppTime = suppTime or 10
-		for target,expireTime in pairs(otherAura) do
-			Ovale:Log("target "..target.. " "..expireTime)
-			if target~=UnitGUID("target") then
-				if Ovale.maintenant - suppTime > expireTime then
-					otherAura[target] = nil
-				else
-					if expireTime > maxTime then
-						maxTime = expireTime
-					end
-					if not minTime or diff<minTime then
-						minTime = diff
-					end
-				end
-			end
-		end
-		Ovale:Log("maxTime final "..maxTime)
-		return minTime, maxTime
-	end
-	return nil
-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 = Ovale.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 = Ovale.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 GetManaAndRate(withBerserker)
-	local _,className = UnitClass("player")
-	local current = Ovale.state.mana
-	if current~=lastEnergyValue then
-		lastEnergyValue = current
-		lastEnergyTime = Ovale.currentTime
-	end
-
-	local rate
-
-	if className == "ROGUE" or (className == "DRUID" and GetShapeshiftForm(true) == 3) then
-		rate = 10 * Ovale.meleeHaste
-		if (className == "ROGUE") then
-			local rush = Ovale:GetAura("player", "HELPFUL", 13750)
-			if rush.stacks>0 then
-				rate = rate * 2
-			end
-		elseif withBerserker then
-			local berserk = Ovale:GetAura("player", "HELPFUL", 50334)
-			if berserk.stacks>0 then
-				mana = mana/2
-			end
-		end
-	elseif className == "HUNTER" then
-		rate = 4 * Ovale.meleeHaste
-	else
-		rate = 0
-	end
-
-	return lastEnergyValue, lastEnergyTime, rate
-end
-
-local function GetManaTime(mana, withBerserker)
-	local lastEnergyValue, lastEnergyTime, rate = GetManaAndRate(withBerserker)
-
-	if rate > 0 then
-		local limit = math.ceil((mana - lastEnergyValue) / rate + lastEnergyTime)
-		return limit
-	else
-		if Ovale.state.mana>=mana then
-			return Ovale.currentTime-1
-		else
-			return nil
-		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
-local function GetTargetAura(condition, filter, 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 aura
-	if type(spellId) == "number" then
-		aura = Ovale:GetAura(target, filter, spellId)
-	elseif Ovale.buffSpellList[spellId] then
-		for k,v in pairs(Ovale.buffSpellList[spellId]) do
-			local newAura = Ovale:GetAura(target, filter, v)
-			if 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 = Ovale:GetAura(target, filter, spellId)
-	else
-		Ovale:Print("ERROR: unknown buff "..spellId)
-		Ovale.bug = true
-		return 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
-			if Ovale.spellInfo[spellId] and Ovale.spellInfo[spellId].duration then
-				ending = aura.start + Ovale.spellInfo[spellId].duration
-			else
-				ending = aura.start + condition.forceduration
-			end
-		else
-			ending = aura.ending
-		end
-		return aura.start, ending
-	else
-		return 0,0
-	end
-end
-
-local lastSaved = {}
-local savedHealth = {}
-local targetGUID = {}
-local lastSPD = {}
-
-local function getTargetDead(target)
-	local second = math.floor(Ovale.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 nil
-	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 Ovale.maintenant + duration
-	else
-		return nil
-	end
-end
-
-Ovale.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,]]
-	-- Test how many armor set parts are equiped by the player
-	-- 1 : set number
-	-- 2 : "more" or "less"
-	-- 3 : limit
-	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,
-	attackPower = function(condition)
-		local base, posBuff, negBuff = UnitAttackPower("player")
-		return base + posBuff + negBuff, 0, 0
-	end,
-	BuffDuration = function(condition)
-		--local name, rank, icon, count, debuffType, duration = UnitBuff("player", Ovale:GetSpellInfoOrNil(condition[1]))
-		--if not name then
---			return nil
-	--	end
-		local start, ending = GetTargetAura(condition, "HELPFUL", getTarget(condition.target))
-		return compare(diffTime(start, ending), condition[2], condition[3])
-	end,
-	-- Test if a buff will expire on the player after a given time
-	-- 1 : buff spell id
-	-- 2 : expiration time
-	BuffExpires = function(condition)
-		local start, ending = GetTargetAura(condition, "HELPFUL", getTarget(condition.target))
-		local timeBefore = avecHate(condition[2], condition.haste)
-		if Ovale.trace then
-			Ovale:Print("timeBefore = " .. nilstring(timeBefore))
-			Ovale:Print("start = " .. nilstring(ending))
-		end
-		return addTime(ending, -timeBefore)
-	end,
-	buffExpires = function(condition)
-		local start, ending = GetTargetAura(condition, "HELPFUL", getTarget(condition.target))
-		if ending then
-			return ending - start, start, -1
-		else
-			return nil
-		end
-	end,
-	-- Test if a time has elapsed since the last buff gain
-	-- 1 : buff spell id
-	-- 2 : time since the buff gain
-	BuffGain = function(condition)
-		local spellId = condition[1]
-		local target = getTarget(condition.target)
-		if spellId then
-			if not Ovale.buff[target][spellId] then
-				return 0
-			end
-			local timeGain = Ovale.buff[target][spellId].gain
-			if not timeGain then
-				timeGain = 0
-			end
-
-			return timeGain + condition[2]
-		end
-		return 0
-	end,
-	-- Test if a buff is active
-	-- 1 : the buff spell id
-	-- stacks : minimum number of stacks
-	BuffPresent = function(condition)
-		local start, ending = GetTargetAura(condition, "HELPFUL", getTarget(condition.target))
-		local timeBefore = avecHate(condition[2], condition.haste)
-		return start, addTime(ending, -timeBefore)
-	end,
-	BuffStealable = function(condition)
-		local i = 1
-		local stealable = false
-		local target = getTarget(condition.target)
-		while true do
-			local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable = UnitBuff(target, i)
-			if not name then
-				break
-			end
-			if isStealable then
-				stealable = true
-				break
-			end
-			i = i + 1
-		end
-		return testbool(stealable, condition[1])
-	end,
-	Casting = function(condition)
-		if Ovale.currentSpellId == condition[1] then
-			return Ovale.startCast, Ovale.endCast
-		else
-			return nil
-		end
-	end,
-	CastTime = function(condition)
-		local name, rank, icon, cost, isFunnel, powerType, castTime = Ovale:GetSpellInfoOrNil(condition[1])
-		if Ovale.trace then
-			Ovale:Print("castTime/1000 = " .. (castTime/1000) .. " " .. condition[2] .. " " .. condition[3])
-		end
-		return compare(castTime/1000, condition[2], condition[3])
-	end,
-	castTime = function(condition)
-		local name, rank, icon, cost, isFunnel, powerType, castTime = Ovale:GetSpellInfoOrNil(condition[1])
-		return castTime/1000, 0, 0
-	end,
-	-- Test if a list of checkboxes is off
-	-- 1,... : the checkboxes names
-	CheckBoxOff = function(condition)
-		for k,v in pairs(condition) do
-			if (Ovale:IsChecked(v)) then
-				return nil
-			end
-		end
-		return 0
-	end,
-	-- Test if a list of checkboxes is on
-	-- 1,... : the checkboxes names
-	CheckBoxOn = function(condition)
-		for k,v in pairs(condition) do
-			if (not Ovale:IsChecked(v)) then
-				return nil
-			end
-		end
-		return 0
-	end,
-	Class = function(condition)
-		local loc, noloc = UnitClass(getTarget(condition.target))
-		return testbool(noloc == condition[1], condition[2])
-	end,
-	-- Test the target classification
-	-- 1 : normal, elite, or worldboss
-	Classification = function(condition)
-		local classification
-		local target = getTarget(condition.target)
-		if UnitLevel(target)==-1 then
-			classification = "worldboss"
-		else
-			classification = UnitClassification(target);
-			if (classification == "rareelite") then
-				classification = "elite"
-			elseif (classification == "rare") then
-				classification = "normal"
-			end
-		end
-
-		if (condition[1]==classification) then
-			return 0
-		else
-			return nil
-		end
-	end,
-	-- Test how many combo points a feral druid or a rogue has
-	-- 1 : "less" or "more"
-	-- 2 : the limit
-	ComboPoints = function(condition)
-		local points = Ovale.state.combo
-		return compare(points, condition[1], condition[2])
-	end,
-	comboPoints = function(condition)
-		return Ovale.state.combo, 0, 0
-	end,
-	Counter = function(condition)
-		return compare(Ovale:GetCounterValue(condition[1]), condition[2], condition[3])
-	end,
-	counter = function(condition)
-		return Ovale:GetCounterValue(condition[1]), 0, 0
-	end,
-	CreatureFamily = function(condition)
-		return testbool(UnitCreatureFamily(getTarget(condition.target)) == LBCT[condition[1]], condition[2])
-	end,
-	CreatureType = function(condition)
-		for _,v in pairs(condition) do
-			if (UnitCreatureType(getTarget(condition.target)) == LBCT[v]) then
-				return 0
-			end
-		end
-		return nil
-	end,
-	damage = function(condition)
-		local spellInfo = Ovale:GetSpellInfo(condition[1])
-		if not spellInfo then
-			return nil
-		end
-		local ret = (spellInfo.base or 0)
-		if spellInfo.bonuscp then
-			ret = ret + (Ovale.state.combo * spellInfo.bonuscp)
-		end
-		if spellInfo.bonusholy then
-			ret = ret + (Ovale.state.holy * spellInfo.bonusholy)
-		end
-		if spellInfo.bonusap then
-			ret = ret + spellInfo.bonusap * UnitAttackPower("player")
-		end
-		if spellInfo.bonusapcp then
-			ret = ret + spellInfo.bonusapcp * UnitAttackPower("player") * Ovale.state.combo
-		end
-		if spellInfo.bonusapholy then
-			ret = ret + spellInfo.bonusapholy * UnitAttackPower("player") * Ovale.state.holy
-		end
-		if spellInfo.bonussp then
-			ret = ret + spellInfo.bonussp * GetSpellBonusDamage(2)
-		end
-		if spellInfo.bonusspholy then
-			ret = ret + spellInfo.bonusspholy * GetSpellBonusDamage(2) * Ovale.state.holy
-		end
-		return ret * Ovale.damageMultiplier, 0, 0
-	end,
-	damageMultiplier = function(condition)
-		return self.damageMultiplier, 0, 0
-	end,
-	DeadIn = function(condition)
-		local deadAt = getTargetDead(getTarget(condition.target))
-		if condition[1] == "more" then
-			return 0, addTime(deadAt, -condition[2])
-		else
-			return addTime(deadAt, -condition[2]), nil
-		end
-	end,
-	deadIn = function(condition)
-		return getTargetDead(getTarget(condition.target)), 0, -1
-	end,
-	-- Test if a debuff will expire on the target after a given time, or if there is less than the
-	-- given number of stacks (if stackable)
-	-- 1 : buff spell id
-	-- 2 : expiration time
-	-- stacks : how many stacks
-	-- mine : 1 means that if the debuff is not ours, the debuff is ignored
-	DebuffExpires = function(condition)
-		local start, ending = GetTargetAura(condition, "HARMFUL", getTarget(condition.target))
-		local tempsMax = avecHate(condition[2], condition.haste)
-		return addTime(ending, -tempsMax)
-	end,
-	debuffExpires = function(condition)
-		local start, ending = GetTargetAura(condition, "HARMFUL", getTarget(condition.target))
-		if ending then
-			return ending - start, start, -1
-		else
-			return nil
-		end
-	end,
-	DebuffPresent = function(condition)
-		local start, ending = GetTargetAura(condition, "HARMFUL", getTarget(condition.target))
-		local timeBefore = avecHate(condition[2], condition.haste)
-		return start, addTime(ending, -timeBefore)
-	end,
-	debuffTick = function(condition)
-		local start, ending = GetTargetAura(condition, "HARMFUL", getTarget(condition.target))
-		local si = Ovale.spellInfo[condition[1]]
-		if not si or not si.duration then
-			return nil
-		end
-		local ticks = floor(Ovale.spellHaste * (si.duration/(si.tick or 3)) + 0.5)
-		local tickLength = (ending - start) / ticks
-		local tickTime = start + tickLength
-		for i=1,ticks do
-			if Ovale.currentTime<=tickTime then
-				break
-			end
-			tickTime = tickTime + tickLength
-		end
-		return tickTime, 0, -1
-	end,
-	Distance = function(condition)
-		if LRC then
-			local target = getTarget(condition.target)
-			local minRange, maxRange = LRC:GetRange(target)
-			if maxRange == nil or minRange == nil then
-				return nil
-			end
-			if condition[1] == "more" then
-				if condition[2]~=nil and maxRange>condition[2] then
-					return 0
-				else
-					return nil
-				end
-			else
-				if condition[2]~=nil and minRange<condition[2] then
-					return 0
-				else
-					return nil
-				end
-			end
-		end
-	end,
-	distance = function(condition)
-		if LRC then
-			return LRC:GetRange(getTarget(condition.target))
-		else
-			return nil
-		end
-	end,
-	--Compare to eclipse power. <0 lunar, >0 solar
-	Eclipse = function(condition)
-		return compare(Ovale.state.eclipse, condition[1], condition[2])
-	end,
-	eclipse = function(condition)
-		return Ovale.state.eclipse
-	end,
-	EffectiveMana = function(condition)
-		local limit = GetManaTime(condition[2], true)
-		if condition[1]=="more" then
-			return limit, nil
-		else
-			return 0,limit
-		end
-	end,
-	effectiveMana = function(condition)
-		return GetManaAndRate(true)
-	end,
-	EndCastTime = function(condition)
-		local name, rank, icon, cost, isFunnel, powerType, castTime = Ovale:GetSpellInfoOrNil(condition[1])
-		local actionCooldownStart, actionCooldownDuration, actionEnable = Ovale:GetComputedSpellCD(condition[1])
-		local startCast = actionCooldownStart + actionCooldownDuration
-		if startCast<Ovale.currentTime then
-			startCast = Ovale.currentTime
-		end
-		return startCast + castTime/1000
-	end,
-	enemies = function(condition)
-		return Ovale:GetNumberOfEnemies(), 0, 0
-	end,
-	Exists = function(condition)
-		return testbool(UnitExists(getTarget(condition.target)) == 1, condition[1])
-	end,
-	Glyph = function(condition)
-		local present = false
-		for i = 1, GetNumGlyphSockets() do
-			local enabled, glypType, glyphTooltipIndex, glyphSpellID = GetGlyphSocketInfo(i)
-			if (glyphSpellID == condition[1]) then
-				present = true
-				break
-			end
-		end
-		return testbool(present, condition[2])
-	end,
-	HasFullControl = function(condition)
-		return testbool(HasFullControl(), condition[1])
-	end,
-	HasShield = function(condition)
-		local _,_,id = string.find(GetInventoryItemLink("player",GetInventorySlotInfo("SecondaryHandSlot")) or "","(item:%d+:%d+:%d+:%d+)")
-		if (not id) then
-			return testbool(false, condition[1])
-		end
-
-		local _,_,_,_,_,_,_,_,itemLoc = GetItemInfo(id)
-		return testbool(itemLoc=="INVTYPE_SHIELD", condition[1])
-	end,
-	HolyPower = function(condition)
-		return compare(Ovale.state.holy, condition[1], condition[2])
-	end,
-	holyPower = function(condition)
-		return Ovale.state.holy, 0, 0
-	end,
-	InCombat = function(condition)
-		return testbool(Ovale.enCombat, condition[1])
-	end,
-	InRange = function(condition)
-		local spellName = GetSpellInfo(condition[1])
-		return testbool(IsSpellInRange(spellName,getTarget(condition.target))==1,condition[2])
-	end,
-	item = function(condition)
-		local itemId = element.params[1]
-		local actionCooldownStart, actionCooldownDuration, actionEnable = GetItemCooldown(itemId)
-		return actionCooldownDuration, actionCooldownStart, -1
-	end,
-	ItemCount = function(condition)
-		if condition.charges == 1 then
-			return compare(GetItemCount(condition[1], false, true), condition[2], condition[3])
-		else
-			return compare(GetItemCount(condition[1]), condition[2], condition[3])
-		end
-	end,
-	itemCount = function(condition)
-		if condition.charges == 1 then
-			return GetItemCount(condition[1], false, true), 0, 0
-		else
-			return GetItemCount(condition[1]), 0, 0
-		end
-	end,
-	IsCasting = function(condition)
-		local casting
-		local target = getTarget(condition.target)
-		local spellId = condition.spell
-		if not spellId then
-			return testbool(UnitCastingInfo(target) or UnitChannelInfo(target), condition[1])
-		elseif type(spellId) == "number" then
-			local spellName = GetSpellInfo(spellId)
-			return testbool(UnitCastingInfo(target)==spellName or UnitChannelInfo(target) == spellName, condition[1])
-		elseif Ovale.buffSpellList[spellId] then
-			local castSpellName = UnitCastingInfo(target) or UnitChannelInfo(target)
-			local found = false
-			for k,v in pairs(Ovale.buffSpellList[spellId]) do
-				local spellName = GetSpellInfo(v)
-				if spellName == castSpellName then
-					found = true
-					break
-				end
-			end
-			return testbool(found, condition[1])
-		elseif spellId == "harmful" then
-			local castSpellName = UnitCastingInfo(target) or UnitChannelInfo(target)
-			return testbool(castSpellName and IsHarmfulSpell(castSpellName), condition[1])
-		elseif spellId == "helpful" then
-			local castSpellName = UnitCastingInfo(target) or UnitChannelInfo(target)
-			return testbool(castSpellName and IsHelpfulSpell(castSpellName), condition[1])
-		end
-	end,
-	IsFeared = function(condition)
-		buildFearSpellList()
-		return testbool(not HasFullControl() and isDebuffInList(fearSpellList), condition[1])
-	end,
-	IsFriend = function(condition)
-		return testbool(UnitIsFriend("player", getTarget(condition.target)), condition[1])
-	end,
-	IsIncapacitated = function(condition)
-		buildIncapacitateSpellList()
-		return testbool(not HasFullControl() and isDebuffInList(incapacitateSpellList), condition[1])
-	end,
-	IsInterruptible = function(condition)
-		local target = getTarget(condition.target)
-		local spell, rank, name, icon, start, ending, isTradeSkill, castID, protected = UnitCastingInfo(target)
-		if not spell then
-			spell, rank, name, icon, start, ending, isTradeSkill, protected = UnitChannelInfo(target)
-		end
-		return testbool(protected ~= nil and not protected, condition[1])
-	end,
-	IsRooted = function(condition)
-		buildRootSpellList()
-		return testbool(isDebuffInList(rootSpellList), condition[1])
-	end,
-	IsStunned = function(condition)
-		buildStunSpellList()
-		return testbool(not HasFullControl() and isDebuffInList(stunSpellList), condition[1])
-	end,
-	LastSpellDamage = function(condition)
-		local spellId = condition[1]
-		if not Ovale.spellDamage[spellId] then
-			return nil
-		end
-		return compare(Ovale.spellDamage[spellId], condition[2], condition[3])
-	end,
-	lastSpellDamage = function(condition)
-		return Ovale.spellDamage(condition[1])
-	end,
-	lastSpellDamageMultiplier = function(condition)
-		return Ovale.lastSpellDM[condition[1]], 0, 0
-	end,
-	lastSpellAttackPower = function(condition)
-		return Ovale.lastSpellAP[condition[1]], 0, 0
-	end,
-	lastSpellSpellPower = function(condition)
-		return Ovale.lastSpellSP[condition[1]], 0, 0
-	end,
-	LastSwing = function(condition)
-		local ret = OvaleSwing:GetLast(condition[1])
-		if condition[2] and ret then
-			ret = ret + condition[2]
-		end
-		return 0, ret
-	end,
-	lastSwing = function(condition)
-		return Ovale.currentTime - OvaleSwing:GetLast(condition[1]), 0, 1
-	end,
-	-- Compare with the player level
-	-- 1 : "less" or "more"
-	-- 2 : the limit
-	Level = function(condition)
-		return compare(UnitLevel(getTarget(condition.target)), condition[1], condition[2])
-	end,
-	level = function(condition)
-		return UnitLevel(condition.target)
-	end,
-	Life = function(condition)
-		local target = getTarget(condition.target)
-		return compare(UnitHealth(target), condition[1], condition[2])
-	end,
-	life = function(condition)
-		local target = getTarget(condition.target)
-		return UnitHealth(target), 0, 0
-	end,
-	LifeMissing = function(condition)
-		local target = getTarget(condition.target)
-		return compare(UnitHealthMax(target)-UnitHealth(target), condition[1], condition[2])
-	end,
-	lifeMissing = function(condition)
-		local target = getTarget(condition.target)
-		return UnitHealthMax(target)-UnitHealth(target), 0, 0
-	end,
-	-- Test if the player life is bellow/above a given value in percent
-	-- 1 : "less" or "more"
-	-- 2 : the limit, in percent
-	LifePercent = function(condition)
-		local target = getTarget(condition.target)
-		if UnitHealthMax(target) == nil or UnitHealthMax(target) == 0 then
-			return nil
-		end
-		return compare(UnitHealth(target)/UnitHealthMax(target), condition[1], condition[2]/100)
-	end,
-	lifePercent = function(condition)
-		local target = getTarget(condition.target)
-		if UnitHealthMax(target) == nil or UnitHealthMax(target) == 0 then
-			return nil
-		end
-		return 100 * UnitHealth(target)/UnitHealthMax(target), 0, 0
-	end,
-	-- Test if a list item is selected
-	-- 1 : the list name
-	-- 2 : the item name
-	List = function(condition)
-		if (condition[1]) then
-			if (Ovale:GetListValue(condition[1]) == condition[2]) then
-				return 0
-			end
-		end
-		return nil
-	end,
-	-- Test if the player mana is above/bellow a given value
-	-- 1 : "less" or "more"
-	-- 2 : the mana/energy/rage... limit
-	Mana = function(condition)
-		local target = getTarget(condition.target)
-		if target == "player" then
-			local limit = GetManaTime(condition[2], false)
-			if condition[1]=="more" then
-				return limit, nil
-			else
-				return 0,limit
-			end
-		else
-			return compare(UnitPower(target), condition[1], condition[2])
-		end
-	end,
-	mana = function(condition)
-		return GetManaAndRate(false)
-	end,
-	ManaPercent = function(condition)
-		local target = getTarget(condition.target)
-		if UnitPowerMax(target) == 0 then
-			return nil
-		end
-		return compare(UnitPower(target)/UnitPowerMax(target), condition[1], condition[2]/100)
-	end,
-	manaPercent = function(condition)
-		local target = getTarget(condition.target)
-		if UnitPowerMax(target) == 0 then
-			return nil
-		end
-		local value, t, rate = GetManaAndRate(false)
-		local conversion = 100/UnitPowerMax(target)
-		return value * conversion, t, rate * conversion
-	end,
-	MaxHealth = function(condition)
-		local target = getTarget(condition.target)
-		return compare(UnitMaxHealth(target), condition[1], condition[2])
-	end,
-	maxHealth = function(condition)
-		return UnitMaxHealth(getTarget(condition.target)), 0, 0
-	end,
-	maxMana = function(condition)
-		return UnitPowerMax(getTarget(condition.target)), 0, 0
-	end,
-	NextSwing = function(condition)
-		local ret = OvaleSwing:GetNext(condition[1])
-		if condition[2] and ret then
-			ret = ret - condition[2]
-		end
-		return ret
-	end,
-	nextSwing = function(condition)
-		return OvaleSwing:GetNext(condition[1]) - Ovale.currentTime, 0, -1
-	end,
-	OtherDebuffExpires = function(condition)
-		local minTime, maxTime = getOtherAura(condition[1], condition[3])
-		if minTime then
-			local timeBefore = condition[2] or 0
-			return minTime - timeBefore, nil
-		end
-		return 0, nil
-	end,
-	OtherDebuffPresent = function(condition)
-		local minTime, maxTime = getOtherAura(condition[1], condition[3])
-		if maxTime and maxTime>0 then
-			local timeBefore = condition[2] or 0
-			return 0, addTime(maxTime, -timeBefore)
-		end
-		return nil
-	end,
-	OtherAuraExpires = OtherDebuffExpires,
-	OtherAuraPresent = OtherDebuffPresent,
-	otherAura = function(condition)
-		local minTime, maxTime = getOtherAura(condition[1])
-		return 0, maxTime, -1
-	end,
-	Present = function(condition)
-		local present = UnitExists(getTarget(condition.target)) and not UnitIsDead(getTarget(condition.target))
-		return testbool(present, condition[1])
-	end,
-	-- Test if any player pet is present (or not)
-	-- 1 : "yes" or "no"
-	PetPresent = function(condition)
-		local present = UnitExists("pet") and not UnitIsDead("pet")
-		return testbool(present, condition[1])
-	end,
-	-- Test the target level difference with the player
-	-- 1 : "less" or "more"
-	-- 2 : [target level]-[player level] limit
-	RelativeLevel = function(condition)
-		local difference
-		local target = getTarget(condition.target)
-		if UnitLevel(target) == -1 then
-			difference = 3
-		else
-			difference = UnitLevel(target) - UnitLevel("player")
-		end
-
-		return compare(difference, condition[1], condition[2])
-	end,
-	remainingCastTime = function(condition)
-		local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible = UnitCastingInfo(getTarget(condition.target))
-		if not endTime then
-			return nil
-		end
-		return 0, endTime/1000, -1
-	end,
-	Runes = function(condition)
-		return GetRune(condition)
-	end,
-	runes = function(condition)
-		local ret = GetRune(condition)
-		if not ret then
-			return nil
-		end
-		if ret < Ovale.maintenant then
-			ret = Ovale.maintenant
-		end
-		return 0, ret, -1
-	end,
-	SoulShards = function(condition)
-		return compare(Ovale.state.shard, condition[1], condition[2])
-	end,
-	soulShards = function(condition)
-		return Ovale.state.shard
-	end,
-	Speed = function(condition)
-		return compare(GetUnitSpeed(getTarget(condition.target))*100/7, condition[1], condition[2])
-	end,
-	speed = function(condition)
-		return GetUnitSpeed(getTarget(condition.target))*100/7
-	end,
-	spell = function(condition)
-		local actionCooldownStart, actionCooldownDuration, actionEnable = Ovale:GetComputedSpellCD(condition[1])
-		return actionCooldownDuration, actionCooldownStart, -1
-	end,
-	spellPower = function(condition)
-		return GetSpellBonusDamage(2), 0, 0
-	end,
-	-- Test if the player is in a given stance
-	-- 1 : the stance
-	Stance = function(condition)
-		if (GetShapeshiftForm(true) == condition[1]) then
-			return 0
-		else
-			return nil
-		end
-	end,
-	Stealthed = function(condition)
-		return testbool(IsStealthed(), condition[1])
-	end,
-	-- Test how many talent points has been spent in a talent
-	-- 1 : the talent identifier (use /script print(Ovale.talentNameToId["Talent name"]) to retreive)
-	-- 2 : "more" or "less"
-	-- 3 : the limit
-	TalentPoints = function(condition)
-		if (not Ovale.listeTalentsRemplie) then
-			Ovale:RemplirListeTalents()
-			return nil
-		end
-		return compare(Ovale.pointsTalent[condition[1]], condition[2], condition[3])
-	end,
-	talentPoints = function(condition)
-		if (not Ovale.listeTalentsRemplie) then
-			Ovale:RemplirListeTalents()
-			return nil
-		end
-		return Ovale.pointsTalent[condition[1]], 0, 0
-	end,
-	-- Test if the target's target is the player (or is not)
-	-- 1 : "yes" (it should be the player) or "no"
-	TargetIsPlayer = function(condition)
-		return testbool(UnitIsUnit("player",getTarget(condition.target).."target"), condition[1])
-	end,
-	Threat = function(condition)
-		local isTanking, status, threatpct = UnitDetailedThreatSituation("player", getTarget(condition.target))
-		return compare(threatpct, condition[1], condition[2])
-	end,
-	threat = function(condition)
-		local isTanking, status, threatpct = UnitDetailedThreatSituation("player", getTarget(condition.target))
-		return threatpct
-	end,
-	TimeInCombat = function(condition)
-		if not Ovale.combatStartTime then
-			return nil
-		elseif condition[1] == "more" then
-			return Ovale.combatStartTime + condition[2]
-		else
-			return 0, Ovale.combatStartTime + condition[2]
-		end
-	end,
-	timeInCombat = function(condition)
-		return Ovale.maintenant - Ovale.combatStartTime, Ovale.maintenant, 1
-	end,
-	timeToDie = function(condition)
-		return 0, getTargetDead(getTarget(condition.target)), -1
-	end,
-	timeWithHaste = function(condition)
-		return avecHate(condition[1], "spell"),0,0
-	end,
-	TotemExpires = function(condition)
-		if type(condition[1]) ~= "number" then
-			condition[1] = totemType[condition[1]]
-		end
-
-		local haveTotem, totemName, startTime, duration = GetTotemInfo(condition[1])
-		if not startTime then
-			return 0
-		end
-		if (condition.totem and Ovale:GetSpellInfoOrNil(condition.totem)~=totemName) then
-			return 0
-		end
-		return addTime(startTime + duration, -(condition[2] or 0))
-	end,
-	TotemPresent = function(condition)
-		if type(condition[1]) ~= "number" then
-			condition[1] = totemType[condition[1]]
-		end
-
-		local haveTotem, totemName, startTime, duration = GetTotemInfo(condition[1])
-		if not startTime then
-			return nil
-		end
-		if (condition.totem and Ovale:GetSpellInfoOrNil(condition.totem)~=totemName) then
-			return nil
-		end
-		return startTime, startTime + duration
-	end,
-	Tracking = function(condition)
-		local what = Ovale:GetSpellInfoOrNil(condition[1])
-		local numTrackingTypes = GetNumTrackingTypes()
-		local present = false
-		for i=1,numTrackingTypes do
-			local name, texture, active = GetTrackingInfo(i)
-			if name == what then
-				present = (active == 1)
-				break
-			end
-		end
-		return testbool(present, condition[2])
-	end,
-	WeaponEnchantExpires = function(condition)
-		local hasMainHandEnchant, mainHandExpiration, mainHandCharges, hasOffHandEnchant, offHandExpiration, offHandCharges = GetWeaponEnchantInfo()
-		if (condition[1] == "mainhand") then
-			if (not hasMainHandEnchant) then
-				return 0
-			end
-			mainHandExpiration = mainHandExpiration/1000
-			if ((condition[2] or 0) >= mainHandExpiration) then
-				return 0
-			else
-				return Ovale.maintenant + mainHandExpiration - condition[2]
-			end
-		else
-			if (not hasOffHandEnchant) then
-				return 0
-			end
-			offHandExpiration = offHandExpiration/1000
-			if ((condition[2] or 0) >= offHandExpiration) then
-				return 0
-			else
-				return Ovale.maintenant + offHandExpiration - condition[2]
-			end
-		end
-	end,
-}
-
-Ovale.conditions.health = Ovale.conditions.life
-Ovale.conditions.Health = Ovale.conditions.Life
-Ovale.conditions.healthPercent = Ovale.conditions.lifePercent
-Ovale.conditions.HealthPercent = Ovale.conditions.LifePercent
-Ovale.conditions.HealthMissing = Ovale.conditions.LifeMissing
diff --git a/Locale-esES.lua b/Locale-esES.lua
new file mode 100644
index 0000000..030c053
--- /dev/null
+++ b/Locale-esES.lua
@@ -0,0 +1,4 @@
+local L = LibStub:GetLibrary("AceLocale-3.0"):NewLocale("Ovale", "esES", false)
+if not L then return end
+
+--@localization(locale="esES", format="lua_additive_table", same-key-is-true=true, handle-subnamespaces="concat")@
\ No newline at end of file
diff --git a/Ovale.lua b/Ovale.lua
index 4812bb2..5549ffc 100644
--- a/Ovale.lua
+++ b/Ovale.lua
@@ -1,26 +1,19 @@
-local L = LibStub("AceLocale-3.0"):GetLocale("Ovale")

 Ovale = LibStub("AceAddon-3.0"):NewAddon("Ovale", "AceEvent-3.0", "AceConsole-3.0")
+
+--<private-static-properties>
+local L = LibStub("AceLocale-3.0"):GetLocale("Ovale")
 local Recount = Recount
 local Skada = Skada
+--</private-static-properties>

+--<public-static-properties>
 --Default scripts (see "defaut" directory)
 Ovale.defaut = {}
 --The table of check boxes definition
 Ovale.casesACocher = {}
---key: spell name / value: action icon id
-Ovale.actionSort = {}
---key: talentId / value: points in this talent
-Ovale.pointsTalent = {}
---key: talentId / value: talent name (not used)
-Ovale.talentIdToName = {}
-Ovale.spellList = {}
---key: talent name / value: talent id
-Ovale.talentNameToId = {}
 --allows to do some initialization the first time the addon is enabled
 Ovale.firstInit = false
---allows to fill the player talent tables on first use
-Ovale.listeTalentsRemplie = false
 --the frame with the icons
 Ovale.frame = nil
 --check boxes GUI items
@@ -31,235 +24,21 @@ Ovale.dropDowns = {}
 Ovale.masterNodes = nil
 --set it if there was a bug, traces will be enabled on next frame
 Ovale.bug = false
+Ovale.traced = false
 --trace next script function calls
 Ovale.trace=false
 --in combat?
 Ovale.enCombat = false
---current computed spell haste. "2" means 2 times faster
-Ovale.spellHaste = 1
---current computed melee haste TODO: why I don't use character sheet value anyway?
-Ovale.meleeHaste = 1
---current auras
-Ovale.aura = { player = {}, target = {}}
---allow to track the current target
-Ovale.targetGUID = nil
---spell info from the current script (by spellId)
-Ovale.spellInfo = {}
---track when a buff was applied (used for the old eclipse mechanism, maybe this could be removed?)
-Ovale.buff = {}
---player class
-Ovale.className = nil
---the state in the current frame
---TODO: really, the simulator should be in its own class
-Ovale.state = {rune={}, cd = {}, counter={}}
---spells that count for scoring
-Ovale.scoreSpell = {}
---tracks debuffs on the units that are not the current target
-Ovale.otherAura = {}
 --score in current combat
 Ovale.score = 0
 --maximal theoric score in current combat
 Ovale.maxScore = 0
---increased at each frame, allows to know if the aura was updated this frame
---TODO: aura should be tracked using combat log events or something like that
---and it should be in its own class
-Ovale.serial = 0
---spell counter (see Counter function)
-Ovale.counter = {}
---the spells that the player has casted but that did not reach their target
---the result is computed by the simulator, allowing to ignore lag or missile travel time
-Ovale.lastSpell = {}
---the damage of the last spell or dot (by id)
-Ovale.spellDamage = {}
---the attack power of the last spell
-Ovale.lastSpellAP = {}
-Ovale.lastSpellSP = {}
-Ovale.lastSpellDM = {}
-Ovale.damageMultiplier = 1
-Ovale.numberOfEnemies = nil
-Ovale.enemies = {}
-Ovale.refreshNeeded = false
+Ovale.refreshNeeded = {}
 Ovale.compileOnItems = false
-
--- List haste buff that does not appear in the character sheet and that are not raid wide buffs
-Ovale.selfHasteBuff =
-{
-	[53657] = 9, -- Judgement of the pure
-	[49016] = 20 -- Unholy Frenzy
-}
-
--- List temporary damage multiplier
-Ovale.selfDamageBuff =
-{
-	[5217] = 1.15, -- Tiger's fury
-	[57933] = 1.15 -- Tricks of the trade
-}
-
-Ovale.buffSpellList =
-{
-	fear =
-	{
-		5782, -- Fear
-		5484, -- Howl of terror
-		5246, -- Intimidating Shout
-		8122, -- Psychic scream
-	},
-	root =
-	{
-		23694, -- Improved Hamstring
-		339, -- Entangling Roots
-		122, -- Frost Nova
-		47168, -- Improved Wing Clip
-	},
-	incapacitate =
-	{
-		6770, -- Sap
-		12540, -- Gouge
-		20066, -- Repentance
-	},
-	stun =
-	{
-		5211, -- Bash
-		44415, -- Blackout
-		6409, -- Cheap Shot
-		22427, -- Concussion Blow
-		853, -- Hammer of Justice
-		408, -- Kidney Shot
-		46968, -- Shockwave
-	},
-	strengthagility=
-	{
-		6673, -- Battle Shout
-		8076, -- Strength of Earth
-		57330, -- Horn of Winter
-		93435 --Roar of Courage (Cat, Spirit Beast)
-	},
-	stamina =
-	{
-		21562, -- Fortitude TODO: vérifier
-		469, -- Commanding Shout
-		6307, -- Blood Pact
-		90364 -- Qiraji Fortitude
-	},
-	lowerarmor=
-	{
-		58567, -- Sunder Armor (x3)
-		8647, -- Expose Armor
-		91565, -- Faerie Fire (x3)
-		35387, --Corrosive Spit (x3 Serpent)
-		50498 --Tear Armor (x3 Raptor)
-	},
-	magicaldamagetaken=
-	{
-		65142, -- Ebon Plague
-		60433, -- Earth and Moon
-		93068, -- Master Poisoner
-		1490, -- Curse of the Elements
-		85547, -- Jinx 1
-		86105, -- Jinx 2
-		34889, --Fire Breath (Dragonhawk)
-		24844 --Lightning Breath (Wind serpent)
-	},
-	magicalcrittaken=
-    {
-        17800, -- Shadow and Flame
-        22959 -- Critical Mass
-    },
-	physicaldamagetaken=
-	{
-		30069, -- Blood Frenzy (rank 1)
-		30070, -- Blood Frenzy (rank 2)
-		81327, -- Brittle Bones (rank 1)
-		81328, -- Brittle Bones (rank 2)
-		58684, -- Savage Combat (rank 1)
-		58683, -- Savage Combat (rank 2)
-		55749, -- Acid Spit (Worm)
-		50518, -- Ravage (Ravager)
-	},
-	lowerphysicaldamage=
-	{
-		99, -- Demoralizing Roar
-		702, -- Curse of Weakness
-		1160, -- Demoralizing Shout
-		26017, -- Vindication
-		81130, -- Scarlet Fever
-		50256, --Demoralizing Roar (Bear)
-		24423, -- Demoralizing Screech (Carrion Bird)
-	},
-	meleeslow=
-	{
-		55095, --Icy Touch
-		58179, --Infected Wounds rank 1
-		58180, --Infected Wounds rank 2
-		68055, --Judgments of the just
-		6343, --Thunderclap
-		8042, --Earth Shock
-		54404, --Dust Cloud (Tallstrider)
-		90315, -- Tailspin (Fox)
-	},
-	castslow =
-	{
-		1714, --Curse of Tongues
-        58604, --Lava Breath (Core Hound)
-        50274, --Spore Cloud (Sporebat)
-        5761, --Mind-numbing Poison
-        73975, --Necrotic Strike
-        31589 --Slow
-	},
-	bleed=
-	{
-		33876, --Mangle cat
-		33878, --Mangle bear
-		46856, -- Trauma rank 1
-		46857, -- Trauma rank 2
-		16511, --Hemorrhage
-		50271, --Tendon Rip (Hyena)
-		35290 --Gore (Boar)
-	},
-	heroism=
-	{
-		2825, --Bloodlust
-		32182, --Heroism
-		80353, --Time warp
-		90355 -- Ancient Hysteria (Core Hound)
-	},
-	meleehaste =
-	{
-		8515, -- Windfury
-		55610, -- Improved Icy Talons
-		53290 -- Hunting Party
-	},
-	spellhaste =
-	{
-		24907, -- Moonkin aura
-		2895, -- Wrath of Air Totem
-		49868 -- Mind Quickening
-	},
-	enrage =
-	{
-		49016, -- Unholy Frenzy
-		18499, -- Berserker Rage
-		12292, -- Death Wish
-		12880, -- Enrage (rank 1)
-		14201, -- Enrage (rank 2)
-		14202, -- Enrage (rank 3)
-		5229, -- Enrage (Bear)
-        52610, -- Savage Roar (Cat)
-        76691, -- Vengeance (All Tank Specs)
-	},
-	criticalstrike =
-	{
-		51740, -- Elemental Oath
-		51698, -- Honor Among Thieves (rank 1)
-		51700, -- Honor Among Thieves (rank 2)
-		51701, -- Honor Among Thieves (rank 3)
-		17007, -- Leader of the Pack
-		29801, -- Rampage
-		24604, -- Furious Howl (Wolf)
-		90309, -- Terrifying Roar (Devilsaur)
-	}
-}
-
+Ovale.combatStartTime = nil
+Ovale.needCompile = false
+Ovale.listes = {}
+--</public-static-properties>

 --Key bindings
 BINDING_HEADER_OVALE = "Ovale"
@@ -269,407 +48,9 @@ BINDING_NAME_OVALE_CHECKBOX2 = L["Inverser la boîte à cocher "].."(3)"
 BINDING_NAME_OVALE_CHECKBOX3 = L["Inverser la boîte à cocher "].."(4)"
 BINDING_NAME_OVALE_CHECKBOX4 = L["Inverser la boîte à cocher "].."(5)"

---GUI option
-local options =
-{
-	type = "group",
-	args =
-	{
-		apparence =
-		{
-			name = L["Apparence"],
-			type = "group",
-			args =
-			{
-				combatUniquement =
-				{
-					order = 1,
-					type = "toggle",
-					name = L["En combat uniquement"],
-					get = function(info)
-						return Ovale.db.profile.apparence.enCombat
-					end,
-					set = function(info, v)
-						Ovale.db.profile.apparence.enCombat = v
-						Ovale:UpdateVisibility()
-					end,
-					width = "full"
-				},
-				targetOnly =
-				{
-					order = 1.5,
-					type = "toggle",
-					name = L["Si cible uniquement"],
-					get = function(info)
-						return Ovale.db.profile.apparence.avecCible
-					end,
-					set = function(info, v)
-						Ovale.db.profile.apparence.avecCible = v
-						Ovale:UpdateVisibility()
-					end,
-					width = "full"
-				},
-				iconScale =
-				{
-					order = 2,
-					type = "range",
-					name = L["Taille des icônes"],
-					desc = L["La taille des icônes"],
-					min = 0.1, max = 16, step = 0.1,
-					get = function(info) return Ovale.db.profile.apparence.iconScale end,
-					set = function(info,value) Ovale.db.profile.apparence.iconScale = value; Ovale:UpdateFrame() end
-				},
-				secondIconScale =
-				{
-					order = 2.5,
-					type = "range",
-					name = L["Taille du second icône"],
-					min = 0.2, max = 1, step = 0.1,
-					get = function(info) return Ovale.db.profile.apparence.secondIconScale end,
-					set = function(info,value) Ovale.db.profile.apparence.secondIconScale = value; Ovale:UpdateFrame() end
-				},
-				fontScale =
-				{
-					order = 3,
-					type = "range",
-					name = L["Taille des polices"],
-					desc = L["La taille des polices"],
-					min = 0.1, max = 2, step = 0.1,
-					get = function(info) return Ovale.db.profile.apparence.fontScale end,
-					set = function(info,value) Ovale.db.profile.apparence.fontScale = value; Ovale:UpdateFrame() end
-				},
-				smallIconScale =
-				{
-					order = 4,
-					type = "range",
-					name = L["Taille des petites icônes"],
-					desc = L["La taille des petites icônes"],
-					min = 0.1, max = 16, step = 0.1,
-					get = function(info) return Ovale.db.profile.apparence.smallIconScale end,
-					set = function(info,value) Ovale.db.profile.apparence.smallIconScale = value; Ovale:UpdateFrame() end
-				},
-				margin =
-				{
-					order = 5.5,
-					type = "range",
-					name = L["Marge entre deux icônes"],
-					min = -16, max = 64, step = 1,
-					get = function(info) return Ovale.db.profile.apparence.margin end,
-					set = function(info,value) Ovale.db.profile.apparence.margin = value; Ovale:UpdateFrame() end
-				},
-				iconShiftX =
-				{
-					order = 5.6,
-					type = "range",
-					name = L["Décalage horizontal des options"],
-					min = -256, max = 256, step = 1,
-					get = function(info) return Ovale.db.profile.apparence.iconShiftX end,
-					set = function(info,value) Ovale.db.profile.apparence.iconShiftX = value; Ovale:UpdateFrame() end
-				},
-				iconShiftY =
-				{
-					order = 5.7,
-					type = "range",
-					name = L["Décalage vertical des options"],
-					min = -256, max = 256, step = 1,
-					get = function(info) return Ovale.db.profile.apparence.iconShiftY end,
-					set = function(info,value) Ovale.db.profile.apparence.iconShiftY = value; Ovale:UpdateFrame() end
-				},
-				raccourcis =
-				{
-					order = 6,
-					type = "toggle",
-					name = L["Raccourcis clavier"],
-					desc = L["Afficher les raccourcis clavier dans le coin inférieur gauche des icônes"],
-					get = function(info) return Ovale.db.profile.apparence.raccourcis end,
-					set = function(info, value) Ovale.db.profile.apparence.raccourcis = value end
-				},
-				numeric =
-				{
-					order = 7,
-					type = "toggle",
-					name = L["Affichage numérique"],
-					desc = L["Affiche le temps de recharge sous forme numérique"],
-					get = function(info) return Ovale.db.profile.apparence.numeric end,
-					set = function(info, value) Ovale.db.profile.apparence.numeric = value end
-				},
-				verrouille =
-				{
-					order = 8,
-					type = "toggle",
-					name = L["Verrouiller position"],
-					get = function(info) return Ovale.db.profile.apparence.verrouille end,
-					set = function(info, value) Ovale.db.profile.apparence.verrouille = value end
-				},
-				vertical =
-				{
-					order = 9,
-					type = "toggle",
-					name = L["Vertical"],
-					get = function(info) return Ovale.db.profile.apparence.vertical end,
-					set = function(info, value) Ovale.db.profile.apparence.vertical = value; Ovale:UpdateFrame() end
-				},
-				alpha =
-				{
-					order = 9.5,
-					type = "range",
-					name = L["Opacité des icônes"],
-					min = 0, max = 100, step = 5,
-					get = function(info) return Ovale.db.profile.apparence.alpha * 100 end,
-					set = function(info, value) Ovale.db.profile.apparence.alpha = value/100; Ovale.frame.frame:SetAlpha(value/100) end
-				},
-				optionsAlpha =
-				{
-					order = 9.5,
-					type = "range",
-					name = L["Opacité des options"],
-					min = 0, max = 100, step = 5,
-					get = function(info) return Ovale.db.profile.apparence.optionsAlpha * 100 end,
-					set = function(info, value) Ovale.db.profile.apparence.optionsAlpha = value/100; Ovale.frame.content:SetAlpha(value/100) end
-				},
-				predictif =
-				{
-					order = 10,
-					type = "toggle",
-					name = L["Prédictif"],
-					desc = L["Affiche les deux prochains sorts et pas uniquement le suivant"],
-					get = function(info) return Ovale.db.profile.apparence.predictif end,
-					set = function(info, value) Ovale.db.profile.apparence.predictif = value; Ovale:UpdateFrame() end
-				},
-				moving =
-				{
-					order = 11,
-					type = "toggle",
-					name = L["Défilement"],
-					desc = L["Les icônes se déplacent"],
-					get = function(info) return Ovale.db.profile.apparence.moving end,
-					set = function(info, value) Ovale.db.profile.apparence.moving = value; Ovale:UpdateFrame() end
-				},
-				hideEmpty =
-				{
-					order = 12,
-					type = "toggle",
-					name = L["Cacher bouton vide"],
-					get = function(info) return Ovale.db.profile.apparence.hideEmpty end,
-					set = function(info, value) Ovale.db.profile.apparence.hideEmpty = value; Ovale:UpdateFrame() end
-				},
-				targetHostileOnly =
-				{
-					order = 13,
-					type = "toggle",
-					name = L["Cacher si cible amicale ou morte"],
-					get = function(info) return Ovale.db.profile.apparence.targetHostileOnly end,
-					set = function(info, value) Ovale.db.profile.apparence.targetHostileOnly = value; Ovale:UpdateFrame() end
-				},
-				highlightIcon =
-				{
-					order = 14,
-					type = "toggle",
-					name = L["Illuminer l'icône"],
-					desc = L["Illuminer l'icône quand la technique doit être spammée"],
-					get = function(info) return Ovale.db.profile.apparence.highlightIcon end,
-					set = function(info, value) Ovale.db.profile.apparence.highlightIcon = value; Ovale:UpdateFrame() end
-				},
-				clickThru =
-				{
-					order = 15,
-					type = "toggle",
-					name = L["Ignorer les clics souris"],
-					get = function(info) return Ovale.db.profile.apparence.clickThru end,
-					set = function(info, value) Ovale.db.profile.apparence.clickThru = value; Ovale:UpdateFrame() end
-				},
-				latencyCorrection =
-				{
-					order = 16,
-					type = "toggle",
-					name = L["Correction de la latence"],
-					get = function(info) return Ovale.db.profile.apparence.latencyCorrection end,
-					set = function(info, value) Ovale.db.profile.apparence.latencyCorrection = value end
-				},
-				hideVehicule =
-				{
-					order = 17,
-					type = "toggle",
-					name = L["Cacher dans les véhicules"],
-					get = function(info) return Ovale.db.profile.apparence.hideVehicule end,
-					set = function(info, value) Ovale.db.profile.apparence.hideVehicule = value end
-				},
-				flashIcon =
-				{
-					order = 18,
-					type = "toggle",
-					name = L["Illuminer l'icône quand le temps de recharge est écoulé"],
-					get = function(info) return Ovale.db.profile.apparence.flashIcon end,
-					set = function(info, value) Ovale.db.profile.apparence.flashIcon = value; Ovale:UpdateFrame() end
-				},
-				targetText =
-				{
-					order = 19,
-					type = "input",
-					name = L["Caractère de portée"],
-					desc = L["Ce caractère est affiché dans un coin de l'icône pour indiquer si la cible est à portée"],
-					get = function(info) return Ovale.db.profile.apparence.targetText end,
-					set = function(info, value) Ovale.db.profile.apparence.targetText = value; Ovale:UpdateFrame() end
-				}
-			}
-		},
-		code =
-		{
-			name = L["Code"],
-			type = "group",
-			args =
-			{
-				code =
-				{
-					order = 1,
-					type = "input",
-					multiline = 15,
-					name = L["Code"],
-					get = function(info)
-						return string.gsub(Ovale.db.profile.code, "\t", "    ")
-					end,
-					set = function(info,v)
-						Ovale.db.profile.code = v
-						Ovale.needCompile = true
-					end,
-					width = "full"
-				}
-			}
-		},
-		actions =
-		{
-			name = "Actions",
-			type = "group",
-			args =
-			{
-				show =
-				{
-					order = -1,
-					type = "execute",
-					name = L["Afficher la fenêtre"],
-					guiHidden = true,
-					func = function()
-						Ovale.db.profile.display = true
-						Ovale:UpdateVisibility()
-					end
-				},
-				hide =
-				{
-					order = -2,
-					type = "execute",
-					name = L["Cacher la fenêtre"],
-					guiHidden = true,
-					func = function()
-						Ovale.db.profile.display = false
-						Ovale.frame:Hide()
-					end
-				},
-				config  =
-				{
-					name = "Configuration",
-					type = "execute",
-					func = function() Ovale:AfficherConfig() end
-				},
-				code  =
-				{
-					name = "Code",
-					type = "execute",
-					func = function() Ovale:AfficherCode() end
-				},
-				debug =
-				{
-					order = -3,
-					name = "Debug",
-					type = "execute",
-					func = function()
-						for i=1,10 do Ovale:Print(i.."="..UnitPower("player", i)) end
-						Ovale:Print(Ovale.state.eclipse)
-					end
-				},
-				talent =
-				{
-					order = -4,
-					name = "List talent id",
-					type = "execute",
-					func = function()
-						for k,v in pairs(Ovale.talentNameToId) do
-							Ovale:Print(k.."="..v)
-						end
-					end
-				},
-				targetbuff =
-				{
-					order = -5,
-					name = "List target buff and debuff spell id",
-					type = "execute",
-					func = function()
-						Ovale:DebugListAura("target", "HELPFUL")
-						Ovale:DebugListAura("target", "HARMFUL")
-					end
-				},
-				buff =
-				{
-					order = -6,
-					name = "List player buff and debuff spell id",
-					type = "execute",
-					func = function()
-						Ovale:DebugListAura("player", "HELPFUL")
-						Ovale:DebugListAura("player", "HARMFUL")
-					end
-				},
-				glyph =
-				{
-					order = -7,
-					name = "List player glyphs",
-					type = "execute",
-					func = function()
-						for i=1,GetNumGlyphs() do
-							local name, level, enabled, texture, spellId = GetGlyphInfo(i)
-							if spellId then	Ovale:Print(name..": "..spellId.." ("..tostring(enabled)..")") end
-						end
-					end
-				},
-				spell =
-				{
-					order = -8,
-					name = "List player spells",
-					type = "execute",
-					func = function()
-						local book=BOOKTYPE_SPELL
-						while true do
-							local i=1
-							while true do
-								local skillType, spellId = GetSpellBookItemInfo(i, book)
-								if not spellId then
-									break
-								end
-								local spellName = GetSpellBookItemName(i, book)
-								Ovale:Print(spellName..": "..spellId)
-								i = i + 1
-							end
-							if book == BOOKTYPE_SPELL then
-								book = BOOKTYPE_PET
-							else
-								break
-							end
-						end
-					end
-				}
-			}
-		}
-	}
-}
-
-local function nilstring(text)
-	if text == nil then
-		return "nil"
-	else
-		return text
-	end
-end
-
+--<public-static-methods>
 function Ovale:Debug()
-	self:Print(self:DebugNode(self.masterNodes[1]))
+	self:Print(OvaleCompile:DebugNode(self.masterNodes[1]))
 end

 function Ovale:DebugListAura(target, filter)
@@ -684,94 +65,28 @@ function Ovale:DebugListAura(target, filter)
 	end
 end

-function Ovale:OnInitialize()
-	self.AceConfig = LibStub("AceConfig-3.0");
-	self.AceConfigDialog = LibStub("AceConfigDialog-3.0");
-end
-
-function Ovale:GetOtherAura(spellId)
-	if not self.otherAura[spellId] then
-		self.otherAura[spellId] = {}
-	end
-	return self.otherAura[spellId]
-end
-
-function Ovale:WithHaste(temps, hate)
-	if not temps then
-		temps = 0
-	end
-	if (not hate) then
-		return temps
-	elseif (hate == "spell") then
-		return temps/self.spellHaste
-	elseif (hate == "melee") then
-		return temps/self.meleeHaste
-	else
-		return temps
-	end
-end
-
 function Ovale:CompileAll()
-	if self.db.profile.code then
-		self.masterNodes = self:Compile(self.db.profile.code)
-		self.refreshNeeded = true
+	if OvaleOptions:GetProfile().code then
+		self.masterNodes = OvaleCompile:Compile(OvaleOptions:GetProfile().code)
+		self.refreshNeeded.player = true
 		self:UpdateFrame()
 		self.needCompile = false
 	end
 end

-function Ovale:HandleProfileChanges()
-	if self.firstInit then
-		if (self.db.profile.code) then
-			self.needCompile = true
-		end
-	end
-end

 function Ovale:FirstInit()
-	self:RemplirActionIndexes()
-	self:RemplirListeTalents()
-	self:FillSpellList()
-
-	local playerClass, englishClass = UnitClass("player")
-	self.className = englishClass
-	if self.className == "DEATHKNIGHT" then
-		for i=1,6 do
-			self.state.rune[i] = {}
-		end
-	end
-	self.playerGuid = UnitGUID("player")
-
-	self:ChargerDefaut()
-
-	self.frame = LibStub("AceGUI-3.0"):Create("OvaleFrame")
-
-	self.frame:SetPoint("TOPLEFT",UIParent,"BOTTOMLEFT",self.db.profile.left,self.db.profile.top)
-
 	self.firstInit = true
-
-
-	options.args.profile = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db)
-	self.AceConfig:RegisterOptionsTable("Ovale", options.args.code)
-	self.AceConfig:RegisterOptionsTable("Ovale Actions", options.args.actions, "Ovale")
-	self.AceConfig:RegisterOptionsTable("Ovale Profile", options.args.profile)
-	self.AceConfig:RegisterOptionsTable("Ovale Apparence", options.args.apparence)

-	self.AceConfigDialog:AddToBlizOptions("Ovale", "Ovale")
-	self.AceConfigDialog:AddToBlizOptions("Ovale Profile", "Profile", "Ovale")
-	self.AceConfigDialog:AddToBlizOptions("Ovale Apparence", "Apparence", "Ovale")
+	OvaleData:FirstInit()

-	self.db.RegisterCallback( self, "OnNewProfile", "HandleProfileChanges" )
-	self.db.RegisterCallback( self, "OnProfileReset", "HandleProfileChanges" )
-	self.db.RegisterCallback( self, "OnProfileChanged", "HandleProfileChanges" )
-	self.db.RegisterCallback( self, "OnProfileCopied", "HandleProfileChanges" )
+	self.frame = LibStub("AceGUI-3.0"):Create("OvaleFrame")

+	local profile = OvaleOptions:GetProfile()

-	if self.db.profile.code then
-		self.needCompile = true
-	end
+	self.frame:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", profile.left, profile.top)
 	self:UpdateFrame()
-	if not Ovale.db.profile.display then
+	if not profile.display then
 		self.frame:Hide()
 	end
 end
@@ -785,198 +100,32 @@ function Ovale:OnEnable()
 	RegisterAddonMessagePrefix("Ovale")
     self:RegisterEvent("PLAYER_REGEN_ENABLED");
     self:RegisterEvent("PLAYER_REGEN_DISABLED");
-    self:RegisterEvent("SPELLS_CHANGED")
-    self:RegisterEvent("PLAYER_TALENT_UPDATE")
-    self:RegisterEvent("CHARACTER_POINTS_CHANGED")
-    self:RegisterEvent("ACTIONBAR_SLOT_CHANGED");
-    self:RegisterEvent("UPDATE_BINDINGS");
-    self:RegisterEvent("UNIT_AURA");
-    self:RegisterEvent("ACTIONBAR_PAGE_CHANGED")
-    self:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
-	self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
-	self:RegisterEvent("UNIT_SPELLCAST_START")
-	self:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
-	self:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
     self:RegisterEvent("PLAYER_TARGET_CHANGED")
-	self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
 	self:RegisterEvent("CHAT_MSG_ADDON")
 	self:RegisterEvent("GLYPH_UPDATED")
 	self:RegisterEvent("GLYPH_ADDED")
 	self:RegisterEvent("UNIT_INVENTORY_CHANGED")
-
-	self:UNIT_AURA("","player")
-
+
 	self:UpdateVisibility()
 end

 function Ovale:OnDisable()
     -- Called when the addon is disabled
-	self:UnregisterEvent("UNIT_INVENTORY_CHANGED")
-	self:UnregisterEvent("ACTIONBAR_PAGE_CHANGED")
-    self:UnregisterEvent("PLAYER_REGEN_ENABLED")
+	self:UnregisterEvent("PLAYER_REGEN_ENABLED")
     self:UnregisterEvent("PLAYER_REGEN_DISABLED")
-    self:UnregisterEvent("PLAYER_TALENT_UPDATE")
-    self:UnregisterEvent("ACTIONBAR_SLOT_CHANGED")
-    self:UnregisterEvent("SPELLS_CHANGED")
-    self:UnregisterEvent("CHARACTER_POINTS_CHANGED")
-    self:UnregisterEvent("UPDATE_BINDINGS")
-    self:UnregisterEvent("UNIT_AURA")
-    self:UnregisterEvent("UNIT_SPELLCAST_CHANNEL_START")
-	self:UnregisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
-    self:UnregisterEvent("UNIT_SPELLCAST_SUCCEEDED")
-	self:UnregisterEvent("UNIT_SPELLCAST_START")
     self:UnregisterEvent("PLAYER_TARGET_CHANGED")
-    self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
     self:UnregisterEvent("CHAT_MSG_ADDON")
     self:UnregisterEvent("GLYPH_UPDATED")
-	self:UnregisterEvent("UNIT_SPELLCAST_INTERRUPTED")
+    self:UnregisterEvent("GLYPH_ADDED")
+	self:UnregisterEvent("UNIT_INVENTORY_CHANGED")
     self.frame:Hide()
 end

-function Ovale:ACTIONBAR_SLOT_CHANGED(event, slot, unknown)
-	if (slot == 0) then
-		self:RemplirActionIndexes()
-	elseif (slot) then
-	-- on reçoit aussi si c'est une macro avec mouseover à chaque fois que la souris passe sur une cible!
-		self:RemplirActionIndex(tonumber(slot))
-	end
-end
-
-function Ovale:ACTIONBAR_PAGE_CHANGED()
-	-- self:RemplirActionIndexes()
-end
-
-function Ovale:CHARACTER_POINTS_CHANGED()
-	self:RemplirListeTalents()
---	self:Print("CHARACTER_POINTS_CHANGED")
-end
-
-function Ovale:PLAYER_TALENT_UPDATE()
-	self:RemplirListeTalents()
---	self:Print("PLAYER_TALENT_UPDATE")
-end
-
---The user learnt a new spell
-function Ovale:SPELLS_CHANGED()
-	-- self:RemplirActionIndexes()
-	-- self:RemplirListeTalents()
-	self:FillSpellList()
-	self.needCompile = true
-end
-
 function Ovale:UNIT_INVENTORY_CHANGED()
 	if self.compileOnItems then
 		self.needCompile = true
 	else
-		self.refreshNeeded = true
-	end
-end
-
---Called when the user changed his key bindings
-function Ovale:UPDATE_BINDINGS()
-	self:RemplirActionIndexes()
-end
-
---Called for each combat log event
-function Ovale:COMBAT_LOG_EVENT_UNFILTERED(event, ...)
-	local time, event, hideCaster, sourceGUID, sourceName, sourceFlags, sourceRaidFlags, destGUID, destName, destFlags, destRaidFlags = select(1, ...)
-
-	if sourceGUID == self.playerGuid then
-		-- self:Print("event="..event.." source="..nilstring(sourceName).." destName="..nilstring(destName).." " ..GetTime())
-
-		if string.find(event, "SPELL_PERIODIC_DAMAGE")==1 or string.find(event, "SPELL_DAMAGE")==1 then
-			local spellId, spellName, spellSchool, amount = select(12, ...)
-			self.spellDamage[spellId] = amount
-		end
-
-		--Called when a missile reached or missed its target
-		--Update lastSpell accordingly
-		--Do not use SPELL_CAST_SUCCESS because it is sent when the missile has not reached the target
-
-		if
-				string.find(event, "SPELL_AURA_APPLIED")==1
-				or string.find(event, "SPELL_AURA_REFRESH")==1
-				or string.find(event, "SPELL_DAMAGE")==1
-				or string.find(event, "SPELL_MISSED") == 1
-				or string.find(event, "SPELL_CAST_SUCCESS") == 1
-				or string.find(event, "SPELL_CAST_FAILED") == 1 then
-			local spellId, spellName = select(12, ...)
-			for i,v in ipairs(self.lastSpell) do
-				if (v.spellId == spellId or v.auraSpellId == spellId) and v.allowRemove then
-					if not v.channeled and (v.removeOnSuccess or
-								string.find(event, "SPELL_CAST_SUCCESS") ~= 1) then
-						table.remove(self.lastSpell, i)
-						self.refreshNeeded = true
-						--self:Print("LOG_EVENT on supprime "..spellId.." a "..GetTime())
-					end
-					--self:Print(UnitDebuff("target", "Etreinte de l'ombre"))
-					break
-				end
-			end
-		end
-		if self.otherAurasEnabled then
-			--Track debuffs on units that are not the current target
-			if string.find(event, "SPELL_AURA_") == 1 then
-				local spellId, spellName, spellSchool, auraType = select(12, ...)
-				-- auraType == "DEBUFF" and
-				if self.spellInfo[spellId] and self.spellInfo[spellId].duration then
-					local otherDebuff = self:GetOtherAura(spellId)
-					if event == "SPELL_AURA_APPLIED" or event == "SPELL_AURA_REFRESH" then
-						otherDebuff[destGUID] = Ovale.maintenant + self:WithHaste(self.spellInfo[spellId].duration, self.spellInfo[spellId].durationhaste)
-						self.refreshNeeded = true
-					--	self:Print("ajout de "..spellName.." à "..destGUID)
-					elseif event == "SPELL_AURA_REMOVED" then
-						otherDebuff[destGUID] = nil
-						self.refreshNeeded = true
-					--	self:Print("suppression de "..spellName.." de "..destGUID)
-					end
-				end
-			end
-		end
-		--if string.find(event, "SWING")==1 then
-		--	self:Print(select(1, ...))
-		--end
-	end
-
-	if self.numberOfEnemies then
-		if event == "UNIT_DIED" then
-			for k,v in pairs(self.enemies) do
-				if k==destGUID then
-					self.enemies[v] = nil
-					self.numberOfEnemies = self.numberOfEnemies - 1
-					self.refreshNeeded = true
-					--self:Print("enemy die")
-				end
-			end
-		elseif sourceFlags and not self.enemies[sourceGUID] and bit.band(sourceFlags, COMBATLOG_OBJECT_REACTION_HOSTILE)>0
-					and bit.band(sourceFlags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) > 0 and
-				destFlags and bit.band(destFlags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) == 0 then
-			self.enemies[sourceGUID] = true
-			--self:Print("new ennemy source=".. sourceName)
-			self.numberOfEnemies = self.numberOfEnemies + 1
-			self.refreshNeeded = true
-		elseif destGUID and not self.enemies[destGUID] and bit.band(destFlags, COMBATLOG_OBJECT_REACTION_HOSTILE)>0
-					and bit.band(destFlags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) > 0 and
-				sourceFlags and bit.band(sourceFlags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) == 0 then
-			self.enemies[destGUID] = true
-			--self:Print("new ennemy dest=".. destName)
-			self.numberOfEnemies = self.numberOfEnemies + 1
-			self.refreshNeeded = true
-		end
-	end
-
-	if self.otherAurasEnabled then
-		if event == "UNIT_DIED" then
-			--Remove any dead unit from otherAura
-			for k,v in pairs(self.otherAura) do
-				for j,w in pairs(v) do
-					if j==destGUID then
-						v[j] = nil
-						self.refreshNeeded = true
-					end
-				end
-			end
-		end
+		self.refreshNeeded.player = true
 	end
 end

@@ -984,84 +133,10 @@ end
 --Used to update the visibility e.g. if the user chose
 --to hide Ovale if a friendly unit is targeted
 function Ovale:PLAYER_TARGET_CHANGED()
-	self.refreshNeeded = true
+	self.refreshNeeded.target = true
 	self:UpdateVisibility()
 end

---Called when a new aura is added to an unit
---At this time it is not used to keep the aura list (may be used in the future for optimization)
---It is only used to update haste
-function Ovale:UNIT_AURA(event, unit)
-	if unit == "player" or unit == "pet" then
-		local hateBase = GetCombatRatingBonus(18)
-		local hateCommune=0;
-		local hateSorts = 0;
-		local hateCaC = 0;
-		local hateHero = 0
-		local hateClasse = 0
-		local damageMultiplier = 1
-		local i=1;
-
-		if not self.buff[unit] then
-			self.buff[unit] = {}
-		end
-
-		local buff = self.buff[unit]
-
-		while true do
-			local name, rank, iconTexture, count, debuffType, duration, expirationTime, source, stealable, consolidate, spellId =  UnitBuff(unit, i);
-			if (not name) then
-				break
-			end
-			if (not buff[spellId]) then
-				buff[spellId] = {}
-			end
-			buff[spellId].icon = iconTexture
-			buff[spellId].count = count
-			buff[spellId].duration = duration
-			buff[spellId].expirationTime = expirationTime
-			buff[spellId].source = source
-			if (not buff[spellId].present) then
-				buff[spellId].gain = Ovale.maintenant
-			end
-			buff[spellId].lastSeen = Ovale.maintenant
-			buff[spellId].present = true
-
-			if unit == "player" then
-				if self.buffSpellList.spellhaste[spellId] then
-					hateSorts = 5
-				elseif self.buffSpellList.meleehaste[spellId] then
-					hateCaC = 10
-				elseif self.buffSpellList.heroism[spellId] then
-					hateHero = 30
-				elseif self.selfHasteBuff[spellId] then
-					hateClasse = self.selfHasteBuff[spellId]
-				end
-				if self.selfDamageBuff[spellId] then
-					damageMultiplier = damageMultiplier * self.selfDamageBuff[spellId]
-				end
-			end
-			i = i + 1;
-		end
-
-		for k,v in pairs(buff) do
-			if (v.lastSeen ~= Ovale.maintenant) then
-				v.present = false
-			end
-		end
-
-		if unit == "player" then
-			self.spellHaste = 1 + (hateBase + hateCommune + hateSorts + hateHero + hateClasse)/100
-			self.meleeHaste = 1 + (hateBase + hateCommune + hateCaC + hateHero + hateClasse)/100
-			self.damageMultiplier = damageMultiplier
-		end
-
-		self.refreshNeeded = true
---		self.rangedHaste = hateBase + hateCommune + hateHero + hateClasse -- TODO ajouter le bidule du chasseur en spé bête
---		print("spellHaste = "..self.spellHaste)
-	end
-end
-
 --Called when a glyph has been added
 --The script needs to be compiled
 function Ovale:GLYPH_ADDED(event)
@@ -1074,184 +149,6 @@ function Ovale:GLYPH_UPDATED(event)
 	self.needCompile = true
 end

-function Ovale:GetNumberOfEnemies()
-	if not self.numberOfEnemies then
-		self.numberOfEnemies = 0
-	end
-	return self.numberOfEnemies
-end
-
-function Ovale:RemoveSpellFromList(spellId, lineId)
-	for i,v in ipairs(self.lastSpell) do
-		if v.lineId == lineId then
-			table.remove(self.lastSpell, i)
-			--self:Print("RemoveSpellFromList on supprime "..spellId)
-			break
-		end
-	end
-	self.refreshNeeded = true
-end
-
---Called if the player interrupted early his cast
---The spell is removed from the lastSpell table
-function Ovale:UNIT_SPELLCAST_INTERRUPTED(event, unit, name, rank, lineId, spellId)
-	if unit == "player" then
-		--self:Print("UNIT_SPELLCAST_INTERRUPTED "..event.." name="..name.." lineId="..lineId.." spellId="..spellId.. " time="..GetTime())
-		self:RemoveSpellFromList(spellId, lineId)
-	end
-end
-
-function Ovale:UNIT_SPELLCAST_SUCCEEDED(event, unit, name, rank, lineId, spellId)
-	if unit == "player" then
-		--self:Print("UNIT_SPELLCAST_SUCCEEDED "..event.." name="..name.." lineId="..lineId.." spellId="..spellId.. " time="..GetTime())
-		for i,v in ipairs(self.lastSpell) do
-			if v.lineId == lineId then
-				--Already added in UNIT_SPELLCAST_START
-				v.allowRemove = true
-				return
-			end
-		end
-		if not UnitChannelInfo("player") then
-			--A UNIT_SPELLCAST_SUCCEEDED is received when channeling a spell, with a different lineId!
-			local now = GetTime()
-			self:AddSpellToList(spellId, lineId, now, now, false, true)
-		end
-	end
-end
-
-function Ovale:SendScoreToDamageMeter(name, guid, scored, scoreMax)
-	if Recount then
-		local source = Recount.db2.combatants[name]
-		if source then
-			Recount:AddAmount(source,"Ovale",scored)
-			Recount:AddAmount(source,"OvaleMax",scoreMax)
-		end
-	end
-	if Skada then
-		if not guid or not Skada.current or not Skada.total then return end
-		local player = Skada:get_player(Skada.current, guid, nil)
-		if not player then return end
-		if not player.ovale then player.ovale = 0 end
-		if not player.ovaleMax then player.ovaleMax = 0 end
-		player.ovale = player.ovale + scored
-		player.ovaleMax = player.ovaleMax + scoreMax
-		player = Skada:get_player(Skada.total, guid, nil)
-		player.ovale = player.ovale + scored
-		player.ovaleMax = player.ovaleMax + scoreMax
-	end
-end
-
-function Ovale:AddSpellToList(spellId, lineId, startTime, endTime, channeled, allowRemove)
-	local newSpell = {}
-	newSpell.spellId = spellId
-	newSpell.lineId = lineId
-	newSpell.start = startTime
-	newSpell.stop = endTime
-	newSpell.channeled = channeled
-	newSpell.allowRemove = allowRemove
-
-	self.lastSpellAP[spellId] = UnitAttackPower("player")
-	self.lastSpellSP[spellId] = GetSpellBonusDamage(2)
-	self.lastSpellDM[spellId] = self.damageMultiplier
-	self.lastSpell[#self.lastSpell+1] = newSpell
-	--self:Print("on ajoute "..spellId..": ".. newSpell.start.." to "..newSpell.stop.." ("..self.maintenant..")" ..#self.lastSpell)
-
-	if self.spellInfo[spellId] then
-		local si = self.spellInfo[spellId]
-
-		if si.aura then
-			for target, targetInfo in pairs(si.aura) do
-				for filter, filterInfo in pairs(targetInfo) do
-					for auraSpellId, spellData in pairs(filterInfo) do
-						if spellData and spellData ~= "refresh" and spellData > 0 then
-							newSpell.auraSpellId = auraSpellId
-							if target == "player" then
-								newSpell.removeOnSuccess = true
-							end
-							break
-						end
-					end
-				end
-			end
-		end
-
-		--self:Print("spellInfo found")
-		if si and si.buffnocd and UnitBuff("player", GetSpellInfo(si.buffnocd)) then
-			newSpell.nocd = true
-		else
-			newSpell.nocd = false
-		end
-		--Increase or reset the counter that is used by the Counter function
-		if si.resetcounter then
-			self.counter[si.resetcounter] = 0
-			--self:Print("reset counter "..si.resetcounter)
-		end
-		if si.inccounter then
-			local cname = si.inccounter
-			if not self.counter[cname] then
-				self.counter[cname] = 0
-			end
-			self.counter[cname] = self.counter[cname] + 1
-			--self:Print("inc counter "..cname.." to "..self.counter[cname])
-		end
-	else
-		newSpell.removeOnSuccess = true
-	end
-
-	if self.enCombat then
-		--self:Print(tostring(self.scoreSpell[spellId]))
-		if (not self.spellInfo[spellId] or not self.spellInfo[spellId].toggle) and self.scoreSpell[spellId] then
-			--Compute the player score
-			local scored = self.frame:GetScore(spellId)
-			--self:Print("Scored "..scored)
-			if scored~=nil then
-				self.score = self.score + scored
-				self.maxScore = self.maxScore + 1
-				self:SendScoreToDamageMeter(UnitName("player"), UnitGUID("player"), scored, 1)
-			end
-		end
-	end
-	self.refreshNeeded = true
-end
-
-function Ovale:GetCounterValue(id)
-	if self.state.counter[id] then
-		return self.state.counter[id]
-	elseif self.counter[id] then
-		return self.counter[id]
-	else
-		return 0
-	end
-end
-
-function Ovale:UNIT_SPELLCAST_CHANNEL_START(event, unit, name, rank, lineId, spellId)
-	if unit=="player" then
-		--self:Print("UNIT_SPELLCAST_CHANNEL_START "..event.." name="..name.." lineId="..lineId.." spellId="..spellId)
-		local _,_,_,_,startTime, endTime = UnitChannelInfo("player")
-		--self:Print("startTime = " ..startTime.." endTime = "..endTime)
-		self:AddSpellToList(spellId, lineId, startTime/1000, endTime/1000, true, false)
-	end
-end
-
-function Ovale:UNIT_SPELLCAST_CHANNEL_STOP(event, unit, name, rank, lineId, spellId)
-	if unit == "player" then
-		--self:Print("UNIT_SPELLCAST_CHANNEL_STOP "..event.." name="..name.." lineId="..lineId.." spellId="..spellId)
-		self:RemoveSpellFromList(spellId, lineId)
-	end
-end
-
---Called when a spell started its cast
-function Ovale:UNIT_SPELLCAST_START(event, unit, name, rank, lineId, spellId)
-	--self:Print("UNIT_SPELLCAST_START "..event.." name="..name.." lineId="..lineId.." spellId="..spellId)
-	if unit=="player" then
-		local _,_,_,_,startTime,endTime = UnitCastingInfo("player")
-		--local spell, rank, icon, cost, isFunnel, powerType, castTime, minRange, maxRange = GetSpellInfo(spellId)
-		--local startTime =  GetTime()
-		--self:AddSpellToList(spellId, lineId, startTime, startTime + castTime/1000)
-		self:AddSpellToList(spellId, lineId, startTime/1000, endTime/1000, false, false)
-	end
-end
-
 function Ovale:CHAT_MSG_ADDON(event, prefix, msg, type, author)
 	if prefix ~= "Ovale" then return end
 	if type ~= "RAID" and type~= "PARTY" then return end
@@ -1275,127 +172,29 @@ function Ovale:PLAYER_REGEN_DISABLED()
 	self.enCombat = true
 	self.score = 0
 	self.maxScore = 0
-	self.combatStartTime = self.maintenant
-	if self.numberOfEnemies then
-		self.numberOfEnemies = 0
-		self.enemies = {}
-	end
+	self.combatStartTime = OvaleState.maintenant
 	self:UpdateVisibility()
 end

-function Ovale:ChercherShortcut(id)
--- ACTIONBUTTON1..12 => principale (1..12, 13..24, 73..108)
--- MULTIACTIONBAR1BUTTON1..12 => bas gauche (61..72)
--- MULTIACTIONBAR2BUTTON1..12 => bas droite (49..60)
--- MULTIACTIONBAR3BUTTON1..12 => haut droit (25..36)
--- MULTIACTIONBAR4BUTTON1..12 => haut gauche (37..48)
-	local name;
-	if (id<=24 or id>72) then
-		name = "ACTIONBUTTON"..(((id-1)%12)+1);
-	elseif (id<=36) then
-		name = "MULTIACTIONBAR3BUTTON"..(id-24);
-	elseif (id<=48) then
-		name = "MULTIACTIONBAR4BUTTON"..(id-36);
-	elseif (id<=60) then
-		name = "MULTIACTIONBAR2BUTTON"..(id-48);
-	else
-		name = "MULTIACTIONBAR1BUTTON"..(id-60);
-	end
-	local key = GetBindingKey(name);
---[[	if (not key) then
-		DEFAULT_CHAT_FRAME:AddMessage(id.."=>"..name.." introuvable")
-	else
-		DEFAULT_CHAT_FRAME:AddMessage(id.."=>"..name.."="..key)
-	end]]
-	return key;
-end
-
-function Ovale:GetSpellInfoOrNil(spell)
-	if (spell) then
-		return GetSpellInfo(spell)
-	else
-		return nil
-	end
-end
-
-function Ovale:RemplirActionIndex(i)
-	self.shortCut[i] = self:ChercherShortcut(i)
-	local actionText = GetActionText(i)
-	if actionText then
-		self.actionMacro[actionText] = i
-	else
-		local type, spellId = GetActionInfo(i);
-		if (type=="spell") then
-			self.actionSort[spellId] = i
-		elseif (type =="item") then
-			self.actionObjet[spellId] = i
-		end
-	end
-end
-
-function Ovale:RemplirActionIndexes()
-	self.actionSort = {}
-	self.actionMacro = {}
-	self.actionObjet = {}
-	self.shortCut = {}
-	for i=1,120 do
-		self:RemplirActionIndex(i)
-	end
-end
-
-function Ovale:FillSpellList()
-	self.spellList = {}
-	local book=BOOKTYPE_SPELL
-	while true do
-		local i=1
-		while true do
-			local skillType, spellId = GetSpellBookItemInfo(i, book)
-			if not spellId then
-				break
-			end
-			if skillType~="FUTURESPELL" then
-				local spellName = GetSpellBookItemName(i, book)
-				self.spellList[spellId] = spellName
-			end
-			i = i + 1
-		end
-		if book==BOOKTYPE_SPELL then
-			book = BOOKTYPE_PET
-		else
-			break
-		end
-	end
-end
-
-function Ovale:RemplirListeTalents()
-	local numTabs = GetNumTalentTabs();
-	for t=1, numTabs do
-		local numTalents = GetNumTalents(t);
-		for i=1, numTalents do
-			local nameTalent, icon, tier, column, currRank, maxRank = GetTalentInfo(t,i);
-			local link = GetTalentLink(t,i)
-			if link then
-				local a, b, talentId = string.find(link, "talent:(%d+)");
-				talentId = tonumber(talentId)
-				self.talentIdToName[talentId] = nameTalent
-				self.talentNameToId[nameTalent] = talentId
-				self.pointsTalent[talentId] = currRank
-				self.listeTalentsRemplie = true
-				self.needCompile = true
-			end
+function Ovale:SendScoreToDamageMeter(name, guid, scored, scoreMax)
+	if Recount then
+		local source = Recount.db2.combatants[name]
+		if source then
+			Recount:AddAmount(source,"Ovale",scored)
+			Recount:AddAmount(source,"OvaleMax",scoreMax)
 		end
 	end
-end
-
-function Ovale:AddRune(time, type, value)
-	if value<0 then
-		for i=1,6 do
-			if (self.state.rune[i].type == type or self.state.rune[i].type==4)and self.state.rune[i].cd<=time then
-				self.state.rune[i].cd = time + 10
-			end
-		end
-	else
-
+	if Skada then
+		if not guid or not Skada.current or not Skada.total then return end
+		local player = Skada:get_player(Skada.current, guid, nil)
+		if not player then return end
+		if not player.ovale then player.ovale = 0 end
+		if not player.ovaleMax then player.ovaleMax = 0 end
+		player.ovale = player.ovale + scored
+		player.ovaleMax = player.ovaleMax + scoreMax
+		player = Skada:get_player(Skada.total, guid, nil)
+		player.ovale = player.ovale + scored
+		player.ovaleMax = player.ovaleMax + scoreMax
 	end
 end

@@ -1405,1066 +204,6 @@ function Ovale:Log(text)
 	end
 end

-function Ovale:GetAura(target, filter, spellId, forceduration)
-	if not self.aura[target] then
-		self.aura[target] = {}
-	end
-	if not self.aura[target][filter] then
-		self.aura[target][filter] = {}
-	end
-	if not self.aura[target][filter][spellId] then
-		self.aura[target][filter][spellId] = {}
-	end
-	local myAura = self.aura[target][filter][spellId]
-	if myAura.serial == Ovale.serial then
-		return myAura
-	end
-
-	myAura.mine = false
-	myAura.start = nil
-	myAura.ending = nil
-	myAura.stacks = 0
-	myAura.serial = Ovale.serial
-
-	local i = 1
-
-	while (true) do
-		local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, thisSpellId =  UnitAura(target, i, filter);
-		if not name then
-			break
-		end
-		if (unitCaster=="player" or not myAura.mine) and (spellId == thisSpellId or spellId == debuffType) then
-			myAura.mine = (unitCaster == "player")
-			myAura.start = expirationTime - duration
-
-			if expirationTime>0 then
-				myAura.ending = expirationTime
-			else
-				myAura.ending = nil
-			end
-			if count and count>0 then
-				myAura.stacks = count
-			else
-				myAura.stacks = 1
-			end
-			if myAura.mine then
-				break
-			end
-		end
-		i = i + 1;
-	end
-	return myAura
-end
-
-function Ovale:GetCD(spellId)
-	if not spellId then
-		return nil
-	end
-
-	if self.spellInfo[spellId] and self.spellInfo[spellId].cd then
-		local cdname
-		if self.spellInfo[spellId].sharedcd then
-			cdname = self.spellInfo[spellId].sharedcd
-		else
-			cdname = spellId
-		end
-		if not self.state.cd[cdname] then
-			self.state.cd[cdname] = {}
-		end
-		return self.state.cd[cdname]
-	else
-		return nil
-	end
-end
-
-function Ovale:AddEclipse(endCast, spellId)
-	local newAura = self:GetAura("player", "HELPFUL", spellId)
-	newAura.start = endCast + 0.5
-	newAura.stacks = 1
-	newAura.ending = nil
-end
-
--- Cast a spell in the simulator
--- spellId : the spell id
--- startCast : temps du cast
--- endCast : fin du cast
--- nextCast : temps auquel le prochain sort peut être lancé (>=endCast, avec le GCD)
--- nocd : le sort ne déclenche pas son cooldown
-function Ovale:AddSpellToStack(spellId, startCast, endCast, nextCast, nocd)
-	if not spellId then
-		return
-	end
-
-	local newSpellInfo = self.spellInfo[spellId]
-
-	--On enregistre les infos sur le sort en cours
-	self.attenteFinCast = nextCast
-	self.currentSpellId = spellId
-	self.startCast = startCast
-	self.endCast = endCast
-	--Temps actuel de la simulation : un peu après le dernier cast (ou maintenant si dans le passé)
-	if startCast>=self.maintenant then
-		self.currentTime = startCast+0.1
-	else
-		self.currentTime = self.maintenant
-	end
-
-	if Ovale.trace then
-		Ovale:Print("add spell "..spellId.." at "..startCast.." currentTime = "..self.currentTime.. " nextCast="..self.attenteFinCast .. " endCast="..endCast)
-	end
-
-	--Effet du sort au moment du début du cast
-	--(donc si cast déjà commencé, on n'en tient pas compte)
-	if startCast >= self.maintenant then
-		if newSpellInfo then
-			if newSpellInfo.inccounter then
-				local id = newSpellInfo.inccounter
-				self.state.counter[id] = self:GetCounterValue(id) + 1
-			end
-
-			if newSpellInfo.resetcounter then
-				self.state.counter[newSpellInfo.resetcounter] = 0
-			end
-		end
-	end
-
-	--Effet du sort au moment où il est lancé
-	--(donc si il est déjà lancé, on n'en tient pas compte)
-	if endCast >= self.maintenant then
-		--Mana
-		local _, _, _, cost = GetSpellInfo(spellId)
-		if cost then
-			self.state.mana = self.state.mana - cost
-		end
-
-		if newSpellInfo then
-
-			if newSpellInfo.mana then
-				self.state.mana = self.state.mana - newSpellInfo.mana
-			end
-
-			--Points de combo
-			if newSpellInfo.combo then
-				self.state.combo = self.state.combo + newSpellInfo.combo
-				if self.state.combo<0 then
-					self.state.combo = 0
-				end
-			end
-			--Runes
-			if newSpellInfo.frost then
-				self:AddRune(startCast, 3, newSpellInfo.frost)
-			end
-			if newSpellInfo.death then
-				self:AddRune(startCast, 4, newSpellInfo.death)
-			end
-			if newSpellInfo.blood then
-				self:AddRune(startCast, 1, newSpellInfo.blood)
-			end
-			if newSpellInfo.unholy then
-				self:AddRune(startCast, 2, newSpellInfo.unholy)
-			end
-			if newSpellInfo.holy then
-				self.state.holy = self.state.holy + newSpellInfo.holy
-				if self.state.holy < 0 then
-					self.state.holy = 0
-				elseif self.state.holy > 3 then
-					self.state.holy = 3
-				end
-			end
-			if newSpellInfo.shard then
-				self.state.shard = self.state.shard + newSpellInfo.shard
-				if self.state.shard < 0 then
-					self.state.shard = 0
-				elseif self.state.shard > 3 then
-					self.state.shard = 3
-				end
-			end
-		end
-	end
-
-	-- Effets du sort au moment où il atteint sa cible
-	if newSpellInfo then
-		-- Cooldown du sort
-		local cd = self:GetCD(spellId)
-		if cd then
-			cd.start = startCast
-			cd.duration = newSpellInfo.cd
-			--Pas de cooldown
-			if nocd then
-				cd.duration = 0
-			end
-			--On vérifie si le buff "buffnocd" est présent, auquel cas le CD du sort n'est pas déclenché
-			if newSpellInfo.buffnocd and not nocd then
-				local buffAura = self:GetAura("player", "HELPFUL", newSpellInfo.buffnocd)
-				if self.traceAura then
-					if buffAura then
-						self:Print("buffAura stacks = "..buffAura.stacks.." start="..nilstring(buffAura.start).." ending = "..nilstring(buffAura.ending))
-						self:Print("startCast = "..startCast)
-					else
-						self:Print("buffAura = nil")
-					end
-					self.traceAura = false
-				end
-				if buffAura and buffAura.stacks>0 and buffAura.start and buffAura.start<=startCast and (not buffAura.ending or buffAura.ending>startCast) then
-					cd.duration = 0
-				end
-			end
-			if newSpellInfo.targetlifenocd and not nocd then
-				if UnitHealth("target")/UnitHealthMax("target")*100<newSpellInfo.targetlifenocd then
-					cd.duration = 0
-				end
-			end
-			cd.enable = 1
-			if newSpellInfo.toggle then
-				cd.toggled = 1
-			end
-		end
-
-		if newSpellInfo.eclipse then
-			self.state.eclipse = self.state.eclipse + newSpellInfo.eclipse
-			if self.state.eclipse < -100 then
-				self.state.eclipse = -100
-				self:AddEclipse(endCast, 48518)
-			elseif self.state.eclipse > 100 then
-				self.state.eclipse = 100
-				self:AddEclipse(endCast, 48517)
-			end
-		end
-		if newSpellInfo.starsurge then
-			local buffAura = self:GetAura("player", "HELPFUL", 48517) --Solar
-			if buffAura.stacks>0 then
-				self:Log("starsurge with solar buff = " .. (- newSpellInfo.starsurge))
-				self.state.eclipse = self.state.eclipse - newSpellInfo.starsurge
-			else
-				buffAura = self:GetAura("player", "HELPFUL", 48518) --Lunar
-				if buffAura.stacks>0 then
-					self:Log("starsurge with lunar buff = " .. newSpellInfo.starsurge)
-					self.state.eclipse = self.state.eclipse + newSpellInfo.starsurge
-				elseif self.state.eclipse < 0 then
-					self:Log("starsurge with eclipse < 0 = " .. (- newSpellInfo.starsurge))
-					self.state.eclipse = self.state.eclipse - newSpellInfo.starsurge
-				else
-					self:Log("starsurge with eclipse > 0 = " .. newSpellInfo.starsurge)
-					self.state.eclipse = self.state.eclipse + newSpellInfo.starsurge
-				end
-			end
-			if self.state.eclipse < -100 then
-				self.state.eclipse = -100
-				self:AddEclipse(endCast, 48518)
-			elseif self.state.eclipse > 100 then
-				self.state.eclipse = 100
-				self:AddEclipse(endCast, 48517)
-			end
-		end
-
-		--Auras causés par le sort
-		if newSpellInfo.aura then
-			for target, targetInfo in pairs(newSpellInfo.aura) do
-				for filter, filterInfo in pairs(targetInfo) do
-					for auraSpellId, spellData in pairs(filterInfo) do
-						local newAura = self:GetAura(target, filter, auraSpellId)
-						newAura.mine = true
-						local duration = spellData
-						local stacks = duration
-						--Optionnellement, on va regarder la durée du buff
-						if auraSpellId and self.spellInfo[auraSpellId] and self.spellInfo[auraSpellId].duration then
-							duration = self.spellInfo[auraSpellId].duration
-						elseif stacks~="refresh" and stacks > 0 then
-							stacks = 1
-						end
-						if stacks=="refresh" then
-							if newAura.ending then
-								newAura.ending = endCast + duration
-							end
-						elseif stacks<0 and newAura.ending then
-							--Buff are immediatly removed when the cast ended, do not need to do it again
-							if filter~="HELPFUL" or target~="player" or endCast>=Ovale.maintenant then
-								newAura.stacks = newAura.stacks + stacks
-								if Ovale.trace then
-									self:Print("removing one stack of "..auraSpellId.." because of ".. spellId.." to ".. newAura.stacks)
-								end
-								--Plus de stacks, on supprime l'aura
-								if newAura.stacks<=0 then
-									self:Log("Aura is completly removed")
-									newAura.stacks = 0
-									newAura.ending = 0
-								end
-							end
-						elseif newAura.ending and newAura.ending >= endCast then
-							newAura.ending = endCast + duration
-							newAura.stacks = newAura.stacks + stacks
-						else
-							newAura.start = endCast
-							newAura.ending = endCast + duration
-							newAura.stacks = stacks
-						end
-						if Ovale.trace then
-							if auraSpellId then
-								self:Print(spellId.." adding "..stacks.." aura "..auraSpellId.." to "..target.." "..filter.." "..newAura.start..","..newAura.ending)
-							else
-								self:Print("adding nil aura")
-							end
-						end
-					end
-				end
-			end
-		end
-	end
-end
-
-function Ovale:InitAllActions()
-	self.maintenant = GetTime();
-	self.gcd = self:GetGCD()
-end
-
-function Ovale:InitCalculerMeilleureAction()
-	self.serial = self.serial + 1
-	self.currentTime = Ovale.maintenant
-	self.currentSpellId = nil
-	self.attenteFinCast = Ovale.maintenant
-	self.state.combo = GetComboPoints("player")
-	self.state.mana = UnitPower("player")
-	self.state.shard = UnitPower("player", 7)
-	self.state.eclipse = UnitPower("player", 8)
-	self.state.holy = UnitPower("player", 9)
-	if self.className == "DEATHKNIGHT" then
-		for i=1,6 do
-			self.state.rune[i].type = GetRuneType(i)
-			local start, duration, runeReady = GetRuneCooldown(i)
-			if runeReady then
-				self.state.rune[i].cd = start
-			else
-				self.state.rune[i].cd = duration + start
-				if self.state.rune[i].cd<0 then
-					self.state.rune[i].cd = 0
-				end
-			end
-		end
-	end
-	for k,v in pairs(self.state.cd) do
-		v.start = nil
-		v.duration = nil
-		v.enable = 0
-		v.toggled = nil
-	end
-
-	for k,v in pairs(self.state.counter) do
-		self.state.counter[k] = self.counter[k]
-	end
-
-	for i,v in ipairs(self.lastSpell) do
-		if not self.spellInfo[v.spellId] or not self.spellInfo[v.spellId].toggle then
-			--[[local spell, rank, displayName, icon, startTime, endTime, isTradeSkill = UnitCastingInfo("player")
-			if spell and spell == v.name and startTime/1000 - v.start < 0.5 and v.stop~=endTime/1000 then
-				print("ancien = "..v.stop)
-				v.stop = endTime/1000
-				print("changement de v.stop en "..v.stop.." "..v.start)
-			end]]
-			self:Log("self.maintenant = " ..self.maintenant.." spellId="..v.spellId.." v.stop="..v.stop)
-			if self.maintenant - v.stop < 5 then
-				self:AddSpellToStack(v.spellId, v.start, v.stop, v.stop, v.nocd)
-			else
-				--self:Print("Removing obsolete "..v.spellId)
-				table.remove(self.lastSpell, i)
-			end
-		end
-	end
-end
-
-local function printTime(temps)
-	if (temps == nil) then
-		Ovale:Print("> nil")
-	else
-		Ovale:Print("> "..temps)
-	end
-end
-
-function Ovale:GetGCD(spellId)
-	if spellId and self.spellInfo[spellId] then
-		if self.spellInfo[spellId].haste == "spell" then
-			local cd = self.spellInfo[spellId].gcd
-			if not cd then
-				cd = 1.5
-			end
-			cd = cd / self.spellHaste
-			if (cd<1) then
-				cd = 1
-			end
-			return cd
-		elseif self.spellInfo[spellId].gcd then
-			return self.spellInfo[spellId].gcd
-		end
-	end
-
-	-- Default value
-	if self.className == "ROGUE" or (self.className == "DRUID" and GetShapeshiftForm(true) == 3) then
-		return 1.0
-	elseif self.className == "MAGE" or self.className == "WARLOCK" or self.className == "PRIEST" or
-			(self.className == "DRUID" and GetShapeshiftForm(true) ~= 1) then
-		local cd = 1.5 / self.spellHaste
-		if (cd<1) then
-			cd = 1
-		end
-		return cd
-	else
-		return 1.5
-	end
-end
-
---Compute the spell Cooldown
-function Ovale:GetComputedSpellCD(spellId)
-	local actionCooldownStart, actionCooldownDuration, actionEnable
-	local cd = self:GetCD(spellId)
-	if cd and cd.start then
-		actionCooldownStart = cd.start
-		actionCooldownDuration = cd.duration
-		actionEnable = cd.enable
-	else
-		actionCooldownStart, actionCooldownDuration, actionEnable = GetSpellCooldown(spellId)
-		-- Les chevaliers de la mort ont des infos fausses sur le CD quand ils n'ont plus les runes
-		-- On force à 1,5s ou 1s en présence impie
-		if self.className=="DEATHKNIGHT" and actionCooldownDuration==10 and
-				(not self.spellInfo[spellId] or self.spellInfo[spellId].cd~=10) then
-			local impie = GetSpellInfo(48265)
-			if impie and UnitBuff("player", impie) then
-				actionCooldownDuration=1
-			else
-				actionCooldownDuration=1.5
-			end
-		end
-		if self.spellInfo[spellId] and self.spellInfo[spellId].forcecd then
-			actionCooldownStart, actionCooldownDuration = GetSpellCooldown(self.spellInfo[spellId].forcecd)
-		end
-	end
-	return actionCooldownStart, actionCooldownDuration, actionEnable
-end
-
-function Ovale:GetActionInfo(element)
-	if not element then
-		return nil
-	end
-
-	local spellId = element.params[1]
-	local action
-	local actionTexture, actionInRange, actionCooldownStart, actionCooldownDuration,
-		actionUsable, actionShortcut, actionIsCurrent, actionEnable
-
-	local target = element.params.target
-	if (not target) then
-		target = "target"
-	end
-
-	if (element.func == "Spell" ) then
-		if not self.spellList[spellId] and not self.actionSort[spellId] then
-			self:Log("Spell "..spellId.." not learnt")
-			return nil
-		end
-
-		--Get spell info
-		action = self.actionSort[spellId]
-		actionCooldownStart, actionCooldownDuration, actionEnable = self:GetComputedSpellCD(spellId)
-
-		--if (not action or not GetActionTexture(action)) then
-			spellName = self.spellList[spellId]
-			if not spellName then
-				spellName = GetSpellInfo(spellId)
-			end
-			actionTexture = GetSpellTexture(spellId)
-			actionInRange = IsSpellInRange(spellName, target)
-			actionUsable = IsUsableSpell(spellId)
-			actionShortcut = nil
-		--end
-	elseif (element.func=="Macro") then
-		action = self.actionMacro[element.params[1]]
-		if action then
-			actionTexture = GetActionTexture(action)
-			actionInRange = IsActionInRange(action, target)
-			actionCooldownStart, actionCooldownDuration, actionEnable = GetActionCooldown(action)
-			actionUsable = IsUsableAction(action)
-			actionShortcut = self.shortCut[action]
-			actionIsCurrent = IsCurrentAction(action)
-		else
-			Ovale:Log("Unknown macro "..element.params[1])
-		end
-	elseif (element.func=="Item") then
-		local itemId
-		if (type(element.params[1]) == "number") then
-			itemId = element.params[1]
-		else
-			local _,_,id = string.find(GetInventoryItemLink("player",GetInventorySlotInfo(element.params[1])) or "","item:(%d+):%d+:%d+:%d+")
-			if not id then
-				return nil
-			end
-			itemId = tonumber(id)
-		end
-
-		if (Ovale.trace) then
-			self:Print("Item "..nilstring(itemId))
-		end
-
-		local spellName = GetItemSpell(itemId)
-		actionUsable = (spellName~=nil)
-
-		action = self.actionObjet[itemId]
-		--if (not action or not GetActionTexture(action)) then
-			actionTexture = GetItemIcon(itemId)
-			actionInRange = IsItemInRange(itemId, target)
-			actionCooldownStart, actionCooldownDuration, actionEnable = GetItemCooldown(itemId)
-			actionShortcut = nil
-			actionIsCurrent = nil
-		--end
-	elseif element.func=="Texture" then
-		actionTexture = "Interface\\Icons\\"..element.params[1]
-		actionCooldownStart = Ovale.maintenant
-		actionCooldownDuration = 0
-		actionEnable = 1
-		actionUsable = true
-	end
-
-	if action then
-		if actionUsable == nil then
-			actionUsable = IsUsableAction(action)
-		end
-		actionShortcut = self.shortCut[action]
-		actionIsCurrent = IsCurrentAction(action)
-	end
-
-	local cd = self:GetCD(spellId)
-	if cd and cd.toggle then
-		actionIsCurrent = 1
-	end
-
-	return actionTexture, actionInRange, actionCooldownStart, actionCooldownDuration,
-					actionUsable, actionShortcut, actionIsCurrent, actionEnable, spellId, target, element.params.nored
-end
-
-local function subTime(time1, duration)
-	if not time1 then
-		return nil
-	else
-		return time1 - duration
-	end
-end
-
-local function addTime(time1, duration)
-	if not time1 then
-		return nil
-	else
-		return time1 + duration
-	end
-end
-
-local function isBeforeEqual(time1, time2)
-	return time1 and (not time2 or time1<=time2)
-end
-
-local function isBefore(time1, time2)
-	return time1 and (not time2 or time1<time2)
-end
-
-local function isAfterEqual(time1, time2)
-	return not time1 or (time2 and time1>=time2)
-end
-
-local function isAfter(time1, time2)
-	return not time1 or (time2 and time1>time2)
-end
-
-function Ovale:CalculerMeilleureAction(element)
-	if (self.bug and not self.trace) then
-		return nil
-	end
-
-	if (not element) then
-		return nil
-	end
-
-	--TODO: créer un objet par type au lieu de ce if else if tout moche
-	if (element.type=="function")then
-		if (element.func == "Spell" or element.func=="Macro" or element.func=="Item" or element.func=="Texture") then
-			local action
-			local actionTexture, actionInRange, actionCooldownStart, actionCooldownDuration,
-				actionUsable, actionShortcut, actionIsCurrent, actionEnable, spellId = self:GetActionInfo(element)
-
-			if not actionTexture then
-				if (Ovale.trace) then
-					self:Print("Action "..element.params[1].." not found")
-				end
-				return nil
-			end
-			if element.params.usable==1 and not actionUsable then
-				if (Ovale.trace) then
-					self:Print("Action "..element.params[1].." not usable")
-				end
-				return nil
-			end
-			if spellId and self.spellInfo[spellId] and self.spellInfo[spellId].casttime then
-				element.castTime = self.spellInfo[spellId].casttime
-			elseif spellId then
-				local spell, rank, icon, cost, isFunnel, powerType, castTime = GetSpellInfo(spellId)
-				if castTime then
-					element.castTime = castTime/1000
-				else
-					element.castTime = nil
-				end
-			else
-				element.castTime = 0
-			end
-			--TODO: not useful anymore?
-			if (spellId and self.spellInfo[spellId] and self.spellInfo[spellId].toggle and actionIsCurrent) then
-				if (Ovale.trace) then
-					self:Print("Action "..element.params[1].." is current action")
-				end
-				return nil
-			end
-			if actionEnable and actionEnable>0 then
-				local restant
-				if (not actionCooldownDuration or actionCooldownStart==0) then
-					restant = self.currentTime
-				else
-					restant = actionCooldownDuration + actionCooldownStart
-				end
-				self:Log("restant = "..restant.." attenteFinCast="..nilstring(self.attenteFinCast))
-				if restant<self.attenteFinCast then
-					if -- spellName==self.currentSpellName or
-						not self.spellInfo[self.currentSpellId] or
-							not self.spellInfo[self.currentSpellId].canStopChannelling then
-						restant = self.attenteFinCast
-					else
-						--TODO: pas exact, parce que si ce sort est reporté de par exemple 0,5s par un debuff
-						--ça tombera entre deux ticks
-						local ticks = floor(self.spellHaste * self.spellInfo[self.currentSpellId].canStopChannelling + 0.5)
-						local tickLength = (self.attenteFinCast - self.startCast) / ticks
-						local tickTime = self.startCast + tickLength
-						if (Ovale.trace) then
-							self:Print(spellName.." restant = " .. restant)
-							self:Print("ticks = "..ticks.." tickLength="..tickLength.." tickTime="..tickTime)
-						end
-						for i=1,ticks do
-							if restant<=tickTime then
-								restant = tickTime
-								break
-							end
-							tickTime = tickTime + tickLength
-						end
-						if (Ovale.trace) then
-							self:Print(spellId.." restant = " .. restant)
-						end
-					end
-				end
-				if (Ovale.trace) then
-					self:Print("Action "..element.params[1].." remains "..restant)
-				end
-				local retourPriorite = element.params.priority
-				if (not retourPriorite) then
-					retourPriorite = 3
-				end
-				return restant, nil, retourPriorite, element
-			else
-				if (Ovale.trace) then
-					self:Print("Action "..element.params[1].." not enabled")
-				end
-			end
-		else
-			local classe = Ovale.conditions[element.func]
-			if (not classe) then
-				self.bug = true
-				self:Print("Function "..element.func.." not found")
-				return nil
-			end
-			local start, ending, rate = classe(element.params)
-
-			if (Ovale.trace) then
-				local parameterList = element.func.."("
-				for k,v in pairs(element.params) do
-					parameterList = parameterList..k.."="..v..","
-				end
-				self:Print("Function "..parameterList..") returned "..nilstring(start)..","..nilstring(ending)..","..nilstring(rate))
-			end
-
-			if rate then
-				if not element.result then
-					element.result = { type = "value" }
-				end
-				local result = element.result
-				result.value = start
-				result.origin = ending
-				result.rate = rate
-				return 0, nil, 3, result
-			else
-				return start, ending
-			end
-		end
-	elseif element.type == "time" then
-		return element.value
-	elseif element.type == "value" then
-		Ovale:Log("value " .. element.value)
-		return 0, nil, 3, element
-	elseif element.type == "after" then
-		local timeA = Ovale:CalculerMeilleureAction(element.time)
-		local startA, endA = Ovale:CalculerMeilleureAction(element.a)
-		return addTime(startA, timeA), addTime(endA, timeA)
-	elseif (element.type == "before") then
-		if (Ovale.trace) then
-			--self:Print(nilstring(element.time).."s before ["..element.nodeId.."]")
-		end
-		local timeA = Ovale:CalculerMeilleureAction(element.time)
-		local startA, endA = Ovale:CalculerMeilleureAction(element.a)
-		return addTime(startA, -timeA), addTime(endA, -timeA)
-	elseif (element.type == "between") then
-		self:Log("between")
-		local tempsA = Ovale:CalculerMeilleureAction(element.a)
-		local tempsB = Ovale:CalculerMeilleureAction(element.b)
-		if tempsB==nil and tempsA==nil then
-			Ovale:Log("diff returns 0 because the two nodes are nil")
-			return 0
-		end
-
-		if tempsA==nil or tempsB==nil then
-			if Ovale.trace then Ovale:Print(element.type.." return nil") end
-			return nil
-		end
-		local diff
-		if tempsA>tempsB then
-			diff = tempsA - tempsB
-		else
-			diff = tempsB - tempsA
-		end
-		Ovale:Log("diff returns "..diff)
-		return diff
-	elseif element.type == "fromuntil" then
-		self:Log("fromuntil")
-		local tempsA = Ovale:CalculerMeilleureAction(element.a)
-		if (tempsA==nil) then
-			if Ovale.trace then Ovale:Print(element.type.." return nil") end
-			return nil
-		end
-		local tempsB = Ovale:CalculerMeilleureAction(element.b)
-		if (tempsB==nil) then
-			if Ovale.trace then Ovale:Print(element.type.." return nil") end
-			return nil
-		end
-		Ovale:Log("fromuntil returns "..(tempsB - tempsA))
-		return tempsB - tempsA
-	elseif element.type == "compare" then
-		self:Log("compare "..element.comparison)
-		local tempsA = Ovale:CalculerMeilleureAction(element.a)
-		local timeB = Ovale:CalculerMeilleureAction(element.time)
-		self:Log(nilstring(tempsA).." "..element.comparison.." "..nilstring(timeB))
-		if element.comparison == "more" and (not tempsA or tempsA>timeB) then
-			if Ovale.trace then Ovale:Print(element.type.." return 0") end
-			return 0
-		elseif element.comparison == "less" and tempsA and tempsA<timeB then
-			if Ovale.trace then Ovale:Print(element.type.." return 0") end
-			return 0
-		elseif element.comparison == "at most" and tempsA and tempsA<=timeB then
-			if Ovale.trace then Ovale:Print(element.type.." return 0") end
-			return 0
-		elseif element.comparison == "at least" and (not tempsA or tempsA>=timeB) then
-			if Ovale.trace then Ovale:Print(element.type.." return 0") end
-			return 0
-		end
-		return nil
-	elseif element.type == "and" or element.type == "if" then
-		if (Ovale.trace) then
-			self:Print(element.type.." ["..element.nodeId.."]")
-		end
-		local startA, endA = Ovale:CalculerMeilleureAction(element.a)
-		if (startA==nil) then
-			if Ovale.trace then Ovale:Print(element.type.." return nil  ["..element.nodeId.."]") end
-			return nil
-		end
-		if startA == endA then
-			if Ovale.trace then Ovale:Print(element.type.." return startA=endA  ["..element.nodeId.."]") end
-			return nil
-		end
-		local startB, endB, prioriteB, elementB = Ovale:CalculerMeilleureAction(element.b)
-		if isAfter(startB, endA) or isAfter(startA, endB) then
-			if Ovale.trace then Ovale:Print(element.type.." return nil ["..element.nodeId.."]") end
-			return nil
-		end
-		if isBefore(startB, startA) then
-			startB = startA
-		end
-		if isAfter(endB, endA) then
-			endB = endA
-		end
-		if Ovale.trace then
-			Ovale:Print(element.type.." return "..nilstring(startB)..","..nilstring(endB).." ["..element.nodeId.."]")
-		end
-		return startB, endB, prioriteB, elementB
-	elseif element.type == "unless" then
-		if Ovale.trace then
-			self:Print(element.type)
-		end
-		local startA, endA = Ovale:CalculerMeilleureAction(element.a)
-		local startB, endB, prioriteB, elementB = Ovale:CalculerMeilleureAction(element.b)
-
-		if isBeforeEqual(startA, startB) and isAfterEqual(endA, endB) then
-			if Ovale.trace then Ovale:Print(element.type.." return nil") end
-			return nil
-		end
-
-		if isAfterEqual(startA, startB) and isBefore(endA, endB) then
-			if Ovale.trace then Ovale:Print(element.type.." return "..nilstring(endA)..","..nilstring(endB)) end
-			return endA, endB, prioriteB, elementB
-		end
-
-		if isAfter(startA, startB) and isBefore(startA, endB) then
-			endB = startA
-		end
-
-		if isAfter(endA, startB) and isBefore(endA, endB) then
-			startB = endA
-		end
-
-		if Ovale.trace then Ovale:Print(element.type.." return "..nilstring(startB)..","..nilstring(endB)) end
-		return startB, endB, prioriteB, elementB
-	elseif (element.type == "or") then
-		if (Ovale.trace) then
-			self:Print(element.type)
-		end
-
-		local startA, endA = Ovale:CalculerMeilleureAction(element.a)
-		local startB, endB = Ovale:CalculerMeilleureAction(element.b)
-		if isBefore(endA,self.currentTime) then
-			return startB,endB
-		elseif isBefore(endB,self.currentTime) then
-			return startA,endA
-		end
-
-		if isBefore(endA,startB) then
-			return startA,endA
-		elseif isBefore(endB,startA) then
-			return startB,endB
-		end
-
-		if isBefore(startA, startB) then
-			startB = startA
-		end
-		if isAfter(endA, endB) then
-			endB = endA
-		end
-		return startB, endB
-	elseif element.type == "operator" then
-		local startA, endA, prioA, elementA = self:CalculerMeilleureAction(element.a)
-		local startB, endB, prioB, elementB = self:CalculerMeilleureAction(element.b)
-		if not elementA or not elementB then
-			self:Log("operator " .. element.operator .. ": elementA or elementB is nil")
-			return nil
-		end
-		local a = elementA.value
-		local b = elementA.origin
-		local c = elementA.rate
-		local x = elementB.value
-		local y = elementB.origin
-		local z = elementB.rate
-
-		if not a or not x or not b or not y then
-			self:Log("operator " .. element.operator .. ": a or x is nil")
-			return nil
-		end
-
-		self:Log(a.."+(t-"..b..")*"..c.. element.operator..x.."+(t-"..y..")*"..z)
-
-		local l, m, n
-
-		if element.operator == "*" then
-			if c == 0 then
-				l = a*x
-				m = y
-				n = a*z
-			elseif z == 0 then
-				l = x*a; m = b; n = x*c
-			else
-				self:Print("ERROR: at least one value must be constant when multiplying")
-				self.bug = true
-			end
-		elseif element.operator == "+" then
-			if c+z == 0 then
-				l = a+x; m = 0; n = 0
-			else
-				l = a+x; m = (b*c+y*z)/(c+z); n = c+z
-			end
-		elseif element.operator == '-' then
-			if c-z == 0 then
-				l = a-x; m = 0; n = 0
-			else
-				l = a-x; m = (b*c-y*z)/(c-z); n = c-z
-			end
-		elseif element.operator == '/' then
-			if z == 0 then
-				l = a/x; m = b; n = c/x
-			else
-				self:Print("ERROR: second operator of / must be constant")
-				self.bug = true
-			end
-		elseif element.operator == '<' then
-			-- a + (t-b)*c = x + (t-y)*z
-			-- (t-b)*c - (t-y)*z = x - a
-			-- t*c - b*c - t*z + y*z = x - a
-			-- t*(c-z) = x - a + b*c + y*z
-			-- t = (x-a + b*c + y*z)/(c-z)
-			if c == z then
-				if a-b*c < x-y*z then
-					return 0
-				else
-					return nil
-				end
-			else
-				local t = (x-a + b*c + y*z)/(c-z)
-				if c > z then
-					return 0, t
-				else
-					return t, nil
-				end
-			end
-		elseif element.operator == '>' then
-			if c == z then
-				self:Log("> with c==z")
-				if a-b*c > x-y*z then
-					self:Log("a>x")
-					return 0
-				else
-					return nil
-				end
-			else
-				local t = (x-a + b*c + y*z)/(c-z)
-				if c < z then
-					return 0, t
-				else
-					return t, nil
-				end
-			end
-		end
-		if not element.result then
-			element.result = { type = "value" }
-		end
-		local result = element.result
-		result.value = l
-		result.origin = m
-		result.rate = n
-		self:Log("result = " .. l .." + "..m.."*"..n)
-		return startA, endA, 3, result
-	elseif element.type == "lua" then
-		local ret = loadstring(element.lua)()
-		self:Log("lua "..nilstring(ret))
-		if not element.result then
-			element.result = { type = "value" }
-		end
-		local result = element.result
-		result.value = ret
-		result.origin = 0
-		result.rate = 0
-		return 0, nil, 3, result
-	elseif (element.type == "group") then
-		local meilleurTempsFils
-		local bestEnd
-		local meilleurePrioriteFils
-		local bestElement
-		local bestCastTime
-
-		if (Ovale.trace) then
-			self:Print(element.type.." ["..element.nodeId.."]")
-		end
-
-		if #element.nodes == 1 then
-			return Ovale:CalculerMeilleureAction(element.nodes[1])
-		end
-
-		for k, v in ipairs(element.nodes) do
-			local newStart, newEnd, priorite, nouveauElement = Ovale:CalculerMeilleureAction(v)
-			if newStart~=nil and newStart<Ovale.currentTime then
-				newStart = Ovale.currentTime
-			end
-
-
-			if newStart and (not newEnd or newStart<=newEnd) then
-				local remplacer
-
-				local newCastTime
-				if nouveauElement then
-					newCastTime = nouveauElement.castTime
-				end
-				if not newCastTime or newCastTime < self.gcd then
-					newCastTime = self.gcd
-				end
-
-				if (not meilleurTempsFils) then
-					remplacer = true
-				else
-					-- temps maximum entre le nouveau sort et le précédent
-					local maxEcart
-					if (priorite and not meilleurePrioriteFils) then
-						self.bug = true
-						self:Print("Internal error: meilleurePrioriteFils=nil and priorite="..priorite)
-						return nil
-					end
-					if (priorite and priorite > meilleurePrioriteFils) then
-						-- Si le nouveau sort est plus prioritaire que le précédent, on le lance
-						-- si caster le sort actuel repousse le nouveau sort
-						maxEcart = bestCastTime*0.75
-					elseif (priorite and priorite < meilleurePrioriteFils) then
-						-- A l'inverse, si il est moins prioritaire que le précédent, on ne le lance
-						-- que si caster le nouveau sort ne repousse pas le meilleur
-						maxEcart = -newCastTime*0.75
-					else
-						maxEcart = -0.01
-					end
-					if (newStart-meilleurTempsFils < maxEcart) then
-						remplacer = true
-					end
-				end
-				if (remplacer) then
-					meilleurTempsFils = newStart
-					meilleurePrioriteFils = priorite
-					bestElement = nouveauElement
-					bestEnd = newEnd
-					bestCastTime = newCastTime
-				end
-			end
-		end
-
-		if (meilleurTempsFils) then
-			if (Ovale.trace) then
-				if bestElement then
-					self:Print("group best action "..bestElement.params[1].." remains "..meilleurTempsFils..","..nilstring(bestEnd).." ["..element.nodeId.."]")
-				else
-					self:Print("group no best action returns "..meilleurTempsFils..","..nilstring(bestEnd).." ["..element.nodeId.."]")
-				end
-			end
-			return meilleurTempsFils, bestEnd, meilleurePrioriteFils, bestElement
-		else
-			if (Ovale.trace) then self:Print("group return nil") end
-			return nil
-		end
-	end
-	if (Ovale.trace) then self:Print("unknown element "..element.type..", return nil") end
-	return nil
-end
-
-function Ovale:ChargerDefaut()
-	local localizedClass, englishClass = UnitClass("player")
-
-	self.db = LibStub("AceDB-3.0"):New("OvaleDB",
-	{
-		profile =
-		{
-			display = true,
-			code = Ovale.defaut[englishClass],
-			left = 500,
-			top = 500,
-			check = {},
-			list = {},
-			apparence = {enCombat=false, iconScale = 2, secondIconScale = 1, margin = 4, fontScale = 0.5, iconShiftX = 0, iconShiftY = 0,
-				smallIconScale=1, raccourcis=true, numeric=false, avecCible = false,
-				verrouille = false, vertical = false, predictif=false, highlightIcon = true, clickThru = false,
-				latencyCorrection=true, hideVehicule=false, flashIcon=true, targetText = "●", alpha = 1,
-				optionsAlpha = 1, updateInterval=0.1}
-		}
-	})
-end
-
 function Ovale:AfficherConfig()
 	self.AceConfigDialog:SetDefaultSize("Ovale Apparence", 500, 550)
 	self.AceConfigDialog:Open("Ovale Apparence", configFrame)
@@ -2476,14 +215,14 @@ function Ovale:AfficherCode()
 end

 local function OnCheckBoxValueChanged(widget)
-	Ovale.db.profile.check[widget.userdata.k] = widget:GetValue()
+	OvaleOptions:GetProfile().check[widget.userdata.k] = widget:GetValue()
 	if Ovale.casesACocher[widget.userdata.k].compile then
 		Ovale.needCompile = true
 	end
 end

 local function OnDropDownValueChanged(widget)
-	Ovale.db.profile.list[widget.userdata.k] = widget.value
+	OvaleOptions:GetProfile().list[widget.userdata.k] = widget.value
 	if Ovale.listes[widget.userdata.k].compile then
 		Ovale.needCompile = true
 	end
@@ -2494,26 +233,26 @@ function Ovale:ToggleOptions()
 end

 function Ovale:UpdateVisibility()
-	if not Ovale.db.profile.display then
+	if not OvaleOptions:GetProfile().display then
 		self.frame:Hide()
 		return
 	end

 	self.frame:Show()

-	if Ovale.db.profile.apparence.hideVehicule and UnitInVehicle("player") then
+	if OvaleOptions:GetApparence().hideVehicule and UnitInVehicle("player") then
 		self.frame:Hide()
 	end

-	if Ovale.db.profile.apparence.avecCible and not UnitExists("target") then
+	if OvaleOptions:GetApparence().avecCible and not UnitExists("target") then
 		self.frame:Hide()
 	end

-	if Ovale.db.profile.apparence.enCombat and not Ovale.enCombat then
+	if OvaleOptions:GetApparence().enCombat and not Ovale.enCombat then
 		self.frame:Hide()
 	end

-	if Ovale.db.profile.apparence.targetHostileOnly and (UnitIsDead("target") or not UnitCanAttack("player", "target")) then
+	if OvaleOptions:GetApparence().targetHostileOnly and (UnitIsDead("target") or not UnitCanAttack("player", "target")) then
 		self.frame:Hide()
 	end
 end
@@ -2527,15 +266,17 @@ function Ovale:UpdateFrame()

 	self.checkBoxes = {}

+	local profile = OvaleOptions:GetProfile()
+
 	for k,checkBox in pairs(self.casesACocher) do
 		self.checkBoxes[k] = LibStub("AceGUI-3.0"):Create("CheckBox");
 		self.frame:AddChild(self.checkBoxes[k])
 		self.checkBoxes[k]:SetLabel(checkBox.text)
-		if self.db.profile.check[k]==nil then
-			self.db.profile.check[k] = checkBox.checked
+		if profile.check[k]==nil then
+			profile.check[k] = checkBox.checked
 		end
-		if (self.db.profile.check[k]) then
-			self.checkBoxes[k]:SetValue(self.db.profile.check[k]);
+		if (profile.check[k]) then
+			self.checkBoxes[k]:SetValue(profile.check[k]);
 		end
 		self.checkBoxes[k].userdata.k = k
 		self.checkBoxes[k]:SetCallback("OnValueChanged",OnCheckBoxValueChanged)
@@ -2547,11 +288,11 @@ function Ovale:UpdateFrame()
 		for k,list in pairs(self.listes) do
 			self.dropDowns[k] = LibStub("AceGUI-3.0"):Create("Dropdown");
 			self.dropDowns[k]:SetList(list.items)
-			if not self.db.profile.list[k] then
-				self.db.profile.list[k] = list.default
+			if not profile.list[k] then
+				profile.list[k] = list.default
 			end
-			if (self.db.profile.list[k]) then
-				self.dropDowns[k]:SetValue(self.db.profile.list[k]);
+			if (profile.list[k]) then
+				self.dropDowns[k]:SetValue(profile.list[k]);
 			end
 			self.dropDowns[k].userdata.k = k
 			self.dropDowns[k]:SetCallback("OnValueChanged",OnDropDownValueChanged)
@@ -2568,29 +309,11 @@ function Ovale:GetListValue(v)
 	return self.dropDowns[v] and self.dropDowns[v].value
 end

-function Ovale:GetSpellInfo(spellId)
-	if (not self.spellInfo[spellId]) then
-		self.spellInfo[spellId] = { aura = {player = {}, target = {}} }
-	end
-	return self.spellInfo[spellId]
-end
-
-function Ovale:ResetSpellInfo()
-	self.spellInfo = {}
-end
-
-function Ovale:EnableOtherAuras()
-	if self.otherAurasEnabled then
-		return
-	end
-	self.otherAurasEnabled = true
-end
-
 function Ovale:SetCheckBox(v,on)
 	for k,checkBox in pairs(self.casesACocher) do
 		if v==0 then
 			self.checkBoxes[k]:SetValue(on)
-			self.db.profile.check[k] = on
+			OvaleOptions:GetProfile().check[k] = on
 			break
 		end
 		v = v - 1
@@ -2601,9 +324,10 @@ function Ovale:ToggleCheckBox(v)
 	for k,checkBox in pairs(self.casesACocher) do
 		if v==0 then
 			self.checkBoxes[k]:SetValue(not self.checkBoxes[k]:GetValue())
-			self.db.profile.check[k] = self.checkBoxes[k]:GetValue()
+			OvaleOptions:GetProfile().check[k] = self.checkBoxes[k]:GetValue()
 			break
 		end
 		v = v - 1
 	end
-end
\ No newline at end of file
+end
+--</public-static-methods>
diff --git a/Ovale.toc b/Ovale.toc
index afe973f..fa5ec41 100644
--- a/Ovale.toc
+++ b/Ovale.toc
@@ -3,7 +3,7 @@
 ## Notes: Show the icon of the next spell to cast
 ## Notes-frFR: Affiche l'icône du prochain sort à lancer
 ## Author: Sidoine
-## Version: 4.3.11
+## Version: 4.3.12
 ## OptionalDeps: Ace3, Masque, Recount, Skada, LibBabble-CreatureType-3.0, LibRangeCheck-2.0
 ## SavedVariables: OvaleDB
 ## SavedVariablesPerCharacter: OvaleDBPC
@@ -15,6 +15,7 @@ embeds.xml
 Locale-frFR.lua
 Locale-deDE.lua
 Locale-enUS.lua
+Locale-esES.lua
 Locale-itIT.lua
 Locale-koKR.lua
 Locale-ptBR.lua
@@ -22,14 +23,24 @@ Locale-ruRU.lua
 Locale-zhCN.lua
 Locale-zhTW.lua
 Ovale.lua
-Condition.lua
+OvaleActionBar.lua
+OvaleAura.lua
+OvaleBestAction.lua
+OvaleCompile.lua
+OvaleCondition.lua
+OvaleData.lua
 OvaleEquipement.lua
+OvaleEnemies.lua
+OvaleFrame.lua
+OvaleFuture.lua
+OvaleGUID.lua
 OvaleIcone.lua
 OvaleIcone.xml
-OvaleFrame.lua
-OvaleCompile.lua
+OvaleOptions.lua
 OvaleRecount.lua
 OvaleSkada.lua
+OvaleSpellDamage.lua
+OvaleState.lua
 OvaleSwing.lua
 defaut\Chaman.lua
 defaut\Chasseur.lua
diff --git a/OvaleActionBar.lua b/OvaleActionBar.lua
new file mode 100644
index 0000000..8bd29da
--- /dev/null
+++ b/OvaleActionBar.lua
@@ -0,0 +1,108 @@
+-- Keep data about the player action bars (key bindings mostly)
+OvaleActionBar = LibStub("AceAddon-3.0"):NewAddon("OvaleActionBar", "AceEvent-3.0")
+
+--<public-static-properties>
+--key: spell name / value: action icon id
+OvaleActionBar.actionSort = {}
+OvaleActionBar.actionMacro = {}
+OvaleActionBar.actionObjet = {}
+OvaleActionBar.shortCut = {}
+--</public-static-properties>
+
+--<public-static-methods>
+function OvaleActionBar:OnEnable()
+    self:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
+    self:RegisterEvent("UPDATE_BINDINGS")
+	self:FillActionIndexes()
+end
+
+function OvaleActionBar:OnDisable()
+    self:UnregisterEvent("ACTIONBAR_SLOT_CHANGED")
+    self:UnregisterEvent("UPDATE_BINDINGS")
+end
+
+function OvaleActionBar:ACTIONBAR_SLOT_CHANGED(event, slot, unknown)
+	if (slot == 0) then
+		self:FillActionIndexes()
+	elseif (slot) then
+	-- on reoit aussi si c'est une macro avec mouseover  chaque fois que la souris passe sur une cible!
+		self:FillActionIndex(tonumber(slot))
+	end
+end
+
+--Called when the user changed his key bindings
+function OvaleActionBar:UPDATE_BINDINGS()
+	self:FillActionIndexes()
+end
+
+function OvaleActionBar:FillActionIndexes()
+	self.actionSort = {}
+	self.actionMacro = {}
+	self.actionObjet = {}
+	self.shortCut = {}
+	for i=1,120 do
+		self:FillActionIndex(i)
+	end
+end
+
+function OvaleActionBar:FillActionIndex(i)
+	self.shortCut[i] = self:FindKeyBinding(i)
+	local actionText = GetActionText(i)
+	if actionText then
+		self.actionMacro[actionText] = i
+	else
+		local type, spellId = GetActionInfo(i);
+		if (type=="spell") then
+			self.actionSort[spellId] = i
+		elseif (type =="item") then
+			self.actionObjet[spellId] = i
+		end
+	end
+end
+
+function OvaleActionBar:FindKeyBinding(id)
+-- ACTIONBUTTON1..12 => principale (1..12, 13..24, 73..108)
+-- MULTIACTIONBAR1BUTTON1..12 => bas gauche (61..72)
+-- MULTIACTIONBAR2BUTTON1..12 => bas droite (49..60)
+-- MULTIACTIONBAR3BUTTON1..12 => haut droit (25..36)
+-- MULTIACTIONBAR4BUTTON1..12 => haut gauche (37..48)
+	local name;
+	if (id<=24 or id>72) then
+		name = "ACTIONBUTTON"..(((id-1)%12)+1);
+	elseif (id<=36) then
+		name = "MULTIACTIONBAR3BUTTON"..(id-24);
+	elseif (id<=48) then
+		name = "MULTIACTIONBAR4BUTTON"..(id-36);
+	elseif (id<=60) then
+		name = "MULTIACTIONBAR2BUTTON"..(id-48);
+	else
+		name = "MULTIACTIONBAR1BUTTON"..(id-60);
+	end
+	local key = GetBindingKey(name);
+--[[	if (not key) then
+		DEFAULT_CHAT_FRAME:AddMessage(id.."=>"..name.." introuvable")
+	else
+		DEFAULT_CHAT_FRAME:AddMessage(id.."=>"..name.."="..key)
+	end]]
+	return key;
+end
+
+-- Get the action id that match a spell id
+function OvaleActionBar:GetForSpell(spellId)
+	return self.actionSort[spellId]
+end
+
+-- Get the action id that match a macro id
+function OvaleActionBar:GetForMacro(macroId)
+	return self.actionMacro[macroId]
+end
+
+-- Get the action id that match an item id
+function OvaleActionBar:GetForItem(itemId)
+	return self.actionObjet[itemId]
+end
+
+function OvaleActionBar:GetBinding(actionId)
+	return self.shortCut[actionId]
+end
+--</public-static-methods>
diff --git a/OvaleAura.lua b/OvaleAura.lua
new file mode 100644
index 0000000..ae0f3d4
--- /dev/null
+++ b/OvaleAura.lua
@@ -0,0 +1,269 @@
+-- This addon keep the list of all the aura for all the units
+-- Fore each aura, it saves the state of the player when it was refreshed
+
+OvaleAura = LibStub("AceAddon-3.0"):NewAddon("OvaleAura", "AceEvent-3.0")
+
+--<public-static-properties>
+OvaleAura.aura = {}
+OvaleAura.serial = 0
+OvaleAura.spellHaste = 1
+OvaleAura.meleeHaste = 1
+OvaleAura.damageMultiplier = 1
+OvaleAura.playerGUID = nil
+--</public-static-properties>
+
+-- Events
+--<public-static-methods>
+function OvaleAura:OnEnable()
+	self.playerGUID = UnitGUID("player")
+	self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
+end
+
+function OvaleAura:OnDisable()
+	self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
+end
+
+function OvaleAura:COMBAT_LOG_EVENT_UNFILTERED(event, ...)
+	local time, event, hideCaster, sourceGUID, sourceName, sourceFlags, sourceRaidFlags, destGUID, destName, destFlags, destRaidFlags = select(1, ...)
+
+	if string.find(event, "SPELL_AURA_") == 1 then
+		local spellId, spellName, spellSchool, auraType = select(12, ...)
+
+		local unitId = OvaleGUID:GetUnitId(destGUID)
+
+		if unitId then
+			self:UpdateAuras(unitId, destGUID)
+		end
+
+		if sourceGUID == self.playerGUID and (event == "SPELL_AURA_APPLIED" or event == "SPELL_AURA_REFRESH" or event == "SPELL_AURA_APPLIED_DOSE") then
+			local aura = self:GetAuraByGUID(destGUID, spellId, true)
+			if aura then
+				aura.spellHaste = self.spellHaste
+			end
+		end
+	end
+
+	if event == "UNIT_DIED" then
+		self.aura[destGUID] = nil
+		local unitId = OvaleGUID:GetUnitId(destGUID)
+		if unitId then
+			Ovale.refreshNeeded[unitId] = true
+		end
+	end
+end
+
+function OvaleAura:AddAura(unitGUID, spellId, unitCaster, icon, count, debuffType, duration, expirationTime, isStealable, name)
+	local auraList = self.aura[unitGUID]
+
+	if not auraList[spellId] then
+		auraList[spellId] = {}
+	end
+
+	local mine = (unitCaster == "player")
+	local aura
+	if mine then
+		if not auraList[spellId].mine then
+			auraList[spellId].mine = { gain = OvaleState.maintenant }
+		end
+		aura = auraList[spellId].mine
+	else
+		if not auraList[spellId].other then
+			auraList[spellId].other = { gain = OvaleState.maintenant }
+		end
+		aura = auraList[spellId].other
+	end
+
+	aura.serial = self.serial
+
+	if count == 0 then
+		count = 1
+	end
+
+	if not aura.ending or aura.ending < expirationTime or aura.stacks < count then
+		aura.icon = icon
+		aura.stacks = count
+		aura.debuffType = debuffType
+		if duration > 0 then
+			aura.duration = duration
+			aura.ending = expirationTime
+		else
+			aura.duration = nil
+			aura.ending = nil
+		end
+		aura.start = expirationTime - duration
+		aura.stealable = isStealable
+		aura.mine = mine
+		aura.source = unitCaster
+		aura.name = name
+	end
+end
+
+-- Private methods
+function OvaleAura:UpdateAuras(unitId, unitGUID)
+	self.serial = self.serial + 1
+
+	local hateBase
+	local hateCommune
+	local hateSorts
+	local hateCaC
+	local hateHero
+	local hateClasse
+	local damageMultiplier
+
+	if unitId == "player" then
+		hateBase = GetCombatRatingBonus(18)
+		hateCommune = 0
+		hateSorts = 0
+		hateCaC = 0
+		hateHero = 0
+		hateClasse = 0
+		damageMultiplier = 1
+	end
+
+	if not unitGUID then
+		unitGUID = UnitGUID(unitId)
+	end
+
+	if not self.aura[unitGUID] then
+		self.aura[unitGUID] = {}
+	end
+
+	local i = 1
+
+	local mode = "HELPFUL"
+	while (true) do
+		local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId =  UnitAura(unitId, i, mode)
+		if not name then
+			if mode == "HELPFUL" then
+				mode = "HARMFUL"
+				i = 1
+			else
+				break
+			end
+		else
+			self:AddAura(unitGUID, spellId, unitCaster, icon, count, debuffType, duration, expirationTime, isStealable, name)
+			if debuffType then
+				-- TODO: not very clean
+				-- should be computed by OvaleState:GetAura
+				self:AddAura(unitGUID, debuffType, unitCaster, icon, count, debuffType, duration, expirationTime, isStealable, name)
+			end
+
+			if unitId == "player" then
+				if OvaleData.buffSpellList.spellhaste[spellId] then
+					hateSorts = 5
+				elseif OvaleData.buffSpellList.meleehaste[spellId] then
+					hateCaC = 10
+				elseif OvaleData.buffSpellList.heroism[spellId] then
+					hateHero = 30
+				elseif OvaleData.selfHasteBuff[spellId] then
+					hateClasse = OvaleData.selfHasteBuff[spellId]
+				end
+				if OvaleData.selfDamageBuff[spellId] then
+					damageMultiplier = damageMultiplier * OvaleData.selfDamageBuff[spellId]
+				end
+			end
+			i = i + 1
+		end
+	end
+
+	local auraList = self.aura[unitGUID]
+	--Removes expired aura
+	for spellId,whoseTable in pairs(auraList) do
+		for whose,aura in pairs(whoseTable) do
+			if aura.serial ~= self.serial then
+				-- Ovale:Print("Removing "..aura.name.." from "..whose .. " self.serial = " ..self.serial .. " aura.serial = " ..aura.serial)
+				whoseTable[whose] = nil
+			end
+		end
+		if not next(whoseTable) then
+			--Ovale:Print("Removing "..spellId)
+			auraList[spellId] = nil
+		end
+	end
+
+	--Clear unit if all aura have been deleted
+	if not next(auraList) then
+		self.aura[unitGUID] = nil
+	end
+
+	--Update player haste
+	if unitId == "player" then
+		self.spellHaste = 1 + (hateBase + hateCommune + hateSorts + hateHero + hateClasse)/100
+		self.meleeHaste = 1 + (hateBase + hateCommune + hateCaC + hateHero + hateClasse)/100
+		self.damageMultiplier = damageMultiplier
+	end
+
+	Ovale.refreshNeeded[unitId] = true
+end
+
+-- Public methods
+function OvaleAura:GetAuraByGUID(guid, spellId, mine, unitId)
+	if not guid then
+		return nil
+	end
+	local auraTable = self.aura[guid]
+	if not auraTable then
+		if not unitId then
+			unitId = OvaleGUID:GetUnitId(guid)
+		end
+		if not unitId then
+			return nil
+		end
+		self:UpdateAuras(unitId, guid)
+		auraTable = self.aura[guid]
+		if not auraTable then
+			-- no aura on target
+			return nil
+		end
+	end
+	local aura = auraTable[spellId]
+	if not aura then return nil end
+	if mine or mine == 1 then
+		return aura.mine
+	elseif aura.other then
+		return aura.other
+	else
+		return aura.mine
+	end
+end
+
+function OvaleAura:GetAura(unitId, spellId, mine)
+	return self:GetAuraByGUID(UnitGUID(unitId), spellId, mine, unitId)
+end
+
+-- Look for the last of my aura on any targt that will expires.
+-- Returns its expiration time
+function OvaleAura:GetExpirationTimeOnAnyTarget(spellId)
+	local ending = nil
+	local starting = nil
+
+	for unitId,auraTable in pairs(self.aura) do
+		if auraTable[spellId] then
+			local aura = auraTable[spellId].mine
+			if aura then
+				local newEnding = aura.ending
+				local newStarting = aura.start
+				if newStarting and (not staring or newStarting < starting) then
+					starting = newStarting
+				end
+				if newEnding and (not ending or newEnding > ending) then
+					ending = newEnding
+				end
+			end
+		end
+	end
+	return starting, ending
+end
+
+function OvaleAura:Debug()
+	Ovale:Print("------")
+	for guid,auraTable in pairs(self.aura) do
+		Ovale:Print("***"..guid)
+		for spellId,whoseTable in pairs(auraTable) do
+			for whose,aura in pairs(whoseTable) do
+				Ovale:Print(guid.." "..whose.." "..spellId .. " "..aura.name .. " stacks ="..aura.stacks)
+			end
+		end
+	end
+end
+--</public-static-methods>
diff --git a/OvaleBestAction.lua b/OvaleBestAction.lua
new file mode 100644
index 0000000..cccbe52
--- /dev/null
+++ b/OvaleBestAction.lua
@@ -0,0 +1,616 @@
+OvaleBestAction = {}
+
+--<private-static-methods>
+local function nilstring(text)
+	if text == nil then
+		return "nil"
+	else
+		return text
+	end
+end
+
+local function printTime(temps)
+	if (temps == nil) then
+		Ovale:Print("> nil")
+	else
+		Ovale:Print("> "..temps)
+	end
+end
+
+local function addTime(time1, duration)
+	if not time1 then
+		return nil
+	else
+		return time1 + duration
+	end
+end
+
+local function isBeforeEqual(time1, time2)
+	return time1 and (not time2 or time1<=time2)
+end
+
+local function isBefore(time1, time2)
+	return time1 and (not time2 or time1<time2)
+end
+
+local function isAfterEqual(time1, time2)
+	return not time1 or (time2 and time1>=time2)
+end
+
+local function isAfter(time1, time2)
+	return not time1 or (time2 and time1>time2)
+end
+--</private-static-methods>
+
+--<public-static-methods>
+function OvaleBestAction:StartNewAction()
+	OvaleState:Reset()
+	OvaleFuture:Apply()
+end
+
+function OvaleBestAction:GetActionInfo(element)
+	if not element then
+		return nil
+	end
+
+	local spellId = element.params[1]
+	local action
+	local actionTexture, actionInRange, actionCooldownStart, actionCooldownDuration,
+		actionUsable, actionShortcut, actionIsCurrent, actionEnable
+
+	local target = element.params.target
+	if (not target) then
+		target = OvaleCondition.defaultTarget
+	end
+
+	if (element.func == "Spell" ) then
+		action = OvaleActionBar:GetForSpell(spellId)
+		if not OvaleData.spellList[spellId] and not action then
+			Ovale:Log("Spell "..spellId.." not learnt")
+			return nil
+		end
+
+		actionCooldownStart, actionCooldownDuration, actionEnable = OvaleData:GetComputedSpellCD(spellId)
+
+		spellName = OvaleData.spellList[spellId]
+		if not spellName then
+			spellName = GetSpellInfo(spellId)
+		end
+		actionTexture = GetSpellTexture(spellId)
+		actionInRange = IsSpellInRange(spellName, target)
+		actionUsable = IsUsableSpell(spellId)
+		actionShortcut = nil
+	elseif (element.func=="Macro") then
+		action = OvaleActionBar:GetForMacro(element.params[1])
+		if action then
+			actionTexture = GetActionTexture(action)
+			actionInRange = IsActionInRange(action, target)
+			actionCooldownStart, actionCooldownDuration, actionEnable = GetActionCooldown(action)
+			actionUsable = IsUsableAction(action)
+			actionShortcut = OvaleActionBar:GetBinding(action)
+			actionIsCurrent = IsCurrentAction(action)
+		else
+			Ovale:Log("Unknown macro "..element.params[1])
+		end
+	elseif (element.func=="Item") then
+		local itemId
+		if (type(element.params[1]) == "number") then
+			itemId = element.params[1]
+		else
+			local _,_,id = string.find(GetInventoryItemLink("player",GetInventorySlotInfo(element.params[1])) or "","item:(%d+):%d+:%d+:%d+")
+			if not id then
+				return nil
+			end
+			itemId = tonumber(id)
+		end
+
+		if (Ovale.trace) then
+			Ovale:Print("Item "..nilstring(itemId))
+		end
+
+		local spellName = GetItemSpell(itemId)
+		actionUsable = (spellName~=nil)
+
+		action = OvaleActionBar:GetForItem(itemId)
+		actionTexture = GetItemIcon(itemId)
+		actionInRange = IsItemInRange(itemId, target)
+		actionCooldownStart, actionCooldownDuration, actionEnable = GetItemCooldown(itemId)
+		actionShortcut = nil
+		actionIsCurrent = nil
+	elseif element.func=="Texture" then
+		actionTexture = "Interface\\Icons\\"..element.params[1]
+		actionCooldownStart = OvaleState.maintenant
+		actionCooldownDuration = 0
+		actionEnable = 1
+		actionUsable = true
+	end
+
+	if action then
+		if actionUsable == nil then
+			actionUsable = IsUsableAction(action)
+		end
+		actionShortcut = OvaleActionBar:GetBinding(action)
+		actionIsCurrent = IsCurrentAction(action)
+	end
+
+	local cd = OvaleState:GetCD(spellId)
+	if cd and cd.toggle then
+		actionIsCurrent = 1
+	end
+
+	return actionTexture, actionInRange, actionCooldownStart, actionCooldownDuration,
+					actionUsable, actionShortcut, actionIsCurrent, actionEnable, spellId, target, element.params.nored
+end
+
+function OvaleBestAction:Compute(element)
+	if (Ovale.bug and not Ovale.trace) then
+		return nil
+	end
+
+	if (not element) then
+		return nil
+	end
+
+	--TODO: crer un objet par type au lieu de ce if else if tout moche
+	if (element.type=="function")then
+		if (element.func == "Spell" or element.func=="Macro" or element.func=="Item" or element.func=="Texture") then
+			local action
+			local actionTexture, actionInRange, actionCooldownStart, actionCooldownDuration,
+				actionUsable, actionShortcut, actionIsCurrent, actionEnable, spellId = self:GetActionInfo(element)
+
+			if not actionTexture then
+				if (Ovale.trace) then
+					Ovale:Print("Action "..element.params[1].." not found")
+				end
+				return nil
+			end
+			if element.params.usable==1 and not actionUsable then
+				if (Ovale.trace) then
+					Ovale:Print("Action "..element.params[1].." not usable")
+				end
+				return nil
+			end
+			if spellId and OvaleData.spellInfo[spellId] and OvaleData.spellInfo[spellId].casttime then
+				element.castTime = OvaleData.spellInfo[spellId].casttime
+			elseif spellId then
+				local spell, rank, icon, cost, isFunnel, powerType, castTime = GetSpellInfo(spellId)
+				if castTime then
+					element.castTime = castTime/1000
+				else
+					element.castTime = nil
+				end
+			else
+				element.castTime = 0
+			end
+			--TODO: not useful anymore?
+			if (spellId and OvaleData.spellInfo[spellId] and OvaleData.spellInfo[spellId].toggle and actionIsCurrent) then
+				if (Ovale.trace) then
+					Ovale:Print("Action "..element.params[1].." is current action")
+				end
+				return nil
+			end
+			if actionEnable and actionEnable>0 then
+				local restant
+				if (not actionCooldownDuration or actionCooldownStart==0) then
+					restant = OvaleState.currentTime
+				else
+					restant = actionCooldownDuration + actionCooldownStart
+				end
+				Ovale:Log("restant = "..restant.." attenteFinCast="..nilstring(OvaleState.attenteFinCast))
+				if restant<OvaleState.attenteFinCast then
+					if	not OvaleData.spellInfo[OvaleState.currentSpellId] or
+							not OvaleData.spellInfo[OvaleState.currentSpellId].canStopChannelling then
+						restant = OvaleState.attenteFinCast
+					else
+						--TODO: pas exact, parce que si ce sort est report de par exemple 0,5s par un debuff
+						--a tombera entre deux ticks
+						local ticks = floor(OvaleAura.spellHaste * OvaleData.spellInfo[OvaleState.currentSpellId].canStopChannelling + 0.5)
+						local tickLength = (OvaleState.attenteFinCast - OvaleState.startCast) / ticks
+						local tickTime = OvaleState.startCast + tickLength
+						if (Ovale.trace) then
+							Ovale:Print(spellName.." restant = " .. restant)
+							Ovale:Print("ticks = "..ticks.." tickLength="..tickLength.." tickTime="..tickTime)
+						end
+						for i=1,ticks do
+							if restant<=tickTime then
+								restant = tickTime
+								break
+							end
+							tickTime = tickTime + tickLength
+						end
+						if (Ovale.trace) then
+							Ovale:Print(spellId.." restant = " .. restant)
+						end
+					end
+				end
+				if (Ovale.trace) then
+					Ovale:Print("Action "..element.params[1].." remains "..restant)
+				end
+				local retourPriorite = element.params.priority
+				if (not retourPriorite) then
+					retourPriorite = 3
+				end
+				return restant, nil, retourPriorite, element
+			else
+				if (Ovale.trace) then
+					Ovale:Print("Action "..element.params[1].." not enabled")
+				end
+			end
+		else
+			local classe = OvaleCondition.conditions[element.func]
+			if (not classe) then
+				Ovale.bug = true
+				Ovale:Print("Function "..element.func.." not found")
+				return nil
+			end
+			local start, ending, rate = classe(element.params)
+
+			if (Ovale.trace) then
+				local parameterList = element.func.."("
+				for k,v in pairs(element.params) do
+					parameterList = parameterList..k.."="..v..","
+				end
+				Ovale:Print("Function "..parameterList..") returned "..nilstring(start)..","..nilstring(ending)..","..nilstring(rate))
+			end
+
+			if rate then
+				if not element.result then
+					element.result = { type = "value" }
+				end
+				local result = element.result
+				result.value = start
+				result.origin = ending
+				result.rate = rate
+				return 0, nil, 3, result
+			else
+				return start, ending
+			end
+		end
+	elseif element.type == "time" then
+		return element.value
+	elseif element.type == "value" then
+		Ovale:Log("value " .. element.value)
+		return 0, nil, 3, element
+	elseif element.type == "after" then
+		local timeA = self:Compute(element.time)
+		local startA, endA = self:Compute(element.a)
+		return addTime(startA, timeA), addTime(endA, timeA)
+	elseif (element.type == "before") then
+		if (Ovale.trace) then
+			--Ovale:Print(nilstring(element.time).."s before ["..element.nodeId.."]")
+		end
+		local timeA = self:Compute(element.time)
+		local startA, endA = self:Compute(element.a)
+		return addTime(startA, -timeA), addTime(endA, -timeA)
+	elseif (element.type == "between") then
+		Ovale:Log("between")
+		local tempsA = self:Compute(element.a)
+		local tempsB = self:Compute(element.b)
+		if tempsB==nil and tempsA==nil then
+			Ovale:Log("diff returns 0 because the two nodes are nil")
+			return 0
+		end
+
+		if tempsA==nil or tempsB==nil then
+			if Ovale.trace then Ovale:Print(element.type.." return nil") end
+			return nil
+		end
+		local diff
+		if tempsA>tempsB then
+			diff = tempsA - tempsB
+		else
+			diff = tempsB - tempsA
+		end
+		Ovale:Log("diff returns "..diff)
+		return diff
+	elseif element.type == "fromuntil" then
+		Ovale:Log("fromuntil")
+		local tempsA = self:Compute(element.a)
+		if (tempsA==nil) then
+			if Ovale.trace then Ovale:Print(element.type.." return nil") end
+			return nil
+		end
+		local tempsB = self:Compute(element.b)
+		if (tempsB==nil) then
+			if Ovale.trace then Ovale:Print(element.type.." return nil") end
+			return nil
+		end
+		Ovale:Log("fromuntil returns "..(tempsB - tempsA))
+		return tempsB - tempsA
+	elseif element.type == "compare" then
+		Ovale:Log("compare "..element.comparison)
+		local tempsA = self:Compute(element.a)
+		local timeB = self:Compute(element.time)
+		Ovale:Log(nilstring(tempsA).." "..element.comparison.." "..nilstring(timeB))
+		if element.comparison == "more" and (not tempsA or tempsA>timeB) then
+			if Ovale.trace then Ovale:Print(element.type.." return 0") end
+			return 0
+		elseif element.comparison == "less" and tempsA and tempsA<timeB then
+			if Ovale.trace then Ovale:Print(element.type.." return 0") end
+			return 0
+		elseif element.comparison == "at most" and tempsA and tempsA<=timeB then
+			if Ovale.trace then Ovale:Print(element.type.." return 0") end
+			return 0
+		elseif element.comparison == "at least" and (not tempsA or tempsA>=timeB) then
+			if Ovale.trace then Ovale:Print(element.type.." return 0") end
+			return 0
+		end
+		return nil
+	elseif element.type == "and" or element.type == "if" then
+		if (Ovale.trace) then
+			Ovale:Print(element.type.." ["..element.nodeId.."]")
+		end
+		local startA, endA = self:Compute(element.a)
+		if (startA==nil) then
+			if Ovale.trace then Ovale:Print(element.type.." return nil  ["..element.nodeId.."]") end
+			return nil
+		end
+		if startA == endA then
+			if Ovale.trace then Ovale:Print(element.type.." return startA=endA  ["..element.nodeId.."]") end
+			return nil
+		end
+		local startB, endB, prioriteB, elementB = self:Compute(element.b)
+		if isAfter(startB, endA) or isAfter(startA, endB) then
+			if Ovale.trace then Ovale:Print(element.type.." return nil ["..element.nodeId.."]") end
+			return nil
+		end
+		if isBefore(startB, startA) then
+			startB = startA
+		end
+		if isAfter(endB, endA) then
+			endB = endA
+		end
+		if Ovale.trace then
+			Ovale:Print(element.type.." return "..nilstring(startB)..","..nilstring(endB).." ["..element.nodeId.."]")
+		end
+		return startB, endB, prioriteB, elementB
+	elseif element.type == "unless" then
+		if Ovale.trace then
+			Ovale:Print(element.type)
+		end
+		local startA, endA = self:Compute(element.a)
+		local startB, endB, prioriteB, elementB = self:Compute(element.b)
+
+		if isBeforeEqual(startA, startB) and isAfterEqual(endA, endB) then
+			if Ovale.trace then Ovale:Print(element.type.." return nil") end
+			return nil
+		end
+
+		if isAfterEqual(startA, startB) and isBefore(endA, endB) then
+			if Ovale.trace then Ovale:Print(element.type.." return "..nilstring(endA)..","..nilstring(endB)) end
+			return endA, endB, prioriteB, elementB
+		end
+
+		if isAfter(startA, startB) and isBefore(startA, endB) then
+			endB = startA
+		end
+
+		if isAfter(endA, startB) and isBefore(endA, endB) then
+			startB = endA
+		end
+
+		if Ovale.trace then Ovale:Print(element.type.." return "..nilstring(startB)..","..nilstring(endB)) end
+		return startB, endB, prioriteB, elementB
+	elseif (element.type == "or") then
+		if (Ovale.trace) then
+			Ovale:Print(element.type)
+		end
+
+		local startA, endA = self:Compute(element.a)
+		local startB, endB = self:Compute(element.b)
+		if isBefore(endA,OvaleState.currentTime) then
+			return startB,endB
+		elseif isBefore(endB,OvaleState.currentTime) then
+			return startA,endA
+		end
+
+		if isBefore(endA,startB) then
+			return startA,endA
+		elseif isBefore(endB,startA) then
+			return startB,endB
+		end
+
+		if isBefore(startA, startB) then
+			startB = startA
+		end
+		if isAfter(endA, endB) then
+			endB = endA
+		end
+		return startB, endB
+	elseif element.type == "operator" then
+		local startA, endA, prioA, elementA = self:Compute(element.a)
+		local startB, endB, prioB, elementB = self:Compute(element.b)
+		if not elementA or not elementB then
+			Ovale:Log("operator " .. element.operator .. ": elementA or elementB is nil")
+			return nil
+		end
+		local a = elementA.value
+		local b = elementA.origin
+		local c = elementA.rate
+		local x = elementB.value
+		local y = elementB.origin
+		local z = elementB.rate
+
+		if not a or not x or not b or not y then
+			Ovale:Log("operator " .. element.operator .. ": a or x is nil")
+			return nil
+		end
+
+		Ovale:Log(a.."+(t-"..b..")*"..c.. element.operator..x.."+(t-"..y..")*"..z)
+
+		local l, m, n
+
+		if element.operator == "*" then
+			if c == 0 then
+				l = a*x
+				m = y
+				n = a*z
+			elseif z == 0 then
+				l = x*a; m = b; n = x*c
+			else
+				Ovale:Print("ERROR: at least one value must be constant when multiplying")
+				Ovale.bug = true
+			end
+		elseif element.operator == "+" then
+			if c+z == 0 then
+				l = a+x; m = 0; n = 0
+			else
+				l = a+x; m = (b*c+y*z)/(c+z); n = c+z
+			end
+		elseif element.operator == '-' then
+			if c-z == 0 then
+				l = a-x; m = 0; n = 0
+			else
+				l = a-x; m = (b*c-y*z)/(c-z); n = c-z
+			end
+		elseif element.operator == '/' then
+			if z == 0 then
+				l = a/x; m = b; n = c/x
+			else
+				Ovale:Print("ERROR: second operator of / must be constant")
+				Ovale.bug = true
+			end
+		elseif element.operator == '<' then
+			-- a + (t-b)*c = x + (t-y)*z
+			-- (t-b)*c - (t-y)*z = x - a
+			-- t*c - b*c - t*z + y*z = x - a
+			-- t*(c-z) = x - a + b*c + y*z
+			-- t = (x-a + b*c + y*z)/(c-z)
+			if c == z then
+				if a-b*c < x-y*z then
+					return 0
+				else
+					return nil
+				end
+			else
+				local t = (x-a + b*c + y*z)/(c-z)
+				if c > z then
+					return 0, t
+				else
+					return t, nil
+				end
+			end
+		elseif element.operator == '>' then
+			if c == z then
+				Ovale:Log("> with c==z")
+				if a-b*c > x-y*z then
+					Ovale:Log("a>x")
+					return 0
+				else
+					return nil
+				end
+			else
+				local t = (x-a + b*c + y*z)/(c-z)
+				if c < z then
+					return 0, t
+				else
+					return t, nil
+				end
+			end
+		end
+		if not element.result then
+			element.result = { type = "value" }
+		end
+		local result = element.result
+		result.value = l
+		result.origin = m
+		result.rate = n
+		Ovale:Log("result = " .. l .." + "..m.."*"..n)
+		return startA, endA, 3, result
+	elseif element.type == "lua" then
+		local ret = loadstring(element.lua)()
+		Ovale:Log("lua "..nilstring(ret))
+		if not element.result then
+			element.result = { type = "value" }
+		end
+		local result = element.result
+		result.value = ret
+		result.origin = 0
+		result.rate = 0
+		return 0, nil, 3, result
+	elseif (element.type == "group") then
+		local meilleurTempsFils
+		local bestEnd
+		local meilleurePrioriteFils
+		local bestElement
+		local bestCastTime
+
+		if (Ovale.trace) then
+			Ovale:Print(element.type.." ["..element.nodeId.."]")
+		end
+
+		if #element.nodes == 1 then
+			return self:Compute(element.nodes[1])
+		end
+
+		for k, v in ipairs(element.nodes) do
+			local newStart, newEnd, priorite, nouveauElement = self:Compute(v)
+			if newStart~=nil and newStart<OvaleState.currentTime then
+				newStart = OvaleState.currentTime
+			end
+
+
+			if newStart and (not newEnd or newStart<=newEnd) then
+				local remplacer
+
+				local newCastTime
+				if nouveauElement then
+					newCastTime = nouveauElement.castTime
+				end
+				if not newCastTime or newCastTime < OvaleState.gcd then
+					newCastTime = OvaleState.gcd
+				end
+
+				if (not meilleurTempsFils) then
+					remplacer = true
+				else
+					-- temps maximum entre le nouveau sort et le prcdent
+					local maxEcart
+					if (priorite and not meilleurePrioriteFils) then
+						Ovale.bug = true
+						Ovale:Print("Internal error: meilleurePrioriteFils=nil and priorite="..priorite)
+						return nil
+					end
+					if (priorite and priorite > meilleurePrioriteFils) then
+						-- Si le nouveau sort est plus prioritaire que le prcdent, on le lance
+						-- si caster le sort actuel repousse le nouveau sort
+						maxEcart = bestCastTime*0.75
+					elseif (priorite and priorite < meilleurePrioriteFils) then
+						-- A l'inverse, si il est moins prioritaire que le prcdent, on ne le lance
+						-- que si caster le nouveau sort ne repousse pas le meilleur
+						maxEcart = -newCastTime*0.75
+					else
+						maxEcart = -0.01
+					end
+					if (newStart-meilleurTempsFils < maxEcart) then
+						remplacer = true
+					end
+				end
+				if (remplacer) then
+					meilleurTempsFils = newStart
+					meilleurePrioriteFils = priorite
+					bestElement = nouveauElement
+					bestEnd = newEnd
+					bestCastTime = newCastTime
+				end
+			end
+		end
+
+		if (meilleurTempsFils) then
+			if (Ovale.trace) then
+				if bestElement then
+					Ovale:Print("group best action "..bestElement.params[1].." remains "..meilleurTempsFils..","..nilstring(bestEnd).." ["..element.nodeId.."]")
+				else
+					Ovale:Print("group no best action returns "..meilleurTempsFils..","..nilstring(bestEnd).." ["..element.nodeId.."]")
+				end
+			end
+			return meilleurTempsFils, bestEnd, meilleurePrioriteFils, bestElement
+		else
+			if (Ovale.trace) then Ovale:Print("group return nil") end
+			return nil
+		end
+	end
+	if (Ovale.trace) then Ovale:Print("unknown element "..element.type..", return nil") end
+	return nil
+end
+--</public-static-methods>
diff --git a/OvaleCompile.lua b/OvaleCompile.lua
index 68eaa4d..afc00ec 100644
--- a/OvaleCompile.lua
+++ b/OvaleCompile.lua
@@ -1,9 +1,14 @@
 local L = LibStub("AceLocale-3.0"):GetLocale("Ovale")

+OvaleCompile = {}
+
+--<private-static-properties>
 local node={}
 local defines = {}
 local customFunctions = {}
+--</private-static-properties>

+--<private-static-methods>
 local function AddNode(newNode)
 	node[#node+1] = newNode
 	newNode.nodeId = #node
@@ -48,12 +53,12 @@ local function HasGlyph(spellId)
 end

 local function HasTalent(talentId)
-	if not Ovale.listeTalentsRemplie then
-		Ovale:RemplirListeTalents()
+	if not OvaleData.listeTalentsRemplie then
+		OvaleData:RemplirListeTalents()
 	end
-	if Ovale.listeTalentsRemplie then
-		if Ovale.pointsTalent[talentId]~=nil then
-			return Ovale.pointsTalent[talentId]>0
+	if OvaleData.listeTalentsRemplie then
+		if OvaleData.pointsTalent[talentId]~=nil then
+			return OvaleData.pointsTalent[talentId]>0
 		else
 			Ovale:Print("Unknown talent "..talentId)
 			return false
@@ -79,7 +84,7 @@ local function TestConditions(paramList)
 			Ovale.casesACocher[cb] = {}
 		end
 		Ovale.casesACocher[cb].compile = true
-		if not Ovale.db.profile.check[cb] then
+		if not OvaleOptions:GetProfile().check[cb] then
 			return false
 		end
 	end
@@ -89,7 +94,7 @@ local function TestConditions(paramList)
 			Ovale.casesACocher[cb] = {}
 		end
 		Ovale.casesACocher[cb].compile = true
-		if Ovale.db.profile.check[cb] then
+		if OvaleOptions:GetProfile().check[cb] then
 			return false
 		end
 	end
@@ -100,7 +105,7 @@ local function TestConditions(paramList)
 			Ovale.listes[list] = { items = {}}
 		end
 		Ovale.listes[list].compile = true
-		if Ovale.db.profile.list[list] ~= key then
+		if OvaleOptions:GetProfile().list[list] ~= key then
 			return false
 		end
 	end
@@ -155,7 +160,7 @@ local function ParseSpellAddDebuff(params)
 	local spellId = paramList[1]
 	if spellId then
 		paramList[1] = nil
-		Ovale:GetSpellInfo(spellId).aura.player.HARMFUL = paramList
+		OvaleData:GetSpellInfo(spellId).aura.player.HARMFUL = paramList
 	end
 	return ""
 end
@@ -165,7 +170,7 @@ local function ParseSpellAddBuff(params)
 	local spellId = paramList[1]
 	if spellId then
 		paramList[1] = nil
-		Ovale:GetSpellInfo(spellId).aura.player.HELPFUL = paramList
+		OvaleData:GetSpellInfo(spellId).aura.player.HELPFUL = paramList
 	end
 	return ""
 end
@@ -175,7 +180,7 @@ local function ParseSpellAddTargetDebuff(params)
 	local spellId = paramList[1]
 	if spellId then
 		paramList[1] = nil
-		Ovale:GetSpellInfo(spellId).aura.target.HARMFUL = paramList
+		OvaleData:GetSpellInfo(spellId).aura.target.HARMFUL = paramList
 	end
 	return ""
 end
@@ -188,7 +193,7 @@ local function ParseSpellInfo(params)
 		if not TestConditions(paramList) then
 			return ""
 		end
-		local spellInfo = Ovale:GetSpellInfo(spellId)
+		local spellInfo = OvaleData:GetSpellInfo(spellId)
 		for k,v in pairs(paramList) do
 			if k == "addduration" then
 				spellInfo.duration = spellInfo.duration + v
@@ -207,7 +212,7 @@ local function ParseScoreSpells(params)
 		local spellId = tonumber(v)
 		if spellId then
 			--Ovale:Print("Add spell to score "..spellId)
-			Ovale.scoreSpell[spellId] = true
+			OvaleData.scoreSpell[spellId] = true
 		else
 			Ovale:Print("unknown spell "..v)
 		end
@@ -215,10 +220,10 @@ local function ParseScoreSpells(params)
 end

 local function ParseSpellList(name, params)
-	Ovale.buffSpellList[name] = {}
+	OvaleData.buffSpellList[name] = {}
 	local i = 1
 	for v in string.gmatch(params, "(%d+)") do
-		Ovale.buffSpellList[name][i] = tonumber(v)
+		OvaleData.buffSpellList[name][i] = tonumber(v)
 		i = i + 1
 	end
 end
@@ -418,21 +423,10 @@ local function ParseAddIcon(params, text)
 	return masterNode
 end

-function Ovale:CompileInputs(text)
-	self.casesACocher = {}
-	self.listes = {}
-	self.defaultListes = {}
-	self.defaultCheck = {}
-
-	text = string.gsub(text, "AddListItem%s*%(%s*(%w+)%s+(%w+)%s+\"(.-)\"%s*(.-)%s*%)", ParseAddListItem)
-	text = string.gsub(text, "AddCheckBox%s*%(%s*(%w+)%s+\"(.-)\"%s*(.-)%s*%)", ParseAddCheckBox)
-	return text
-end
-
 local function ParseCanStopChannelling(text)
 	local spellId = tonumber(text)
 	if spellId then
-		Ovale:GetSpellInfo(spellId).canStopChannelling = true
+		OvaleData:GetSpellInfo(spellId).canStopChannelling = true
 	else
 		Ovale:Print("CanStopChannelling with unknown spell "..spellId)
 	end
@@ -440,7 +434,7 @@ local function ParseCanStopChannelling(text)
 end

 local function ParseSpellName(text)
-	local spell = Ovale:GetSpellInfoOrNil(text)
+	local spell = OvaleData:GetSpellInfoOrNil(text)
 	if (spell) then
 		return '"'..spell..'"'
 	else
@@ -452,10 +446,21 @@ end
 local function ParseL(text)
 	return '"'..L[text]..'"'
 end
+--</private-static-methods>

-function Ovale:Compile(text)
-	self.compileOnItems = false
-	self.bug = false
+--<public-static-methods>
+function OvaleCompile:CompileInputs(text)
+	Ovale.casesACocher = {}
+	Ovale.listes = {}
+
+	text = string.gsub(text, "AddListItem%s*%(%s*(%w+)%s+(%w+)%s+\"(.-)\"%s*(.-)%s*%)", ParseAddListItem)
+	text = string.gsub(text, "AddCheckBox%s*%(%s*(%w+)%s+\"(.-)\"%s*(.-)%s*%)", ParseAddCheckBox)
+	return text
+end
+
+function OvaleCompile:Compile(text)
+	Ovale.compileOnItems = false
+	Ovale.bug = false
 	node = {}
 	defines = {}

@@ -476,7 +481,7 @@ function Ovale:Compile(text)
 	text = string.gsub(text, "L%s*%(%s*(%w+)%s*%)", ParseL)

 	-- Options diverses
-	Ovale:ResetSpellInfo()
+	OvaleData:ResetSpellInfo()
 	text = string.gsub(text, "CanStopChannelling%s*%(%s*(%w+)%s*%)", ParseCanStopChannelling)
 	text = string.gsub(text, "SpellAddBuff%s*%((.-)%)", ParseSpellAddBuff)
 	text = string.gsub(text, "SpellAddDebuff%s*%((.-)%)", ParseSpellAddDebuff)
@@ -511,7 +516,7 @@ function Ovale:Compile(text)
 	return masterNodes
 end

-function Ovale:DebugNode(node)
+function OvaleCompile:DebugNode(node)
 	local text
 	if (not node) then
 		return "#nil"
@@ -557,4 +562,5 @@ function Ovale:DebugNode(node)
 	end

 	return text
-end
\ No newline at end of file
+end
+--</public-static-methods>
diff --git a/OvaleCondition.lua b/OvaleCondition.lua
new file mode 100644
index 0000000..3c4cd50
--- /dev/null
+++ b/OvaleCondition.lua
@@ -0,0 +1,1175 @@
+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 (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
+	else
+		if (not a or (b~=nil and a<b)) then
+			return 0
+		else
+			return nil
+		end
+	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)
+	return OvaleState:GetExpirationTimeOnAnyTarget(spellId)
+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 GetManaAndRate(withBerserker)
+	local _,className = UnitClass("player")
+	local current = OvaleState.state.mana
+	if current~=lastEnergyValue then
+		lastEnergyValue = current
+		lastEnergyTime = OvaleState.currentTime
+	end
+
+	local rate
+
+	if className == "ROGUE" or (className == "DRUID" and GetShapeshiftForm(true) == 3) then
+		rate = 10 * OvaleAura.meleeHaste
+		if (className == "ROGUE") then
+			local rush = OvaleState:GetAura("player", 13750)
+			if rush and rush.stacks>0 then
+				rate = rate * 2
+			end
+		elseif withBerserker then
+			local berserk = OvaleState:GetAura("player", 50334)
+			if berserk and berserk.stacks>0 then
+				mana = mana/2
+			end
+		end
+	elseif className == "HUNTER" then
+		rate = 4 * OvaleAura.meleeHaste
+	else
+		rate = 0
+	end
+
+	return lastEnergyValue, lastEnergyTime, rate
+end
+
+local function GetManaTime(mana, withBerserker)
+	local lastEnergyValue, lastEnergyTime, rate = GetManaAndRate(withBerserker)
+
+	if rate > 0 then
+		local limit = math.ceil((mana - lastEnergyValue) / rate + lastEnergyTime)
+		return limit
+	else
+		if OvaleState.state.mana>=mana then
+			return OvaleState.currentTime-1
+		else
+			return nil
+		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
+local function GetTargetAura(condition, filter, 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 = (condition.mine == 1)
+
+	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:Print("ERROR: unknown buff "..spellId)
+		Ovale.bug = true
+		return 0,0
+	end
+
+	if not aura then
+		return 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
+			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
+	else
+		return 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 nil
+	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
+--</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,]]
+	-- Test how many armor set parts are equiped by the player
+	-- 1 : set number
+	-- 2 : "more" or "less"
+	-- 3 : limit
+	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,
+	attackPower = function(condition)
+		local base, posBuff, negBuff = UnitAttackPower("player")
+		return base + posBuff + negBuff, 0, 0
+	end,
+	BuffDuration = function(condition)
+		--local name, rank, icon, count, debuffType, duration = UnitBuff("player", OvaleData:GetSpellInfoOrNil(condition[1]))
+		--if not name then
+--			return nil
+	--	end
+		local start, ending = GetTargetAura(condition, "HELPFUL", getTarget(condition.target))
+		return compare(diffTime(start, ending), condition[2], condition[3])
+	end,
+	-- Test if a buff will expire on the player after a given time
+	-- 1 : buff spell id
+	-- 2 : expiration time
+	BuffExpires = function(condition)
+		local start, ending = GetTargetAura(condition, "HELPFUL", getTarget(condition.target))
+		local timeBefore = avecHate(condition[2], condition.haste)
+		if Ovale.trace then
+			Ovale:Print("timeBefore = " .. nilstring(timeBefore))
+			Ovale:Print("start = " .. nilstring(ending))
+		end
+		return addTime(ending, -timeBefore)
+	end,
+	buffExpires = function(condition)
+		local start, ending = GetTargetAura(condition, "HELPFUL", getTarget(condition.target))
+		if ending then
+			return ending - start, start, -1
+		else
+			return nil
+		end
+	end,
+	-- Test if a time has elapsed since the last buff gain
+	-- 1 : buff spell id
+	-- 2 : time since the buff gain
+	BuffGain = function(condition)
+		local spellId = condition[1]
+		local target = getTarget(condition.target)
+		if spellId then
+			local aura = OvaleState:GetAura(target,spellId)
+			if not aura then
+				return 0
+			end
+			local timeGain = aura.gain
+			if not timeGain then
+				timeGain = 0
+			end
+
+			return timeGain + condition[2]
+		end
+		return 0
+	end,
+	-- Test if a buff is active
+	-- 1 : the buff spell id
+	-- stacks : minimum number of stacks
+	BuffPresent = function(condition)
+		local start, ending = GetTargetAura(condition, "HELPFUL", getTarget(condition.target))
+		local timeBefore = avecHate(condition[2], condition.haste)
+		return start, addTime(ending, -timeBefore)
+	end,
+	BuffStealable = function(condition)
+		local i = 1
+		local stealable = false
+		local target = getTarget(condition.target)
+		while true do
+			local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable = UnitBuff(target, i)
+			if not name then
+				break
+			end
+			if isStealable then
+				stealable = true
+				break
+			end
+			i = i + 1
+		end
+		return testbool(stealable, condition[1])
+	end,
+	Casting = function(condition)
+		if OvaleState.currentSpellId == condition[1] then
+			return OvaleState.startCast, OvaleState.endCast
+		else
+			return nil
+		end
+	end,
+	CastTime = function(condition)
+		local name, rank, icon, cost, isFunnel, powerType, castTime = OvaleData:GetSpellInfoOrNil(condition[1])
+		if Ovale.trace then
+			Ovale:Print("castTime/1000 = " .. (castTime/1000) .. " " .. condition[2] .. " " .. condition[3])
+		end
+		return compare(castTime/1000, condition[2], condition[3])
+	end,
+	castTime = function(condition)
+		local name, rank, icon, cost, isFunnel, powerType, castTime = OvaleData:GetSpellInfoOrNil(condition[1])
+		return castTime/1000, 0, 0
+	end,
+	-- Test if a list of checkboxes is off
+	-- 1,... : the checkboxes names
+	CheckBoxOff = function(condition)
+		for k,v in pairs(condition) do
+			if (Ovale:IsChecked(v)) then
+				return nil
+			end
+		end
+		return 0
+	end,
+	-- Test if a list of checkboxes is on
+	-- 1,... : the checkboxes names
+	CheckBoxOn = function(condition)
+		for k,v in pairs(condition) do
+			if (not Ovale:IsChecked(v)) then
+				return nil
+			end
+		end
+		return 0
+	end,
+	Class = function(condition)
+		local loc, noloc = UnitClass(getTarget(condition.target))
+		return testbool(noloc == condition[1], condition[2])
+	end,
+	-- Test the target classification
+	-- 1 : normal, elite, or worldboss
+	Classification = function(condition)
+		local classification
+		local target = getTarget(condition.target)
+		if UnitLevel(target)==-1 then
+			classification = "worldboss"
+		else
+			classification = UnitClassification(target);
+			if (classification == "rareelite") then
+				classification = "elite"
+			elseif (classification == "rare") then
+				classification = "normal"
+			end
+		end
+
+		if (condition[1]==classification) then
+			return 0
+		else
+			return nil
+		end
+	end,
+	-- Test how many combo points a feral druid or a rogue has
+	-- 1 : "less" or "more"
+	-- 2 : the limit
+	ComboPoints = function(condition)
+		local points = OvaleState.state.combo
+		return compare(points, condition[1], condition[2])
+	end,
+	comboPoints = function(condition)
+		return OvaleState.state.combo, 0, 0
+	end,
+	Counter = function(condition)
+		return compare(OvaleState:GetCounterValue(condition[1]), condition[2], condition[3])
+	end,
+	counter = function(condition)
+		return OvaleState:GetCounterValue(condition[1]), 0, 0
+	end,
+	CreatureFamily = function(condition)
+		return testbool(UnitCreatureFamily(getTarget(condition.target)) == LBCT[condition[1]], condition[2])
+	end,
+	CreatureType = function(condition)
+		for _,v in pairs(condition) do
+			if (UnitCreatureType(getTarget(condition.target)) == LBCT[v]) then
+				return 0
+			end
+		end
+		return nil
+	end,
+	damage = function(condition)
+		local spellInfo = OvaleData:GetSpellInfo(condition[1])
+		if not spellInfo then
+			return nil
+		end
+		local ret = (spellInfo.base or 0)
+		if spellInfo.bonuscp then
+			ret = ret + (OvaleState.state.combo * spellInfo.bonuscp)
+		end
+		if spellInfo.bonusholy then
+			ret = ret + (OvaleState.state.holy * spellInfo.bonusholy)
+		end
+		if spellInfo.bonusap then
+			ret = ret + spellInfo.bonusap * UnitAttackPower("player")
+		end
+		if spellInfo.bonusapcp then
+			ret = ret + spellInfo.bonusapcp * UnitAttackPower("player") * OvaleState.state.combo
+		end
+		if spellInfo.bonusapholy then
+			ret = ret + spellInfo.bonusapholy * UnitAttackPower("player") * OvaleState.state.holy
+		end
+		if spellInfo.bonussp then
+			ret = ret + spellInfo.bonussp * GetSpellBonusDamage(2)
+		end
+		if spellInfo.bonusspholy then
+			ret = ret + spellInfo.bonusspholy * GetSpellBonusDamage(2) * OvaleState.state.holy
+		end
+		return ret * OvaleAura.damageMultiplier, 0, 0
+	end,
+	damageMultiplier = function(condition)
+		return OvaleAura.damageMultiplier, 0, 0
+	end,
+	DeadIn = function(condition)
+		local deadAt = getTargetDead(getTarget(condition.target))
+		if condition[1] == "more" then
+			return 0, addTime(deadAt, -condition[2])
+		else
+			return addTime(deadAt, -condition[2]), nil
+		end
+	end,
+	deadIn = function(condition)
+		return getTargetDead(getTarget(condition.target)), 0, -1
+	end,
+	-- Test if a debuff will expire on the target after a given time, or if there is less than the
+	-- given number of stacks (if stackable)
+	-- 1 : buff spell id
+	-- 2 : expiration time
+	-- stacks : how many stacks
+	-- mine : 1 means that if the debuff is not ours, the debuff is ignored
+	DebuffExpires = function(condition)
+		local start, ending = GetTargetAura(condition, "HARMFUL", getTarget(condition.target))
+		local tempsMax = avecHate(condition[2], condition.haste)
+		return addTime(ending, -tempsMax)
+	end,
+	debuffExpires = function(condition)
+		local start, ending = GetTargetAura(condition, "HARMFUL", getTarget(condition.target))
+		if ending then
+			return ending - start, start, -1
+		else
+			return nil
+		end
+	end,
+	DebuffPresent = function(condition)
+		local start, ending = GetTargetAura(condition, "HARMFUL", getTarget(condition.target))
+		local timeBefore = avecHate(condition[2], condition.haste)
+		return start, addTime(ending, -timeBefore)
+	end,
+	Distance = function(condition)
+		if LRC then
+			local target = getTarget(condition.target)
+			local minRange, maxRange = LRC:GetRange(target)
+			if maxRange == nil or minRange == nil then
+				return nil
+			end
+			if condition[1] == "more" then
+				if condition[2]~=nil and maxRange>condition[2] then
+					return 0
+				else
+					return nil
+				end
+			else
+				if condition[2]~=nil and minRange<condition[2] then
+					return 0
+				else
+					return nil
+				end
+			end
+		end
+	end,
+	distance = function(condition)
+		if LRC then
+			return LRC:GetRange(getTarget(condition.target))
+		else
+			return nil
+		end
+	end,
+	--Compare to eclipse power. <0 lunar, >0 solar
+	Eclipse = function(condition)
+		return compare(OvaleState.state.eclipse, condition[1], condition[2])
+	end,
+	eclipse = function(condition)
+		return OvaleState.state.eclipse
+	end,
+	EffectiveMana = function(condition)
+		local limit = GetManaTime(condition[2], true)
+		if condition[1]=="more" then
+			return limit, nil
+		else
+			return 0,limit
+		end
+	end,
+	effectiveMana = function(condition)
+		return GetManaAndRate(true)
+	end,
+	EndCastTime = function(condition)
+		local name, rank, icon, cost, isFunnel, powerType, castTime = OvaleData:GetSpellInfoOrNil(condition[1])
+		local actionCooldownStart, actionCooldownDuration, actionEnable = OvaleData:GetComputedSpellCD(condition[1])
+		local startCast = actionCooldownStart + actionCooldownDuration
+		if startCast<OvaleState.currentTime then
+			startCast = OvaleState.currentTime
+		end
+		return startCast + castTime/1000
+	end,
+	enemies = function(condition)
+		return OvaleEnemies:GetNumberOfEnemies(), 0, 0
+	end,
+	Exists = function(condition)
+		return testbool(UnitExists(getTarget(condition.target)) == 1, condition[1])
+	end,
+	Glyph = function(condition)
+		local present = false
+		for i = 1, GetNumGlyphSockets() do
+			local enabled, glypType, glyphTooltipIndex, glyphSpellID = GetGlyphSocketInfo(i)
+			if (glyphSpellID == condition[1]) then
+				present = true
+				break
+			end
+		end
+		return testbool(present, condition[2])
+	end,
+	HasFullControl = function(condition)
+		return testbool(HasFullControl(), condition[1])
+	end,
+	HasShield = function(condition)
+		local _,_,id = string.find(GetInventoryItemLink("player",GetInventorySlotInfo("SecondaryHandSlot")) or "","(item:%d+:%d+:%d+:%d+)")
+		if (not id) then
+			return testbool(false, condition[1])
+		end
+
+		local _,_,_,_,_,_,_,_,itemLoc = GetItemInfo(id)
+		return testbool(itemLoc=="INVTYPE_SHIELD", condition[1])
+	end,
+	HolyPower = function(condition)
+		return compare(OvaleState.state.holy, condition[1], condition[2])
+	end,
+	holyPower = function(condition)
+		return OvaleState.state.holy, 0, 0
+	end,
+	InCombat = function(condition)
+		return testbool(Ovale.enCombat, condition[1])
+	end,
+	InRange = function(condition)
+		local spellName = GetSpellInfo(condition[1])
+		return testbool(IsSpellInRange(spellName,getTarget(condition.target))==1,condition[2])
+	end,
+	item = function(condition)
+		local itemId = element.params[1]
+		local actionCooldownStart, actionCooldownDuration, actionEnable = GetItemCooldown(itemId)
+		return actionCooldownDuration, actionCooldownStart, -1
+	end,
+	ItemCount = function(condition)
+		if condition.charges == 1 then
+			return compare(GetItemCount(condition[1], false, true), condition[2], condition[3])
+		else
+			return compare(GetItemCount(condition[1]), condition[2], condition[3])
+		end
+	end,
+	itemCount = function(condition)
+		if condition.charges == 1 then
+			return GetItemCount(condition[1], false, true), 0, 0
+		else
+			return GetItemCount(condition[1]), 0, 0
+		end
+	end,
+	IsCasting = function(condition)
+		local casting
+		local target = getTarget(condition.target)
+		local spellId = condition.spell
+		if not spellId then
+			return testbool(UnitCastingInfo(target) or UnitChannelInfo(target), condition[1])
+		elseif type(spellId) == "number" then
+			local spellName = GetSpellInfo(spellId)
+			return testbool(UnitCastingInfo(target)==spellName or UnitChannelInfo(target) == spellName, condition[1])
+		elseif OvaleData.buffSpellList[spellId] then
+			local castSpellName = UnitCastingInfo(target) or UnitChannelInfo(target)
+			local found = false
+			for k,v in pairs(OvaleData.buffSpellList[spellId]) do
+				local spellName = GetSpellInfo(v)
+				if spellName == castSpellName then
+					found = true
+					break
+				end
+			end
+			return testbool(found, condition[1])
+		elseif spellId == "harmful" then
+			local castSpellName = UnitCastingInfo(target) or UnitChannelInfo(target)
+			return testbool(castSpellName and IsHarmfulSpell(castSpellName), condition[1])
+		elseif spellId == "helpful" then
+			local castSpellName = UnitCastingInfo(target) or UnitChannelInfo(target)
+			return testbool(castSpellName and IsHelpfulSpell(castSpellName), condition[1])
+		end
+	end,
+	IsFeared = function(condition)
+		local fearSpellList = OvaleData:GetFearSpellList()
+		return testbool(not HasFullControl() and isDebuffInList(fearSpellList), condition[1])
+	end,
+	IsFriend = function(condition)
+		return testbool(UnitIsFriend("player", getTarget(condition.target)), condition[1])
+	end,
+	IsIncapacitated = function(condition)
+		local incapacitateSpellList = OvaleData:GetIncapacitateSpellList()
+		return testbool(not HasFullControl() and isDebuffInList(incapacitateSpellList), condition[1])
+	end,
+	IsInterruptible = function(condition)
+		local target = getTarget(condition.target)
+		local spell, rank, name, icon, start, ending, isTradeSkill, castID, protected = UnitCastingInfo(target)
+		if not spell then
+			spell, rank, name, icon, start, ending, isTradeSkill, protected = UnitChannelInfo(target)
+		end
+		return testbool(protected ~= nil and not protected, condition[1])
+	end,
+	IsRooted = function(condition)
+		local rootSpellList = OvaleData:GetRootSpellList()
+		return testbool(isDebuffInList(rootSpellList), condition[1])
+	end,
+	IsStunned = function(condition)
+		local stunSpellList = OvaleData:GetStunSpellList()
+		return testbool(not HasFullControl() and isDebuffInList(stunSpellList), condition[1])
+	end,
+	LastSpellDamage = function(condition)
+		local spellId = condition[1]
+		if not OvaleSpellDamage:Get(spellId) then
+			return nil
+		end
+		return compare(OvaleSpellDamage:Get(spellId), condition[2], condition[3])
+	end,
+	lastSpellDamage = function(condition)
+		return OvaleSpellDamage:Get(condition[1])
+	end,
+	lastSpellDamageMultiplier = function(condition)
+		return OvaleFuture.lastSpellDM[condition[1]], 0, 0
+	end,
+	lastSpellAttackPower = function(condition)
+		return OvaleFuture.lastSpellAP[condition[1]], 0, 0
+	end,
+	lastSpellSpellPower = function(condition)
+		return OvaleFuture.lastSpellSP[condition[1]], 0, 0
+	end,
+	LastSwing = function(condition)
+		local ret = OvaleSwing:GetLast(condition[1])
+		if condition[2] and ret then
+			ret = ret + condition[2]
+		end
+		return 0, ret
+	end,
+	lastSwing = function(condition)
+		return OvaleState.currentTime - OvaleSwing:GetLast(condition[1]), 0, 1
+	end,
+	-- Compare with the player level
+	-- 1 : "less" or "more"
+	-- 2 : the limit
+	Level = function(condition)
+		return compare(UnitLevel(getTarget(condition.target)), condition[1], condition[2])
+	end,
+	level = function(condition)
+		return UnitLevel(condition.target)
+	end,
+	Life = function(condition)
+		local target = getTarget(condition.target)
+		return compare(UnitHealth(target), condition[1], condition[2])
+	end,
+	life = function(condition)
+		local target = getTarget(condition.target)
+		return UnitHealth(target), 0, 0
+	end,
+	LifeMissing = function(condition)
+		local target = getTarget(condition.target)
+		return compare(UnitHealthMax(target)-UnitHealth(target), condition[1], condition[2])
+	end,
+	lifeMissing = function(condition)
+		local target = getTarget(condition.target)
+		return UnitHealthMax(target)-UnitHealth(target), 0, 0
+	end,
+	-- Test if the player life is bellow/above a given value in percent
+	-- 1 : "less" or "more"
+	-- 2 : the limit, in percent
+	LifePercent = function(condition)
+		local target = getTarget(condition.target)
+		if UnitHealthMax(target) == nil or UnitHealthMax(target) == 0 then
+			return nil
+		end
+		return compare(UnitHealth(target)/UnitHealthMax(target), condition[1], condition[2]/100)
+	end,
+	lifePercent = function(condition)
+		local target = getTarget(condition.target)
+		if UnitHealthMax(target) == nil or UnitHealthMax(target) == 0 then
+			return nil
+		end
+		return 100 * UnitHealth(target)/UnitHealthMax(target), 0, 0
+	end,
+	-- Test if a list item is selected
+	-- 1 : the list name
+	-- 2 : the item name
+	List = function(condition)
+		if (condition[1]) then
+			if (Ovale:GetListValue(condition[1]) == condition[2]) then
+				return 0
+			end
+		end
+		return nil
+	end,
+	-- Test if the player mana is above/bellow a given value
+	-- 1 : "less" or "more"
+	-- 2 : the mana/energy/rage... limit
+	Mana = function(condition)
+		local target = getTarget(condition.target)
+		if target == "player" then
+			local limit = GetManaTime(condition[2], false)
+			if condition[1]=="more" then
+				return limit, nil
+			else
+				return 0,limit
+			end
+		else
+			return compare(UnitPower(target), condition[1], condition[2])
+		end
+	end,
+	mana = function(condition)
+		return GetManaAndRate(false)
+	end,
+	ManaPercent = function(condition)
+		local target = getTarget(condition.target)
+		if UnitPowerMax(target) == 0 then
+			return nil
+		end
+		return compare(UnitPower(target)/UnitPowerMax(target), condition[1], condition[2]/100)
+	end,
+	manaPercent = function(condition)
+		local target = getTarget(condition.target)
+		if UnitPowerMax(target) == 0 then
+			return nil
+		end
+		local value, t, rate = GetManaAndRate(false)
+		local conversion = 100/UnitPowerMax(target)
+		return value * conversion, t, rate * conversion
+	end,
+	MaxHealth = function(condition)
+		local target = getTarget(condition.target)
+		return compare(UnitMaxHealth(target), condition[1], condition[2])
+	end,
+	maxHealth = function(condition)
+		return UnitMaxHealth(getTarget(condition.target)), 0, 0
+	end,
+	maxMana = function(condition)
+		return UnitPowerMax(getTarget(condition.target)), 0, 0
+	end,
+	NextSwing = function(condition)
+		local ret = OvaleSwing:GetNext(condition[1])
+		if condition[2] and ret then
+			ret = ret - condition[2]
+		end
+		return ret
+	end,
+	nextSwing = function(condition)
+		return OvaleSwing:GetNext(condition[1]) - OvaleState.currentTime, 0, -1
+	end,
+	nextTick = function(condition)
+		local start, ending = GetTargetAura(condition, "HARMFUL", getTarget(condition.target))
+		local si = OvaleData.spellInfo[condition[1]]
+		if not si or not si.duration then
+			return nil
+		end
+		local ticks = floor(OvaleAura.spellHaste * (si.duration/(si.tick or 3)) + 0.5)
+		local tickLength = (ending - start) / ticks
+		local tickTime = start + tickLength
+		for i=1,ticks do
+			if OvaleState.currentTime<=tickTime then
+				break
+			end
+			tickTime = tickTime + tickLength
+		end
+		return tickTime, 0, -1
+	end,
+	OtherDebuffExpires = function(condition)
+		local minTime, maxTime = getOtherAura(condition[1], condition[3])
+		if minTime then
+			local timeBefore = condition[2] or 0
+			return minTime - timeBefore, nil
+		end
+		return 0, nil
+	end,
+	OtherDebuffPresent = function(condition)
+		local minTime, maxTime = getOtherAura(condition[1], condition[3])
+		if maxTime and maxTime>0 then
+			local timeBefore = condition[2] or 0
+			return 0, addTime(maxTime, -timeBefore)
+		end
+		return nil
+	end,
+	OtherAuraExpires = OtherDebuffExpires,
+	OtherAuraPresent = OtherDebuffPresent,
+	otherAura = function(condition)
+		local minTime, maxTime = getOtherAura(condition[1])
+		return 0, maxTime, -1
+	end,
+	Present = function(condition)
+		local present = UnitExists(getTarget(condition.target)) and not UnitIsDead(getTarget(condition.target))
+		return testbool(present, condition[1])
+	end,
+	-- Test if any player pet is present (or not)
+	-- 1 : "yes" or "no"
+	PetPresent = function(condition)
+		local present = UnitExists("pet") and not UnitIsDead("pet")
+		return testbool(present, condition[1])
+	end,
+	-- Test the target level difference with the player
+	-- 1 : "less" or "more"
+	-- 2 : [target level]-[player level] limit
+	RelativeLevel = function(condition)
+		local difference
+		local target = getTarget(condition.target)
+		if UnitLevel(target) == -1 then
+			difference = 3
+		else
+			difference = UnitLevel(target) - UnitLevel("player")
+		end
+
+		return compare(difference, condition[1], condition[2])
+	end,
+	remainingCastTime = function(condition)
+		local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible = UnitCastingInfo(getTarget(condition.target))
+		if not endTime then
+			return nil
+		end
+		return 0, endTime/1000, -1
+	end,
+	Runes = function(condition)
+		return GetRune(condition)
+	end,
+	runes = function(condition)
+		local ret = GetRune(condition)
+		if not ret then
+			return nil
+		end
+		if ret < OvaleState.maintenant then
+			ret = OvaleState.maintenant
+		end
+		return 0, ret, -1
+	end,
+	SoulShards = function(condition)
+		return compare(OvaleState.state.shard, condition[1], condition[2])
+	end,
+	soulShards = function(condition)
+		return OvaleState.state.shard
+	end,
+	Speed = function(condition)
+		return compare(GetUnitSpeed(getTarget(condition.target))*100/7, condition[1], condition[2])
+	end,
+	speed = function(condition)
+		return GetUnitSpeed(getTarget(condition.target))*100/7
+	end,
+	spell = function(condition)
+		local actionCooldownStart, actionCooldownDuration, actionEnable = OvaleData:GetComputedSpellCD(condition[1])
+		return actionCooldownDuration, actionCooldownStart, -1
+	end,
+	spellPower = function(condition)
+		return GetSpellBonusDamage(2), 0, 0
+	end,
+	-- Test if the player is in a given stance
+	-- 1 : the stance
+	Stance = function(condition)
+		if (GetShapeshiftForm(true) == condition[1]) then
+			return 0
+		else
+			return nil
+		end
+	end,
+	Stealthed = function(condition)
+		return testbool(IsStealthed(), condition[1])
+	end,
+	-- Test how many talent points has been spent in a talent
+	-- 1 : the talent identifier (use /script print(OvaleData.talentNameToId["Talent name"]) to retreive)
+	-- 2 : "more" or "less"
+	-- 3 : the limit
+	TalentPoints = function(condition)
+		return compare(OvaleData:GetTalentPoints(condition[1]), condition[2], condition[3])
+	end,
+	talentPoints = function(condition)
+		return OvaleData:GetTalentPoints(condition[1]), 0, 0
+	end,
+	-- Test if the target's target is the player (or is not)
+	-- 1 : "yes" (it should be the player) or "no"
+	TargetIsPlayer = function(condition)
+		return testbool(UnitIsUnit("player",getTarget(condition.target).."target"), condition[1])
+	end,
+	Threat = function(condition)
+		local isTanking, status, threatpct = UnitDetailedThreatSituation("player", getTarget(condition.target))
+		return compare(threatpct, condition[1], condition[2])
+	end,
+	threat = function(condition)
+		local isTanking, status, threatpct = UnitDetailedThreatSituation("player", getTarget(condition.target))
+		return threatpct
+	end,
+	TimeInCombat = function(condition)
+		if not Ovale.combatStartTime then
+			return nil
+		elseif condition[1] == "more" then
+			return Ovale.combatStartTime + condition[2]
+		else
+			return 0, Ovale.combatStartTime + condition[2]
+		end
+	end,
+	timeInCombat = function(condition)
+		return OvaleState.maintenant - Ovale.combatStartTime, OvaleState.maintenant, 1
+	end,
+	timeToDie = function(condition)
+		return 0, getTargetDead(getTarget(condition.target)), -1
+	end,
+	timeWithHaste = function(condition)
+		return avecHate(condition[1], "spell"),0,0
+	end,
+	TotemExpires = function(condition)
+		if type(condition[1]) ~= "number" then
+			condition[1] = totemType[condition[1]]
+		end
+
+		local haveTotem, totemName, startTime, duration = GetTotemInfo(condition[1])
+		if not startTime then
+			return 0
+		end
+		if (condition.totem and OvaleData:GetSpellInfoOrNil(condition.totem)~=totemName) then
+			return 0
+		end
+		return addTime(startTime + duration, -(condition[2] or 0))
+	end,
+	TotemPresent = function(condition)
+		if type(condition[1]) ~= "number" then
+			condition[1] = totemType[condition[1]]
+		end
+
+		local haveTotem, totemName, startTime, duration = GetTotemInfo(condition[1])
+		if not startTime then
+			return nil
+		end
+		if (condition.totem and OvaleData:GetSpellInfoOrNil(condition.totem)~=totemName) then
+			return nil
+		end
+		return startTime, startTime + duration
+	end,
+	Tracking = function(condition)
+		local what = OvaleData:GetSpellInfoOrNil(condition[1])
+		local numTrackingTypes = GetNumTrackingTypes()
+		local present = false
+		for i=1,numTrackingTypes do
+			local name, texture, active = GetTrackingInfo(i)
+			if name == what then
+				present = (active == 1)
+				break
+			end
+		end
+		return testbool(present, condition[2])
+	end,
+	WeaponEnchantExpires = function(condition)
+		local hasMainHandEnchant, mainHandExpiration, mainHandCharges, hasOffHandEnchant, offHandExpiration, offHandCharges = GetWeaponEnchantInfo()
+		if (condition[1] == "mainhand") then
+			if (not hasMainHandEnchant) then
+				return 0
+			end
+			mainHandExpiration = mainHandExpiration/1000
+			if ((condition[2] or 0) >= mainHandExpiration) then
+				return 0
+			else
+				return OvaleState.maintenant + mainHandExpiration - condition[2]
+			end
+		else
+			if (not hasOffHandEnchant) then
+				return 0
+			end
+			offHandExpiration = offHandExpiration/1000
+			if ((condition[2] or 0) >= offHandExpiration) then
+				return 0
+			else
+				return OvaleState.maintenant + offHandExpiration - condition[2]
+			end
+		end
+	end,
+}
+
+OvaleCondition.conditions.health = OvaleCondition.conditions.life
+OvaleCondition.conditions.Health = OvaleCondition.conditions.Life
+OvaleCondition.conditions.healthPercent = OvaleCondition.conditions.lifePercent
+OvaleCondition.conditions.HealthPercent = OvaleCondition.conditions.LifePercent
+OvaleCondition.conditions.HealthMissing = OvaleCondition.conditions.LifeMissing
+OvaleCondition.defaultTarget = "target"
+
+--</public-static-properties>
diff --git a/OvaleData.lua b/OvaleData.lua
new file mode 100644
index 0000000..65ec761
--- /dev/null
+++ b/OvaleData.lua
@@ -0,0 +1,427 @@
+OvaleData = LibStub("AceAddon-3.0"):NewAddon("OvaleData", "AceEvent-3.0")
+
+--<public-static-properties>
+OvaleData.spellList = {}
+OvaleData.firstInit = false
+OvaleData.className = nil
+OvaleData.spellInfo = {}
+--allows to fill the player talent tables on first use
+OvaleData.listeTalentsRemplie = false
+--key: talentId / value: points in this talent
+OvaleData.pointsTalent = {}
+--key: talentId / value: talent name (not used)
+OvaleData.talentIdToName = {}
+--key: talent name / value: talent id
+OvaleData.talentNameToId = {}
+--spell info from the current script (by spellId)
+OvaleData.spellInfo = {}
+--spells that count for scoring
+OvaleData.scoreSpell = {}
+
+-- List haste buff that does not appear in the character sheet and that are not raid wide buffs
+OvaleData.selfHasteBuff =
+{
+	[53657] = 9, -- Judgement of the pure
+	[49016] = 20 -- Unholy Frenzy
+}
+
+-- List temporary damage multiplier
+OvaleData.selfDamageBuff =
+{
+	[5217] = 1.15, -- Tiger's fury
+	[57933] = 1.15 -- Tricks of the trade
+}
+
+OvaleData.buffSpellList =
+{
+	fear =
+	{
+		5782, -- Fear
+		5484, -- Howl of terror
+		5246, -- Intimidating Shout
+		8122, -- Psychic scream
+	},
+	root =
+	{
+		23694, -- Improved Hamstring
+		339, -- Entangling Roots
+		122, -- Frost Nova
+		47168, -- Improved Wing Clip
+	},
+	incapacitate =
+	{
+		6770, -- Sap
+		12540, -- Gouge
+		20066, -- Repentance
+	},
+	stun =
+	{
+		5211, -- Bash
+		44415, -- Blackout
+		6409, -- Cheap Shot
+		22427, -- Concussion Blow
+		853, -- Hammer of Justice
+		408, -- Kidney Shot
+		46968, -- Shockwave
+	},
+	strengthagility=
+	{
+		6673, -- Battle Shout
+		8076, -- Strength of Earth
+		57330, -- Horn of Winter
+		93435 --Roar of Courage (Cat, Spirit Beast)
+	},
+	stamina =
+	{
+		21562, -- Fortitude TODO: vérifier
+		469, -- Commanding Shout
+		6307, -- Blood Pact
+		90364 -- Qiraji Fortitude
+	},
+	lowerarmor=
+	{
+		58567, -- Sunder Armor (x3)
+		8647, -- Expose Armor
+		91565, -- Faerie Fire (x3)
+		35387, --Corrosive Spit (x3 Serpent)
+		50498 --Tear Armor (x3 Raptor)
+	},
+	magicaldamagetaken=
+	{
+		65142, -- Ebon Plague
+		60433, -- Earth and Moon
+		93068, -- Master Poisoner
+		1490, -- Curse of the Elements
+		85547, -- Jinx 1
+		86105, -- Jinx 2
+		34889, --Fire Breath (Dragonhawk)
+		24844 --Lightning Breath (Wind serpent)
+	},
+	magicalcrittaken=
+    {
+        17800, -- Shadow and Flame
+        22959 -- Critical Mass
+    },
+	physicaldamagetaken=
+	{
+		30069, -- Blood Frenzy (rank 1)
+		30070, -- Blood Frenzy (rank 2)
+		81327, -- Brittle Bones (rank 1)
+		81328, -- Brittle Bones (rank 2)
+		58684, -- Savage Combat (rank 1)
+		58683, -- Savage Combat (rank 2)
+		55749, -- Acid Spit (Worm)
+		50518, -- Ravage (Ravager)
+	},
+	lowerphysicaldamage=
+	{
+		99, -- Demoralizing Roar
+		702, -- Curse of Weakness
+		1160, -- Demoralizing Shout
+		26017, -- Vindication
+		81130, -- Scarlet Fever
+		50256, --Demoralizing Roar (Bear)
+		24423, -- Demoralizing Screech (Carrion Bird)
+	},
+	meleeslow=
+	{
+		55095, --Icy Touch
+		58179, --Infected Wounds rank 1
+		58180, --Infected Wounds rank 2
+		68055, --Judgments of the just
+		6343, --Thunderclap
+		8042, --Earth Shock
+		54404, --Dust Cloud (Tallstrider)
+		90315, -- Tailspin (Fox)
+	},
+	castslow =
+	{
+		1714, --Curse of Tongues
+        58604, --Lava Breath (Core Hound)
+        50274, --Spore Cloud (Sporebat)
+        5761, --Mind-numbing Poison
+        73975, --Necrotic Strike
+        31589 --Slow
+	},
+	bleed=
+	{
+		33876, --Mangle cat
+		33878, --Mangle bear
+		46856, -- Trauma rank 1
+		46857, -- Trauma rank 2
+		16511, --Hemorrhage
+		50271, --Tendon Rip (Hyena)
+		35290 --Gore (Boar)
+	},
+	heroism=
+	{
+		2825, --Bloodlust
+		32182, --Heroism
+		80353, --Time warp
+		90355 -- Ancient Hysteria (Core Hound)
+	},
+	meleehaste =
+	{
+		8515, -- Windfury
+		55610, -- Improved Icy Talons
+		53290 -- Hunting Party
+	},
+	spellhaste =
+	{
+		24907, -- Moonkin aura
+		2895, -- Wrath of Air Totem
+		49868 -- Mind Quickening
+	},
+	enrage =
+	{
+		49016, -- Unholy Frenzy
+		18499, -- Berserker Rage
+		12292, -- Death Wish
+		12880, -- Enrage (rank 1)
+		14201, -- Enrage (rank 2)
+		14202, -- Enrage (rank 3)
+		5229, -- Enrage (Bear)
+        52610, -- Savage Roar (Cat)
+        76691, -- Vengeance (All Tank Specs)
+	},
+	criticalstrike =
+	{
+		51740, -- Elemental Oath
+		51698, -- Honor Among Thieves (rank 1)
+		51700, -- Honor Among Thieves (rank 2)
+		51701, -- Honor Among Thieves (rank 3)
+		17007, -- Leader of the Pack
+		29801, -- Rampage
+		24604, -- Furious Howl (Wolf)
+		90309, -- Terrifying Roar (Devilsaur)
+	}
+}
+--</public-static-properties>
+
+--<private-static-properties>
+local fearSpellList = nil
+local stunSpellList = nil
+local incapacitateSpellList = nil
+local rootSpellList = nil
+--</private-static-properties>
+
+--<public-static-methods>
+function OvaleData:OnEnable()
+	self:FirstInit()
+    self:RegisterEvent("PLAYER_TALENT_UPDATE")
+    self:RegisterEvent("CHARACTER_POINTS_CHANGED")
+	self:RegisterEvent("SPELLS_CHANGED")
+end
+
+function OvaleData:OnDisable()
+    self:UnregisterEvent("SPELLS_CHANGED")
+    self:UnregisterEvent("PLAYER_TALENT_UPDATE")
+    self:UnregisterEvent("CHARACTER_POINTS_CHANGED")
+end
+
+function OvaleData:CHARACTER_POINTS_CHANGED()
+	self:RemplirListeTalents()
+--	Ovale:Print("CHARACTER_POINTS_CHANGED")
+end
+
+function OvaleData:PLAYER_TALENT_UPDATE()
+	self:RemplirListeTalents()
+--	Ovale:Print("PLAYER_TALENT_UPDATE")
+end
+
+--The user learnt a new spell
+function OvaleData:SPELLS_CHANGED()
+	self:FillSpellList()
+	Ovale.needCompile = true
+end
+
+function OvaleData:GetRootSpellList()
+	if rootSpellList then
+		return rootSpellList
+	end
+	rootSpellList = {}
+	for k, v in pairs(self.buffSpellList.fear) do
+		rootSpellList[v] = true
+	end
+	return rootSpellList
+end
+
+function OvaleData:GetStunSpellList()
+	if stunSpellList then
+		return stunSpellList
+	end
+	stunSpellList = {}
+	for k, v in pairs(self.buffSpellList.stun) do
+		stunListList[v] = true
+	end
+	return stunSpellList
+end
+
+function OvaleData:GetIncapacitateSpellList()
+	if incapacitateSpellList then
+		return incapacitateSpellList
+	end
+	incapacitateSpellList = {}
+	for k, v in pairs(self.buffSpellList.incapacitate) do
+		incapacitateSpellList[v] = true
+	end
+	return incapacitateSpellList
+end
+
+function OvaleData:GetFearSpellList()
+	if fearSpellList then
+		return fearSpellList
+	end
+	fearSpellList = {}
+	for k, v in pairs(self.buffSpellList.fear) do
+		fearSpellList[v] = true
+	end
+	return fearSpellList
+end
+
+
+function OvaleData:GetSpellInfoOrNil(spell)
+	if (spell) then
+		return GetSpellInfo(spell)
+	else
+		return nil
+	end
+end
+
+function OvaleData:FillSpellList()
+	self.spellList = {}
+	local book=BOOKTYPE_SPELL
+	while true do
+		local i=1
+		while true do
+			local skillType, spellId = GetSpellBookItemInfo(i, book)
+			if not spellId then
+				break
+			end
+			if skillType~="FUTURESPELL" then
+				local spellName = GetSpellBookItemName(i, book)
+				self.spellList[spellId] = spellName
+			end
+			i = i + 1
+		end
+		if book==BOOKTYPE_SPELL then
+			book = BOOKTYPE_PET
+		else
+			break
+		end
+	end
+end
+
+function OvaleData:RemplirListeTalents()
+	local numTabs = GetNumTalentTabs();
+	for t=1, numTabs do
+		local numTalents = GetNumTalents(t);
+		for i=1, numTalents do
+			local nameTalent, icon, tier, column, currRank, maxRank = GetTalentInfo(t,i);
+			local link = GetTalentLink(t,i)
+			if link then
+				local a, b, talentId = string.find(link, "talent:(%d+)");
+				talentId = tonumber(talentId)
+				self.talentIdToName[talentId] = nameTalent
+				self.talentNameToId[nameTalent] = talentId
+				self.pointsTalent[talentId] = currRank
+				self.listeTalentsRemplie = true
+				Ovale.needCompile = true
+			end
+		end
+	end
+end
+
+function OvaleData:FirstInit()
+	if self.firstInit then
+		return
+	end
+
+	self.firstInit = true
+
+	local playerClass, englishClass = UnitClass("player")
+	self.className = englishClass
+
+	self:RemplirListeTalents()
+	self:FillSpellList()
+end
+
+function OvaleData:GetTalentPoints(talentId)
+	if not self.listeTalentsRemplie then
+		self:RemplirListeTalents()
+	end
+	return self.pointsTalent[talentId]
+end
+
+function OvaleData:GetSpellInfo(spellId)
+	if (not self.spellInfo[spellId]) then
+		self.spellInfo[spellId] = { aura = {player = {}, target = {}} }
+	end
+	return self.spellInfo[spellId]
+end
+
+function OvaleData:ResetSpellInfo()
+	self.spellInfo = {}
+end
+
+function OvaleData:GetGCD(spellId)
+	if spellId and self.spellInfo[spellId] then
+		if self.spellInfo[spellId].haste == "spell" then
+			local cd = self.spellInfo[spellId].gcd
+			if not cd then
+				cd = 1.5
+			end
+			cd = cd / OvaleAura.spellHaste
+			if (cd<1) then
+				cd = 1
+			end
+			return cd
+		elseif self.spellInfo[spellId].gcd then
+			return self.spellInfo[spellId].gcd
+		end
+	end
+
+	-- Default value
+	if self.className == "ROGUE" or (self.className == "DRUID" and GetShapeshiftForm(true) == 3) then
+		return 1.0
+	elseif self.className == "MAGE" or self.className == "WARLOCK" or self.className == "PRIEST" or
+			(self.className == "DRUID" and GetShapeshiftForm(true) ~= 1) then
+		local cd = 1.5 / OvaleAura.spellHaste
+		if (cd<1) then
+			cd = 1
+		end
+		return cd
+	else
+		return 1.5
+	end
+end
+
+
+--Compute the spell Cooldown
+function OvaleData:GetComputedSpellCD(spellId)
+	local actionCooldownStart, actionCooldownDuration, actionEnable
+	local cd = OvaleState:GetCD(spellId)
+	if cd and cd.start then
+		actionCooldownStart = cd.start
+		actionCooldownDuration = cd.duration
+		actionEnable = cd.enable
+	else
+		actionCooldownStart, actionCooldownDuration, actionEnable = GetSpellCooldown(spellId)
+		-- Les chevaliers de la mort ont des infos fausses sur le CD quand ils n'ont plus les runes
+		-- On force à 1,5s ou 1s en présence impie
+		if self.className=="DEATHKNIGHT" and actionCooldownDuration==10 and
+				(not self.spellInfo[spellId] or self.spellInfo[spellId].cd~=10) then
+			local impie = GetSpellInfo(48265)
+			if impie and UnitBuff("player", impie) then
+				actionCooldownDuration=1
+			else
+				actionCooldownDuration=1.5
+			end
+		end
+		if self.spellInfo[spellId] and self.spellInfo[spellId].forcecd then
+			actionCooldownStart, actionCooldownDuration = GetSpellCooldown(self.spellInfo[spellId].forcecd)
+		end
+	end
+	return actionCooldownStart, actionCooldownDuration, actionEnable
+end
+--</public-static-methods>
diff --git a/OvaleEnemies.lua b/OvaleEnemies.lua
new file mode 100644
index 0000000..322f346
--- /dev/null
+++ b/OvaleEnemies.lua
@@ -0,0 +1,65 @@
+-- Gather information about ennemies
+
+OvaleEnemies = LibStub("AceAddon-3.0"):NewAddon("OvaleEnemies", "AceEvent-3.0")
+
+--<public-static-properties>
+OvaleEnemies.numberOfEnemies = 0
+OvaleEnemies.enemies = {}
+--</public-static-properties>
+
+--<public-static-methods>
+-- Events
+function OvaleEnemies:OnEnable()
+	self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
+	self:RegisterEvent("PLAYER_REGEN_DISABLED")
+end
+
+function OvaleEnemies:OnDisable()
+	self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
+	self:UnregisterEvent("PLAYER_REGEN_DISABLED")
+end
+
+function OvaleEnemies:COMBAT_LOG_EVENT_UNFILTERED(event, ...)
+	local time, event, hideCaster, sourceGUID, sourceName, sourceFlags, sourceRaidFlags, destGUID, destName, destFlags, destRaidFlags = select(1, ...)
+
+	if event == "UNIT_DIED" then
+		for k,v in pairs(self.enemies) do
+			if k==destGUID then
+				self.enemies[v] = nil
+				self.numberOfEnemies = self.numberOfEnemies - 1
+				Ovale.refreshNeeded["player"] = true
+				--Ovale:Print("enemy die")
+			end
+		end
+	elseif sourceFlags and not self.enemies[sourceGUID] and bit.band(sourceFlags, COMBATLOG_OBJECT_REACTION_HOSTILE)>0
+				and bit.band(sourceFlags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) > 0 and
+			destFlags and bit.band(destFlags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) == 0 then
+		self.enemies[sourceGUID] = true
+		--Ovale:Print("new ennemy source=".. sourceName)
+		self.numberOfEnemies = self.numberOfEnemies + 1
+		Ovale.refreshNeeded["player"] = true
+	elseif destGUID and not self.enemies[destGUID] and bit.band(destFlags, COMBATLOG_OBJECT_REACTION_HOSTILE)>0
+				and bit.band(destFlags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) > 0 and
+			sourceFlags and bit.band(sourceFlags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) == 0 then
+		self.enemies[destGUID] = true
+		--Ovale:Print("new ennemy dest=".. destName)
+		self.numberOfEnemies = self.numberOfEnemies + 1
+		Ovale.refreshNeeded["player"] = true
+	end
+end
+
+function OvaleEnemies:PLAYER_REGEN_DISABLED()
+	if self.numberOfEnemies then
+		self.numberOfEnemies = 0
+		self.enemies = {}
+	end
+end
+
+function OvaleEnemies:GetNumberOfEnemies()
+	if not self.numberOfEnemies then
+		self.numberOfEnemies = 0
+	end
+	return self.numberOfEnemies
+end
+--</public-static-methods>
+
diff --git a/OvaleEquipement.lua b/OvaleEquipement.lua
index 5da7bed..7eead27 100644
--- a/OvaleEquipement.lua
+++ b/OvaleEquipement.lua
@@ -1,6 +1,10 @@
 OvaleEquipement = LibStub("AceAddon-3.0"):NewAddon("OvaleEquipement", "AceEvent-3.0")

+--<public-static-properties>
 OvaleEquipement.nombre = {}
+--</public-static-properties>
+
+--<public-static-methods>

 function OvaleEquipement:OnEnable()
 	self:RegisterEvent("UNIT_INVENTORY_CHANGED")
@@ -265,4 +269,7 @@ end

 function OvaleEquipement:PLAYER_ENTERING_WORLD(event)
 	self:Refresh()
-end
\ No newline at end of file
+end
+
+--</public-static-methods>
+
diff --git a/OvaleFrame.lua b/OvaleFrame.lua
index 6e45dff..e4f0121 100644
--- a/OvaleFrame.lua
+++ b/OvaleFrame.lua
@@ -1,60 +1,56 @@
-local AceGUI = LibStub("AceGUI-3.0")
-local Masque = LibStub("Masque", true)
-
-local GetTime = GetTime
-
-----------------
--- Main Frame --
-----------------
---[[
-	Events :
-		OnClose
-
-]]
+--inherits Frame
 do
+--<private-static-properties>
+	local AceGUI = LibStub("AceGUI-3.0")
+	local Masque = LibStub("Masque", true)
+	local GetTime = GetTime
+
 	local Type = "OvaleFrame"
 	local Version = 7
+--</private-static-properties>

-	local function frameOnClose(this)
-		this.obj:Fire("OnClose")
+--<public-methods>
+	local function frameOnClose(self)
+		self.obj:Fire("OnClose")
 	end

-	local function closeOnClick(this)
-		this.obj:Hide()
+	local function closeOnClick(self)
+		self.obj:Hide()
 	end

-	local function frameOnMouseDown(this)
-		if (not Ovale.db.profile.apparence.verrouille) then
-			this:StartMoving()
+	local function frameOnMouseDown(self)
+		if (not OvaleOptions:GetApparence().verrouille) then
+			self:StartMoving()
 			AceGUI:ClearFocus()
 		end
 	end

-	local function ToggleOptions(this)
-		if (this.content:IsShown()) then
-			this.content:Hide()
+	local function ToggleOptions(self)
+		if (self.content:IsShown()) then
+			self.content:Hide()
 		else
-			this.content:Show()
+			self.content:Show()
 		end
 	end

-	local function frameOnMouseUp(this)
-		this:StopMovingOrSizing()
+	local function frameOnMouseUp(self)
+		self:StopMovingOrSizing()
+		local profile = OvaleOptions:GetProfile()

-		if (Ovale.db.profile.left~=this:GetLeft() or Ovale.db.profile.top ~=this:GetTop()) then
-			Ovale.db.profile.left = this:GetLeft()
-			Ovale.db.profile.top = this:GetTop()
+		if (profile.left~=self:GetLeft() or profile.top ~=self:GetTop()) then
+			profile.left = self:GetLeft()
+			profile.top = self:GetTop()
 		end
 	end

-	local function frameOnEnter(this)
-		if (not Ovale.db.profile.apparence.verrouille) then
-			this.obj.barre:Show()
-		end
+	local function frameOnEnter(self)
+		if (not OvaleOptions:GetApparence().verrouille) then
+			self.obj.barre:Show()
+        end
 	end

-	local function frameOnLeave(this)
-		this.obj.barre:Hide()
+	local function frameOnLeave(self)
+		self.obj.barre:Hide()

 	end

@@ -113,7 +109,7 @@ do
 					-- print("sort "..spellId.." parfait")
 					return 1
 				else
-					local lag = Ovale.maintenant - action.waitStart
+					local lag = OvaleState.maintenant - action.waitStart
 					if lag>5 then
 					-- 	print("sort "..spellId.." ignoré (>5s)")
 						return nil
@@ -135,8 +131,8 @@ do
 	end

 	local function OnUpdate(self)
-		if not Ovale.listeTalentsRemplie then
-			Ovale:RemplirListeTalents()
+		if not OvaleData.listeTalentsRemplie then
+			OvaleData:RemplirListeTalents()
 		end
 		if Ovale.needCompile then
 			Ovale:CompileAll()
@@ -144,110 +140,121 @@ do
 		end

 		local now = GetTime()
-
-		if not Ovale.refreshNeeded and self.lastUpdate and now < self.lastUpdate + Ovale.db.profile.apparence.updateInterval then
+		local forceRefresh = not self.lastUpdate or (now > self.lastUpdate + OvaleOptions:GetApparence().updateInterval)
+
+		if not next(Ovale.refreshNeeded) and not forceRefresh then
 			return
 		end
-		Ovale.refreshNeeded = false
 		self.lastUpdate = now

-		Ovale:InitAllActions()
+		OvaleState:StartNewFrame()
 		for k,node in pairs(Ovale.masterNodes) do
-			if Ovale.trace then
-				Ovale:Print("****Master Node "..k)
-			end
-			Ovale:InitCalculerMeilleureAction()
-			local start, ending, priorite, element = Ovale:CalculerMeilleureAction(node)
-			if start then
-				Ovale:Log("CalculerMeilleureAction start = "..start)
-			end
-			local action = self.actions[k]
-			if element and element.type == "value" then
-				local actionTexture
-				if node.params.texture then
-					actionTexture = GetSpellTexture(node.params.texture)
-				end
-				local value
-				if element.value and element.origin and element.rate then
-					value = element.value + (Ovale.maintenant - element.origin) * element.rate
+			local target = node.params.target or "target"
+			OvaleCondition.defaultTarget = target
+
+			if forceRefresh or Ovale.refreshNeeded[target] or Ovale.refreshNeeded["player"] or Ovale.refreshNeeded["pet"] then
+				if Ovale.trace then
+					Ovale:Print("****Master Node "..k)
 				end
-				action.icons[1]:SetValue(value, actionTexture)
-				if #action.icons > 1 then
-					action.icons[2]:Update(element, nil)
+				OvaleBestAction:StartNewAction()
+				local start, ending, priorite, element = OvaleBestAction:Compute(node)
+				if start then
+					Ovale:Log("Compute start = "..start)
 				end
-			else
-				local actionTexture, actionInRange, actionCooldownStart, actionCooldownDuration,
-						actionUsable, actionShortcut, actionIsCurrent, actionEnable, spellId, actionTarget, noRed = Ovale:GetActionInfo(element)
-				if noRed then
-					start = actionCooldownStart + actionCooldownDuration
-					if start < Ovale.currentTime then
-						start = Ovale.currentTime
+				local action = self.actions[k]
+				if element and element.type == "value" then
+					local actionTexture
+					if node.params.texture then
+						actionTexture = GetSpellTexture(node.params.texture)
 					end
-				end
-
-				-- Dans le cas de canStopChannelling, on risque de demander d'interrompre le channelling courant, ce qui est stupide
-				if start and Ovale.currentSpellId and Ovale.attenteFinCast and spellId == Ovale.currentSpellId and start<Ovale.attenteFinCast then
-					start = Ovale.attenteFinCast
-				end
-
-				if (node.params.nocd and start~=nil and Ovale.maintenant<start-node.params.nocd) then
-					action.icons[1]:Update(element, nil)
-				else
-					action.icons[1]:Update(element, start, actionTexture, actionInRange, actionCooldownStart, actionCooldownDuration,
-						actionUsable, actionShortcut, actionIsCurrent, actionEnable, spellId, actionTarget)
-				end
-
-				action.spellId = spellId
-
-				if start == Ovale.maintenant and actionUsable then
-					if not action.waitStart then
-						action.waitStart = Ovale.maintenant
+					local value
+					if element.value and element.origin and element.rate then
+						value = element.value + (OvaleState.maintenant - element.origin) * element.rate
+					end
+					action.icons[1]:SetValue(value, actionTexture)
+					if #action.icons > 1 then
+						action.icons[2]:Update(element, nil)
 					end
 				else
-					action.waitStart = nil
-				end
-
-				if Ovale.db.profile.apparence.moving and action.icons[1].debutAction and action.icons[1].finAction then
-					local top=1-(Ovale.maintenant - action.icons[1].debutAction)/(action.icons[1].finAction-action.icons[1].debutAction)
-					if top<0 then
-						top = 0
-					elseif top>1 then
-						top = 1
+					local actionTexture, actionInRange, actionCooldownStart, actionCooldownDuration,
+							actionUsable, actionShortcut, actionIsCurrent, actionEnable, spellId, actionTarget, noRed = OvaleBestAction:GetActionInfo(element)
+					if noRed then
+						start = actionCooldownStart + actionCooldownDuration
+						if start < OvaleState.currentTime then
+							start = OvaleState.currentTime
+						end
 					end
-					action.icons[1]:SetPoint("TOPLEFT",self.frame,"TOPLEFT",(action.left + top*action.dx)/action.scale,(action.top - top*action.dy)/action.scale)
-					if action.icons[2] then
-						action.icons[2]:SetPoint("TOPLEFT",self.frame,"TOPLEFT",(action.left + (top+1)*action.dx)/action.scale,(action.top - (top+1)*action.dy)/action.scale)
+
+					-- Dans le cas de canStopChannelling, on risque de demander d'interrompre le channelling courant, ce qui est stupide
+					if start and OvaleState.currentSpellId and OvaleState.attenteFinCast and spellId == OvaleState.currentSpellId and start<OvaleState.attenteFinCast then
+						start = OvaleState.attenteFinCast
 					end
-				end
-
-				if (node.params.size ~= "small" and not node.params.nocd and Ovale.db.profile.apparence.predictif) then
-					if start then
-						local castTime=0
-						if spellId then
-							local _, _, _, _, _, _, _castTime = GetSpellInfo(spellId)
-							if _castTime and _castTime>0 then
-								castTime = _castTime/1000
-							end
+
+					if (node.params.nocd and start~=nil and OvaleState.maintenant<start-node.params.nocd) then
+						action.icons[1]:Update(element, nil)
+					else
+						action.icons[1]:Update(element, start, actionTexture, actionInRange, actionCooldownStart, actionCooldownDuration,
+							actionUsable, actionShortcut, actionIsCurrent, actionEnable, spellId, actionTarget)
+					end
+
+					action.spellId = spellId
+
+					if start == OvaleState.maintenant and actionUsable then
+						if not action.waitStart then
+							action.waitStart = OvaleState.maintenant
 						end
-						local gcd = Ovale:GetGCD(spellId)
-						local nextCast
-						if (castTime>gcd) then
-							nextCast = start + castTime
+					else
+						action.waitStart = nil
+					end
+
+					if OvaleOptions:GetApparence().moving and action.icons[1].debutAction and action.icons[1].finAction then
+						local top=1-(OvaleState.maintenant - action.icons[1].debutAction)/(action.icons[1].finAction-action.icons[1].debutAction)
+						if top<0 then
+							top = 0
+						elseif top>1 then
+							top = 1
+						end
+						action.icons[1]:SetPoint("TOPLEFT",self.frame,"TOPLEFT",(action.left + top*action.dx)/action.scale,(action.top - top*action.dy)/action.scale)
+						if action.icons[2] then
+							action.icons[2]:SetPoint("TOPLEFT",self.frame,"TOPLEFT",(action.left + (top+1)*action.dx)/action.scale,(action.top - (top+1)*action.dy)/action.scale)
+						end
+					end
+
+					if (node.params.size ~= "small" and not node.params.nocd and OvaleOptions:GetApparence().predictif) then
+						if start then
+							local castTime=0
+							if spellId then
+								local _, _, _, _, _, _, _castTime = GetSpellInfo(spellId)
+								if _castTime and _castTime>0 then
+									castTime = _castTime/1000
+								end
+							end
+							local gcd = OvaleData:GetGCD(spellId)
+							local nextCast
+							if (castTime>gcd) then
+								nextCast = start + castTime
+							else
+								nextCast = start + gcd
+							end
+							if Ovale.trace then
+								Ovale:Print("****Second icon " .. start)
+							end
+							local spellTarget = element.params.target
+							if spellTarget == "target" or not spellTarget then
+								spellTarget = target
+							end
+							OvaleState:AddSpellToStack(spellId, start, start + castTime, nextCast, false, UnitGUID(spellTarget))
+							start, ending, priorite, element = OvaleBestAction:Compute(node)
+							action.icons[2]:Update(element, start, OvaleBestAction:GetActionInfo(element))
 						else
-							nextCast = start + gcd
-						end
-						if Ovale.trace then
-							Ovale:Print("****Second icon " .. start)
+							action.icons[2]:Update(element, nil)
 						end
-						Ovale:AddSpellToStack(spellId, start, start + castTime, nextCast)
-						start, ending, priorite, element = Ovale:CalculerMeilleureAction(node)
-						action.icons[2]:Update(element, start, Ovale:GetActionInfo(element))
-					else
-						action.icons[2]:Update(element, nil)
 					end
 				end
 			end
 		end
+
+		Ovale.refreshNeeded = {}

 		if (not Ovale.bug) then
 			Ovale.traced = false
@@ -270,7 +277,7 @@ do
 			end
 		end

-		self.frame:EnableMouse(not Ovale.db.profile.apparence.clickThru)
+		self.frame:EnableMouse(not OvaleOptions:GetApparence().clickThru)

 		local left = 0
 		local maxHeight = 0
@@ -283,7 +290,7 @@ do

 		local BARRE = 8

-		local margin =  Ovale.db.profile.apparence.margin
+		local margin =  OvaleOptions:GetApparence().margin

 		for k,node in pairs(Ovale.masterNodes) do
 			if not self.actions[k] then
@@ -294,27 +301,27 @@ do
 			local width, height, newScale
 			local nbIcons
 			if (node.params.size == "small") then
-				newScale = Ovale.db.profile.apparence.smallIconScale
+				newScale = OvaleOptions:GetApparence().smallIconScale
 				width = newScale * 36 + margin
 				height = newScale * 36 + margin
 				nbIcons = 1
 			else
-				newScale = Ovale.db.profile.apparence.iconScale
+				newScale = OvaleOptions:GetApparence().iconScale
 				width =newScale * 36 + margin
 				height = newScale * 36 + margin
-				if Ovale.db.profile.apparence.predictif and node.params.type ~= "value" then
+				if OvaleOptions:GetApparence().predictif and node.params.type ~= "value" then
 					nbIcons = 2
 				else
 					nbIcons = 1
 				end
 			end
-			if (top + height > Ovale.db.profile.apparence.iconScale * 36 + margin) then
+			if (top + height > OvaleOptions:GetApparence().iconScale * 36 + margin) then
 				top = 0
 				left = maxWidth
 			end

 			action.scale = newScale
-			if (Ovale.db.profile.apparence.vertical) then
+			if (OvaleOptions:GetApparence().vertical) then
 				action.left = top
 				action.top = -left-BARRE-margin
 				action.dx = width
@@ -333,15 +340,15 @@ do
 				local icon = action.icons[l]
 				local scale = action.scale
 				if l> 1 then
-					scale = scale  * Ovale.db.profile.apparence.secondIconScale
+					scale = scale  * OvaleOptions:GetApparence().secondIconScale
 				end
 				icon:SetPoint("TOPLEFT",self.frame,"TOPLEFT",(action.left + (l-1)*action.dx)/scale,(action.top - (l-1)*action.dy)/scale)
 				icon:SetScale(scale)
-				icon:SetFontScale(Ovale.db.profile.apparence.fontScale)
+				icon:SetFontScale(OvaleOptions:GetApparence().fontScale)
 				icon:SetParams(node.params)
 				icon:SetHelp(node.params.help)
-				icon:SetRangeIndicator(Ovale.db.profile.apparence.targetText)
-				icon:EnableMouse(not Ovale.db.profile.apparence.clickThru)
+				icon:SetRangeIndicator(OvaleOptions:GetApparence().targetText)
+				icon:EnableMouse(not OvaleOptions:GetApparence().clickThru)
 				icon.cdShown = (l == 1)
 				if Masque then
 					self.skinGroup:AddButton(icon)
@@ -360,18 +367,18 @@ do
 			end
 		end

-		if (Ovale.db.profile.apparence.vertical) then
+		if (OvaleOptions:GetApparence().vertical) then
 			self.barre:SetWidth(maxHeight - margin)
 			self.barre:SetHeight(BARRE)
-			self.frame:SetWidth(maxHeight + Ovale.db.profile.apparence.iconShiftY)
-			self.frame:SetHeight(maxWidth+BARRE+margin + Ovale.db.profile.apparence.iconShiftX)
-			self.content:SetPoint("TOPLEFT",self.frame,"TOPLEFT",maxHeight + Ovale.db.profile.apparence.iconShiftX,Ovale.db.profile.apparence.iconShiftY)
+			self.frame:SetWidth(maxHeight + OvaleOptions:GetApparence().iconShiftY)
+			self.frame:SetHeight(maxWidth+BARRE+margin + OvaleOptions:GetApparence().iconShiftX)
+			self.content:SetPoint("TOPLEFT",self.frame,"TOPLEFT",maxHeight + OvaleOptions:GetApparence().iconShiftX,OvaleOptions:GetApparence().iconShiftY)
 		else
 			self.barre:SetWidth(maxWidth - margin)
 			self.barre:SetHeight(BARRE)
-			self.frame:SetWidth(maxWidth) -- + Ovale.db.profile.apparence.iconShiftX
-			self.frame:SetHeight(maxHeight+BARRE+margin) -- + Ovale.db.profile.apparence.iconShiftY
-			self.content:SetPoint("TOPLEFT",self.frame,"TOPLEFT",maxWidth + Ovale.db.profile.apparence.iconShiftX,Ovale.db.profile.apparence.iconShiftY)
+			self.frame:SetWidth(maxWidth) -- + OvaleOptions:GetApparence().iconShiftX
+			self.frame:SetHeight(maxHeight+BARRE+margin) -- + OvaleOptions:GetApparence().iconShiftY
+			self.content:SetPoint("TOPLEFT",self.frame,"TOPLEFT",maxWidth + OvaleOptions:GetApparence().iconShiftX,OvaleOptions:GetApparence().iconShiftY)
 		end
 	end

@@ -379,7 +386,6 @@ do
 		local frame = CreateFrame("Frame",nil,UIParent)
 		local self = {}

-		self.type = "Frame"

 		self.Hide = Hide
 		self.Show = Show
@@ -392,17 +398,29 @@ do
 		self.ToggleOptions = ToggleOptions
 		self.OnUpdate = OnUpdate
 		self.GetScore = GetScore
-
+
+--<public-properties>
+		self.type = "Frame"
 		self.localstatus = {}
 		self.actions = {}
-
-
 		self.frame = frame
+		self.updateFrame = CreateFrame("Frame")
+		self.barre = self.frame:CreateTexture();
+		self.content = CreateFrame("Frame",nil,frame)
+		if Masque then
+			self.skinGroup = Masque:Group("Ovale")
+		end
+		self.lastUpdate = nil
+		--Cheating with frame object which has an obj property
+		--TODO: Frame Class
+		self.obj = nil
+--</public-properties>
+
 		frame.obj = self
 		frame:SetWidth(100)
 		frame:SetHeight(100)
 		frame:SetPoint("CENTER",UIParent,"CENTER",0,0)
-		if not Ovale.db.profile.apparence.clickThru then
+		if not OvaleOptions:GetApparence().clickThru then
 			frame:EnableMouse()
 		end
 		frame:SetMovable(true)
@@ -413,34 +431,29 @@ do
 		frame:SetScript("OnLeave", frameOnLeave)
 	--	frame:SetScript("OnUpdate", frameOnUpdate)
 		frame:SetScript("OnHide",frameOnClose)
-		frame:SetAlpha(Ovale.db.profile.apparence.alpha)
+		frame:SetAlpha(OvaleOptions:GetApparence().alpha)

-		self.updateFrame = CreateFrame("Frame")
 		self.updateFrame:SetScript("OnUpdate", frameOnUpdate)
 		self.updateFrame.obj = self

-		self.barre = self.frame:CreateTexture();
 		self.barre:SetTexture(0,0.8,0)
 		self.barre:SetPoint("TOPLEFT",0,0)
 		self.barre:Hide()

 		--Container Support
-		local content = CreateFrame("Frame",nil,frame)
-		self.content = content
+		local content = self.content
 		content.obj = self
 		content:SetWidth(200)
 		content:SetHeight(100)
 		content:Hide()
-		content:SetAlpha(Ovale.db.profile.apparence.optionsAlpha)
+		content:SetAlpha(OvaleOptions:GetApparence().optionsAlpha)

 		AceGUI:RegisterAsContainer(self)

-		if Masque then
-			self.skinGroup = Masque:Group("Ovale")
-		end

 		return self
 	end
+--</public-methods>

 	AceGUI:RegisterWidgetType(Type,Constructor,Version)
 end
diff --git a/OvaleFuture.lua b/OvaleFuture.lua
new file mode 100644
index 0000000..362c7bc
--- /dev/null
+++ b/OvaleFuture.lua
@@ -0,0 +1,306 @@
+-- The travelling missiles or spells that have been cast but whose effects were not still not applied
+
+OvaleFuture = LibStub("AceAddon-3.0"):NewAddon("OvaleFuture", "AceEvent-3.0")
+
+--<public-static-properties>
+--spell counter (see Counter function)
+OvaleFuture.counter = {}
+--the spells that the player has casted but that did not reach their target
+--the result is computed by the simulator, allowing to ignore lag or missile travel time
+OvaleFuture.lastSpell = {}
+--the attack power of the last spell
+OvaleFuture.lastSpellAP = {}
+OvaleFuture.lastSpellSP = {}
+OvaleFuture.lastSpellDM = {}
+OvaleFuture.playerGUID = nil
+OvaleFuture.nextSpellTarget = nil
+OvaleFuture.nextSpellLineID = nil
+--</public-static-properties>
+
+-- Events
+--<public-static-methods>
+function OvaleFuture:OnEnable()
+	self.playerGUID = UnitGUID("player")
+	self:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
+	self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
+	self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
+    self:RegisterEvent("UNIT_SPELLCAST_START")
+	self:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
+	self:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
+	self:RegisterEvent("UNIT_SPELLCAST_SENT")
+end
+
+function OvaleFuture:OnDisable()
+	self:UnregisterEvent("UNIT_SPELLCAST_SUCCEEDED")
+	self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
+	self:UnregisterEvent("UNIT_SPELLCAST_INTERRUPTED")
+    self:UnregisterEvent("UNIT_SPELLCAST_CHANNEL_START")
+	self:UnregisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
+    self:UnregisterEvent("UNIT_SPELLCAST_START")
+	self:UnregisterEvent("UNIT_SPELLCAST_SENT")
+end
+
+
+function OvaleFuture:UNIT_SPELLCAST_CHANNEL_START(event, unit, name, rank, lineId, spellId)
+	if unit=="player" then
+		--Ovale:Print("UNIT_SPELLCAST_CHANNEL_START "..event.." name="..name.." lineId="..lineId.." spellId="..spellId .. " " .. GetTime())
+		local _,_,_,_,startTime, endTime = UnitChannelInfo("player")
+		--Ovale:Print("startTime = " ..startTime.." endTime = "..endTime)
+		self:AddSpellToList(spellId, lineId, startTime/1000, endTime/1000, true, false)
+	end
+end
+
+function OvaleFuture:UNIT_SPELLCAST_CHANNEL_STOP(event, unit, name, rank, lineId, spellId)
+	if unit == "player" then
+		--Ovale:Print("UNIT_SPELLCAST_CHANNEL_STOP "..event.." name="..name.." lineId="..lineId.." spellId="..spellId)
+		self:RemoveSpellFromList(spellId, lineId)
+	end
+end
+
+--Called when a spell started its cast
+function OvaleFuture:UNIT_SPELLCAST_START(event, unit, name, rank, lineId, spellId)
+	--Ovale:Print("UNIT_SPELLCAST_START "..event.." name="..name.." lineId="..lineId.." spellId="..spellId .. " time=" .. GetTime())
+	if unit=="player" then
+		local _,_,_,_,startTime,endTime = UnitCastingInfo("player")
+		--local spell, rank, icon, cost, isFunnel, powerType, castTime, minRange, maxRange = GetSpellInfo(spellId)
+		--local startTime =  GetTime()
+		--self:AddSpellToList(spellId, lineId, startTime, startTime + castTime/1000)
+		self:AddSpellToList(spellId, lineId, startTime/1000, endTime/1000, false, false)
+	end
+end
+
+--Called if the player interrupted early his cast
+--The spell is removed from the lastSpell table
+function OvaleFuture:UNIT_SPELLCAST_INTERRUPTED(event, unit, name, rank, lineId, spellId)
+	if unit == "player" then
+		--Ovale:Print("UNIT_SPELLCAST_INTERRUPTED "..event.." name="..name.." lineId="..lineId.." spellId="..spellId.. " time="..GetTime())
+		self:RemoveSpellFromList(spellId, lineId)
+	end
+end
+
+function OvaleFuture:UNIT_SPELLCAST_SENT(event, unit, spell, rank, target, lineId)
+	if unit == "player" then
+		local targetGUID = OvaleGUID.nameToGUID[target]
+		self.nextSpellTarget = targetGUID
+		self.nextSpellLineID = lineId
+		--Ovale:Print(target)
+		for i,v in ipairs(self.lastSpell) do
+			if v.lineId == lineId then
+				v.target = targetGUID
+			end
+		end
+	end
+end
+
+function OvaleFuture:UNIT_SPELLCAST_SUCCEEDED(event, unit, name, rank, lineId, spellId)
+	if unit == "player" then
+		--Ovale:Print("UNIT_SPELLCAST_SUCCEEDED "..event.." name="..name.." lineId="..lineId.." spellId="..spellId.. " time="..GetTime())
+		for i,v in ipairs(self.lastSpell) do
+			if v.lineId == lineId then
+				--Already added in UNIT_SPELLCAST_START
+				v.allowRemove = true
+				return
+			end
+		end
+		if not UnitChannelInfo("player") then
+			--A UNIT_SPELLCAST_SUCCEEDED is received when channeling a spell, with a different lineId!
+			local now = GetTime()
+			self:AddSpellToList(spellId, lineId, now, now, false, true)
+		end
+	end
+end
+
+function OvaleFuture:COMBAT_LOG_EVENT_UNFILTERED(event, ...)
+	local time, event, hideCaster, sourceGUID, sourceName, sourceFlags, sourceRaidFlags, destGUID, destName, destFlags, destRaidFlags = select(1, ...)
+
+	--[[
+	Sequence of events:
+	- casting a spell that damages
+	SPELL_CAST_START
+	SPELL_DAMAGE
+	- casting a spell that misses
+	SPELL_CAST_START
+	SPELL_MISSED
+	- casting a spell then interrupting it
+	SPELL_CAST_START
+	SPELL_CAST_FAILED
+	- casting an instant damaging spell
+	SPELL_CAST_SUCCESS
+	SPELL_DAMAGE
+	- chanelling a damaging spell
+	SPELL_CAST_SUCCESS
+	SPELL_AURA_APPLIED
+	SPELL_PERIODIC_DAMAGE
+	SPELL_PERIODIC_DAMAGE
+	SPELL_PERIODIC_DAMAGE
+	(interruption does not generate an event)
+	- refreshing a buff
+	SPELL_AURA_REFRESH
+	SPELL_CAST_SUCCESS
+	- removing a buff
+	SPELL_AURA_REMOVED
+	- casting a buff
+	SPELL_AURA_APPLIED
+	SPELL_CAST_SUCCESS
+	-casting a DOT that misses
+	SPELL_CAST_SUCCESS
+	SPELL_MISSED
+	- casting a DOT that damages
+	SPELL_CAST_SUCESS
+	SPELL_AURA_APPLIED
+	SPELL_PERIODIC_DAMAGE
+	SPELL_PERIODIC_DAMAGE]]
+
+	if sourceGUID == self.playerGUID then
+		--Called when a missile reached or missed its target
+		--Update lastSpell accordingly
+		--Do not use SPELL_CAST_SUCCESS because it is sent when the missile has not reached the target
+
+			--Ovale:Print("SPELL_CAST_START " .. GetTime())
+		--if string.find(event, "SPELL") == 1 then
+		--	local spellId, spellName = select(12, ...)
+		--	Ovale:Print(event .. " " ..spellName .. " " ..GetTime())
+		--end
+			-- local spellId, spellName = select(12, ...)
+			-- for i,v in ipairs(self.lastSpell) do
+
+			-- end
+		--end
+
+		if
+				string.find(event, "SPELL_AURA_APPLIED")==1
+				or string.find(event, "SPELL_AURA_REFRESH")==1
+				or string.find(event, "SPELL_DAMAGE")==1
+				or string.find(event, "SPELL_MISSED") == 1
+				or string.find(event, "SPELL_CAST_SUCCESS") == 1
+				or string.find(event, "SPELL_CAST_FAILED") == 1 then
+			local spellId, spellName = select(12, ...)
+			for i,v in ipairs(self.lastSpell) do
+				if (v.spellId == spellId or v.auraSpellId == spellId) and v.allowRemove then
+					if not v.channeled and (v.removeOnSuccess or
+								string.find(event, "SPELL_CAST_SUCCESS") ~= 1) then
+						table.remove(self.lastSpell, i)
+						Ovale.refreshNeeded["player"] = true
+						--Ovale:Print("LOG_EVENT on supprime "..spellId.." a "..GetTime())
+					end
+					--Ovale:Print(UnitDebuff("target", "Etreinte de l'ombre"))
+					break
+				end
+			end
+		end
+	end
+
+end
+
+function OvaleFuture:AddSpellToList(spellId, lineId, startTime, endTime, channeled, allowRemove)
+	local newSpell = {}
+	newSpell.spellId = spellId
+	newSpell.lineId = lineId
+	newSpell.start = startTime
+	newSpell.stop = endTime
+	newSpell.channeled = channeled
+	newSpell.allowRemove = allowRemove
+	--TODO unable to know what is the real target
+	if lineId == self.nextSpellLineID and self.nextSpellTarget then
+		newSpell.target = self.nextSpellTarget
+	else
+		newSpell.target = UnitGUID("target")
+	end
+
+	self.lastSpellAP[spellId] = UnitAttackPower("player")
+	self.lastSpellSP[spellId] = GetSpellBonusDamage(2)
+	self.lastSpellDM[spellId] = OvaleAura.damageMultiplier
+	self.lastSpell[#self.lastSpell+1] = newSpell
+	--Ovale:Print("on ajoute "..spellId..": ".. newSpell.start.." to "..newSpell.stop.." ("..OvaleState.maintenant..")" ..#self.lastSpell .. " " ..newSpell.target)
+
+	if OvaleData.spellInfo[spellId] then
+		local si = OvaleData.spellInfo[spellId]
+
+		if si.aura then
+			for target, targetInfo in pairs(si.aura) do
+				for filter, filterInfo in pairs(targetInfo) do
+					for auraSpellId, spellData in pairs(filterInfo) do
+						if spellData and spellData ~= "refresh" and spellData > 0 then
+							newSpell.auraSpellId = auraSpellId
+							if target == "player" then
+								newSpell.removeOnSuccess = true
+							end
+							break
+						end
+					end
+				end
+			end
+		end
+
+		--Ovale:Print("spellInfo found")
+		if si and si.buffnocd and UnitBuff("player", GetSpellInfo(si.buffnocd)) then
+			newSpell.nocd = true
+		else
+			newSpell.nocd = false
+		end
+		--Increase or reset the counter that is used by the Counter function
+		if si.resetcounter then
+			self.counter[si.resetcounter] = 0
+			--Ovale:Print("reset counter "..si.resetcounter)
+		end
+		if si.inccounter then
+			local cname = si.inccounter
+			if not self.counter[cname] then
+				self.counter[cname] = 0
+			end
+			self.counter[cname] = self.counter[cname] + 1
+			--Ovale:Print("inc counter "..cname.." to "..self.counter[cname])
+		end
+	else
+		newSpell.removeOnSuccess = true
+	end
+
+	if Ovale.enCombat then
+		--Ovale:Print(tostring(OvaleData.scoreSpell[spellId]))
+		if (not OvaleData.spellInfo[spellId] or not OvaleData.spellInfo[spellId].toggle) and OvaleData.scoreSpell[spellId] then
+			--Compute the player score
+			local scored = Ovale.frame:GetScore(spellId)
+			--Ovale:Print("Scored "..scored)
+			if scored~=nil then
+				Ovale.score = Ovale.score + scored
+				Ovale.maxScore = Ovale.maxScore + 1
+				Ovale:SendScoreToDamageMeter(UnitName("player"), OvaleAura.playerGUID, scored, 1)
+			end
+		end
+	end
+	Ovale.refreshNeeded["player"] = true
+end
+
+
+function OvaleFuture:RemoveSpellFromList(spellId, lineId)
+	for i,v in ipairs(self.lastSpell) do
+		if v.lineId == lineId then
+			table.remove(self.lastSpell, i)
+			--Ovale:Print("RemoveSpellFromList on supprime "..spellId)
+			break
+		end
+	end
+	Ovale.refreshNeeded["player"] = true
+end
+
+-- Apply the effects of travelling spells
+function OvaleFuture:Apply()
+	for i,v in ipairs(self.lastSpell) do
+		if not OvaleData.spellInfo[v.spellId] or not OvaleData.spellInfo[v.spellId].toggle then
+			--[[local spell, rank, displayName, icon, startTime, endTime, isTradeSkill = UnitCastingInfo("player")
+			if spell and spell == v.name and startTime/1000 - v.start < 0.5 and v.stop~=endTime/1000 then
+				print("ancien = "..v.stop)
+				v.stop = endTime/1000
+				print("changement de v.stop en "..v.stop.." "..v.start)
+			end]]
+			Ovale:Log("OvaleState.maintenant = " ..OvaleState.maintenant.." spellId="..v.spellId.." v.stop="..v.stop)
+			if OvaleState.maintenant - v.stop < 5 then
+				OvaleState:AddSpellToStack(v.spellId, v.start, v.stop, v.stop, v.nocd, v.target)
+			else
+				--Ovale:Print("Removing obsolete "..v.spellId)
+				table.remove(self.lastSpell, i)
+			end
+		end
+	end
+end
+--</public-static-methods>
diff --git a/OvaleGUID.lua b/OvaleGUID.lua
new file mode 100644
index 0000000..9cfb869
--- /dev/null
+++ b/OvaleGUID.lua
@@ -0,0 +1,134 @@
+-- This addon translates a GUID to a target name
+-- Usage: OvaleGUID:GetUnitId(guid)
+
+OvaleGUID = LibStub("AceAddon-3.0"):NewAddon("OvaleGUID", "AceEvent-3.0", "AceConsole-3.0")
+
+--<public-static-properties>
+OvaleGUID.unitId = {}
+OvaleGUID.guid = {}
+OvaleGUID.player = nil
+OvaleGUID.nameToGUID = {}
+OvaleGUID.nameToUnit = {}
+--</public-static-properties>
+
+--<public-static-methods>
+function OvaleGUID:OnEnable()
+	self:Update("player")
+	self:RegisterEvent("PLAYER_LOGIN")
+	self:RegisterEvent("UNIT_TARGET")
+	self:RegisterEvent("PARTY_MEMBERS_CHANGED")
+	self:RegisterEvent("RAID_ROSTER_UPDATE")
+	self:RegisterEvent("UNIT_PET")
+	self:RegisterEvent("ARENA_OPPONENT_UPDATE")
+	self:RegisterEvent("PLAYER_FOCUS_CHANGED")
+	self:RegisterEvent("UPDATE_MOUSEOVER_UNIT")
+	self:RegisterEvent("INSTANCE_ENCOUNTER_ENGAGE_UNIT")
+end
+
+function OvaleGUID:OnDisable()
+	self:UnregisterEvent("PLAYER_LOGIN")
+	self:UnregisterEvent("UNIT_TARGET")
+	self:UnregisterEvent("PARTY_MEMBERS_CHANGED")
+	self:UnregisterEvent("RAID_ROSTER_UPDATE")
+	self:UnregisterEvent("UNIT_PET")
+	self:UnregisterEvent("ARENA_OPPONENT_UPDATE")
+	self:UnregisterEvent("PLAYER_FOCUS_CHANGED")
+	self:UnregisterEvent("UPDATE_MOUSEOVER_UNIT")
+	self:UnregisterEvent("INSTANCE_ENCOUNTER_ENGAGE_UNIT")
+end
+
+function OvaleGUID:Update(unitId)
+	local guid = UnitGUID(unitId)
+	local previousGuid = self.guid[unitId]
+	if unitId == "player" then
+		self.player = guid
+	end
+	if previousGuid ~= guid then
+		if previousGuid then
+			self.unitId[previousGuid][unitId] = nil
+			if not next(self.unitId[previousGuid]) then
+				self.unitId[previousGuid] = nil
+			end
+		end
+		self.guid[unitId] = guid
+		if guid then
+			if not self.unitId[guid] then
+				self.unitId[guid] = {}
+			end
+			-- Ovale:Print("GUID "..guid.." is ".. unitId)
+			self.unitId[guid][unitId] = true
+		end
+		local name = UnitName(unitId)
+		if name and (not self.nameToGUID[name] or unitId == "target"
+				or self.nameToUnit[name] == "mouseover") then
+			self.nameToGUID[name] = guid
+			self.nameToUnit[name] = unitId
+		end
+	end
+end
+
+function OvaleGUID:GetUnitId(guid)
+	local unitIdTable = self.unitId[guid]
+	if not unitIdTable then return nil end
+	return next(unitIdTable)
+end
+
+function OvaleGUID:UpdateWithTarget(unitId)
+	self:Update(unitId)
+	self:Update(unitId.."target")
+end
+
+function OvaleGUID:PLAYER_LOGIN(event)
+	self:Update("player")
+end
+
+function OvaleGUID:UNIT_TARGET(event, unitId)
+	self:Update(unitId .. "target")
+end
+
+function OvaleGUID:PARTY_MEMBERS_CHANGED(event)
+	for i=1, GetNumPartyMembers() do
+		self:UpdateWithTarget("party"..i)
+		self:UpdateWithTarget("partypet"..i)
+	end
+end
+
+function OvaleGUID:RAID_ROSTER_UPDATE(event)
+	for i=1, GetNumRaidMembers() do
+		self:UpdateWithTarget("raid"..i)
+		self:UpdateWithTarget("raidpet"..i)
+	end
+end
+
+function OvaleGUID:UNIT_PET(event, unitId)
+	if string.find(unitId, "party") == 0 then
+		local petId = "partypet" .. string.sub(unitId, 6)
+		self:UpdateWithTarget(petId)
+	elseif string.find(unitId, "raid") == 0 then
+		local petId = "raidpet" .. string.sub(unitId, 5)
+		self:UpdateWithTarget(petId)
+	elseif unitId == "player" then
+		self:UpdateWithTarget("pet")
+	end
+end
+
+function OvaleGUID:ARENA_OPPONENT_UPDATE(event)
+	for i=1, 5 do
+		self:UpdateWithTarget("arena"..i)
+	end
+end
+
+function OvaleGUID:PLAYER_FOCUS_CHANGED(event)
+	self:UpdateWithTarget("focus")
+end
+
+function OvaleGUID:UPDATE_MOUSEOVER_UNIT(event)
+	self:UpdateWithTarget("mouseover")
+end
+
+function OvaleGUID:INSTANCE_ENCOUNTER_ENGAGE_UNIT(event)
+	for i=1, 4 do
+		self:UpdateWithTarget("boss"..i)
+	end
+end
+--</public-static-methods>
diff --git a/OvaleIcone.lua b/OvaleIcone.lua
index b92c677..af9b493 100644
--- a/OvaleIcone.lua
+++ b/OvaleIcone.lua
@@ -1,5 +1,8 @@
 local L = LibStub("AceLocale-3.0"):GetLocale("Ovale")

+--inherits ActionButtonTemplate
+
+--<public-methods>
 local function SetValue(self, value, actionTexture)
 	self.icone:Show()
 	self.icone:SetTexture(actionTexture);
@@ -29,15 +32,15 @@ local function Update(self, element, minAttente, actionTexture, actionInRange, a
 	if (minAttente~=nil and actionTexture) then

 		if (actionTexture~=self.actionCourante or self.ancienneAttente==nil or
-			(minAttente~=Ovale.maintenant and minAttente>self.ancienneAttente+0.01) or
+			(minAttente~=OvaleState.maintenant and minAttente>self.ancienneAttente+0.01) or
 			(minAttente < self.finAction-0.01)) then
 			if (actionTexture~=self.actionCourante or self.ancienneAttente==nil or
-					(minAttente~=Ovale.maintenant and minAttente>self.ancienneAttente+0.01)) then
-				self.debutAction = Ovale.maintenant
+					(minAttente~=OvaleState.maintenant and minAttente>self.ancienneAttente+0.01)) then
+				self.debutAction = OvaleState.maintenant
 			end
 			self.actionCourante = actionTexture
 			self.finAction = minAttente
-			if (minAttente == Ovale.maintenant) then
+			if (minAttente == OvaleState.maintenant) then
 				self.cd:Hide()
 			else
 				self.lastSound = nil
@@ -48,7 +51,7 @@ local function Update(self, element, minAttente, actionTexture, actionInRange, a
 			end
 		end

-		if not Ovale.db.profile.apparence.flashIcon and minAttente<=Ovale.maintenant then
+		if not OvaleOptions:GetApparence().flashIcon and minAttente<=OvaleState.maintenant then
 			self.cd:Hide()
 		end

@@ -65,21 +68,21 @@ local function Update(self, element, minAttente, actionTexture, actionInRange, a
 		end

 		local red
-		if (minAttente > actionCooldownStart + actionCooldownDuration + 0.01 and minAttente > Ovale.maintenant
-			and minAttente>Ovale.attenteFinCast) then
+		if (minAttente > actionCooldownStart + actionCooldownDuration + 0.01 and minAttente > OvaleState.maintenant
+			and minAttente>OvaleState.attenteFinCast) then
 			self.icone:SetVertexColor(0.75,0.2,0.2)
 			red = true
 		else
 			self.icone:SetVertexColor(1,1,1)
 		end

-		--if (minAttente==Ovale.maintenant) then
+		--if (minAttente==OvaleState.maintenant) then
 			--self.cd:Hide()
 		--end

 		if element.params.sound and not self.lastSound then
 			local delay = element.params.soundtime or 0.5
-			if Ovale.maintenant>=minAttente - delay then
+			if OvaleState.maintenant>=minAttente - delay then
 				self.lastSound = element.params.sound
 			--	print("Play" .. self.lastSound)
 				PlaySoundFile(self.lastSound)
@@ -87,10 +90,10 @@ local function Update(self, element, minAttente, actionTexture, actionInRange, a
 		end

 		-- La latence
-		if minAttente>Ovale.maintenant and Ovale.db.profile.apparence.highlightIcon and not red then
+		if minAttente>OvaleState.maintenant and OvaleOptions:GetApparence().highlightIcon and not red then
 			local lag = 0.6
 			local newShouldClick
-			if minAttente<Ovale.maintenant + lag then
+			if minAttente<OvaleState.maintenant + lag then
 				newShouldClick = true
 			else
 				newShouldClick = false
@@ -109,15 +112,15 @@ local function Update(self, element, minAttente, actionTexture, actionInRange, a
 		end

 		-- Le temps restant
-		if ((Ovale.db.profile.apparence.numeric or self.params.text == "always") and minAttente > Ovale.maintenant) then
-			self.remains:SetText(string.format("%.1f", minAttente - Ovale.maintenant))
+		if ((OvaleOptions:GetApparence().numeric or self.params.text == "always") and minAttente > OvaleState.maintenant) then
+			self.remains:SetText(string.format("%.1f", minAttente - OvaleState.maintenant))
 			self.remains:Show()
 		else
 			self.remains:Hide()
 		end

 		-- Le raccourcis clavier
-		if (Ovale.db.profile.apparence.raccourcis) then
+		if (OvaleOptions:GetApparence().raccourcis) then
 			self.shortcut:Show()
 			self.shortcut:SetText(actionShortcut)
 		else
@@ -148,7 +151,7 @@ local function Update(self, element, minAttente, actionTexture, actionInRange, a
 		self.shortcut:Hide()
 		self.remains:Hide()
 		self.focusText:Hide()
-		if Ovale.db.profile.apparence.hideEmpty then
+		if OvaleOptions:GetApparence().hideEmpty then
 			self:Hide()
 		else
 			self:Show()
@@ -177,6 +180,11 @@ local function SetFontScale(self, scale)
 	self.aPortee:SetFont(self.fontName, self.fontHeight * self.fontScale, self.fontFlags)
 end

+local function SetRangeIndicator(self, text)
+	self.aPortee:SetText(text)
+end
+--</public-methods>
+
 function OvaleIcone_OnClick(self)
 	Ovale:ToggleOptions()
 	self:SetChecked(0)
@@ -204,26 +212,36 @@ function OvaleIcone_OnLeave(self)
 	end
 end

-local function SetRangeIndicator(self, text)
-	self.aPortee:SetText(text)
-end
-
 function OvaleIcone_OnLoad(self)
 	local name = self:GetName()
+
+--<public-properties>
 	self.icone = _G[name.."Icon"]
 	self.shortcut = _G[name.."HotKey"]
 	self.remains = _G[name.."Name"]
 	self.aPortee = _G[name.."Count"]
-	self.aPortee:SetText(Ovale.db.profile.apparence.targetText)
+	self.aPortee:SetText(OvaleOptions:GetApparence().targetText)
 	self.cd = _G[name.."Cooldown"]
 	self.normalTexture = _G[name.."NormalTexture"]
-
 	local fontName, fontHeight, fontFlags = self.shortcut:GetFont()
 	self.fontName = fontName
 	self.fontHeight = fontHeight
 	self.fontFlags = fontFlags
-
 	self.focusText = self:CreateFontString(nil, "OVERLAY");
+	self.cdShown = true
+	self.shouldClick = false
+	self.help = nil
+	self.spellId = nil
+	self.fontScale = nil
+	self.lastSound = nil
+	self.ancienneAttente = nil
+	self.finAction = nil
+	self.debutAction = nil
+	self.actionCourante = nil
+	self.params = nil
+--</public-properties>
+
+
 	self.focusText:SetFontObject("GameFontNormal");
 	self.focusText:SetAllPoints(self);
 	self.focusText:SetTextColor(1,1,1);
@@ -237,8 +255,7 @@ function OvaleIcone_OnLoad(self)
 	self.SetFontScale = SetFontScale
 	self.SetRangeIndicator = SetRangeIndicator
 	self.SetValue = SetValue
-	self.cdShown = true
-	if Ovale.db.profile.clickThru then
+	if OvaleOptions:GetProfile().clickThru then
 		self:EnableMouse(false)
 	end
 end
diff --git a/OvaleIcone.xml b/OvaleIcone.xml
index 06177d3..044555b 100644
--- a/OvaleIcone.xml
+++ b/OvaleIcone.xml
@@ -2,7 +2,7 @@

   <CheckButton name="OvaleIcone" virtual="true" inherits="ActionButtonTemplate">
     <Scripts>
-      <OnLoad>OvaleIcone_OnLoad(self);</OnLoad>
+      <OnLoad>OvaleIcone_OnLoad(self)</OnLoad>
       <OnClick>OvaleIcone_OnClick(self)</OnClick>
       <OnEnter>OvaleIcone_OnEnter(self)</OnEnter>
       <OnLeave>OvaleIcone_OnLeave(self)</OnLeave>
diff --git a/OvaleOptions.lua b/OvaleOptions.lua
new file mode 100644
index 0000000..08525d8
--- /dev/null
+++ b/OvaleOptions.lua
@@ -0,0 +1,481 @@
+-- Ovale options and UI
+
+OvaleOptions = LibStub("AceAddon-3.0"):NewAddon("OvaleOptions", "AceEvent-3.0", "AceConsole-3.0")
+
+--<public-static-properties>
+OvaleOptions.firstInit = false
+OvaleOptions.db = nil
+--</public-static-properties>
+
+--<private-static-properties>
+local AceConfig = LibStub("AceConfig-3.0");
+local AceConfigDialog = LibStub("AceConfigDialog-3.0");
+local L = LibStub("AceLocale-3.0"):GetLocale("Ovale")
+
+--GUI option
+local options =
+{
+	type = "group",
+	args =
+	{
+		apparence =
+		{
+			name = L["Apparence"],
+			type = "group",
+			args =
+			{
+				combatUniquement =
+				{
+					order = 1,
+					type = "toggle",
+					name = L["En combat uniquement"],
+					get = function(info)
+						return OvaleOptions.db.profile.apparence.enCombat
+					end,
+					set = function(info, v)
+						OvaleOptions.db.profile.apparence.enCombat = v
+						Ovale:UpdateVisibility()
+					end,
+					width = "full"
+				},
+				targetOnly =
+				{
+					order = 1.5,
+					type = "toggle",
+					name = L["Si cible uniquement"],
+					get = function(info)
+						return OvaleOptions.db.profile.apparence.avecCible
+					end,
+					set = function(info, v)
+						OvaleOptions.db.profile.apparence.avecCible = v
+						Ovale:UpdateVisibility()
+					end,
+					width = "full"
+				},
+				iconScale =
+				{
+					order = 2,
+					type = "range",
+					name = L["Taille des icônes"],
+					desc = L["La taille des icônes"],
+					min = 0.1, max = 16, step = 0.1,
+					get = function(info) return OvaleOptions.db.profile.apparence.iconScale end,
+					set = function(info,value) OvaleOptions.db.profile.apparence.iconScale = value; Ovale:UpdateFrame() end
+				},
+				secondIconScale =
+				{
+					order = 2.5,
+					type = "range",
+					name = L["Taille du second icône"],
+					min = 0.2, max = 1, step = 0.1,
+					get = function(info) return OvaleOptions.db.profile.apparence.secondIconScale end,
+					set = function(info,value) OvaleOptions.db.profile.apparence.secondIconScale = value; Ovale:UpdateFrame() end
+				},
+				fontScale =
+				{
+					order = 3,
+					type = "range",
+					name = L["Taille des polices"],
+					desc = L["La taille des polices"],
+					min = 0.1, max = 2, step = 0.1,
+					get = function(info) return OvaleOptions.db.profile.apparence.fontScale end,
+					set = function(info,value) OvaleOptions.db.profile.apparence.fontScale = value; Ovale:UpdateFrame() end
+				},
+				smallIconScale =
+				{
+					order = 4,
+					type = "range",
+					name = L["Taille des petites icônes"],
+					desc = L["La taille des petites icônes"],
+					min = 0.1, max = 16, step = 0.1,
+					get = function(info) return OvaleOptions.db.profile.apparence.smallIconScale end,
+					set = function(info,value) OvaleOptions.db.profile.apparence.smallIconScale = value; Ovale:UpdateFrame() end
+				},
+				margin =
+				{
+					order = 5.5,
+					type = "range",
+					name = L["Marge entre deux icônes"],
+					min = -16, max = 64, step = 1,
+					get = function(info) return OvaleOptions.db.profile.apparence.margin end,
+					set = function(info,value) OvaleOptions.db.profile.apparence.margin = value; Ovale:UpdateFrame() end
+				},
+				iconShiftX =
+				{
+					order = 5.6,
+					type = "range",
+					name = L["Décalage horizontal des options"],
+					min = -256, max = 256, step = 1,
+					get = function(info) return OvaleOptions.db.profile.apparence.iconShiftX end,
+					set = function(info,value) OvaleOptions.db.profile.apparence.iconShiftX = value; Ovale:UpdateFrame() end
+				},
+				iconShiftY =
+				{
+					order = 5.7,
+					type = "range",
+					name = L["Décalage vertical des options"],
+					min = -256, max = 256, step = 1,
+					get = function(info) return OvaleOptions.db.profile.apparence.iconShiftY end,
+					set = function(info,value) OvaleOptions.db.profile.apparence.iconShiftY = value; Ovale:UpdateFrame() end
+				},
+				raccourcis =
+				{
+					order = 6,
+					type = "toggle",
+					name = L["Raccourcis clavier"],
+					desc = L["Afficher les raccourcis clavier dans le coin inférieur gauche des icônes"],
+					get = function(info) return OvaleOptions.db.profile.apparence.raccourcis end,
+					set = function(info, value) OvaleOptions.db.profile.apparence.raccourcis = value end
+				},
+				numeric =
+				{
+					order = 7,
+					type = "toggle",
+					name = L["Affichage numérique"],
+					desc = L["Affiche le temps de recharge sous forme numérique"],
+					get = function(info) return OvaleOptions.db.profile.apparence.numeric end,
+					set = function(info, value) OvaleOptions.db.profile.apparence.numeric = value end
+				},
+				verrouille =
+				{
+					order = 8,
+					type = "toggle",
+					name = L["Verrouiller position"],
+					get = function(info) return OvaleOptions.db.profile.apparence.verrouille end,
+					set = function(info, value) OvaleOptions.db.profile.apparence.verrouille = value end
+				},
+				vertical =
+				{
+					order = 9,
+					type = "toggle",
+					name = L["Vertical"],
+					get = function(info) return OvaleOptions.db.profile.apparence.vertical end,
+					set = function(info, value) OvaleOptions.db.profile.apparence.vertical = value; Ovale:UpdateFrame() end
+				},
+				alpha =
+				{
+					order = 9.5,
+					type = "range",
+					name = L["Opacité des icônes"],
+					min = 0, max = 100, step = 5,
+					get = function(info) return OvaleOptions.db.profile.apparence.alpha * 100 end,
+					set = function(info, value) OvaleOptions.db.profile.apparence.alpha = value/100; Ovale.frame.frame:SetAlpha(value/100) end
+				},
+				optionsAlpha =
+				{
+					order = 9.5,
+					type = "range",
+					name = L["Opacité des options"],
+					min = 0, max = 100, step = 5,
+					get = function(info) return OvaleOptions.db.profile.apparence.optionsAlpha * 100 end,
+					set = function(info, value) OvaleOptions.db.profile.apparence.optionsAlpha = value/100; Ovale.frame.content:SetAlpha(value/100) end
+				},
+				predictif =
+				{
+					order = 10,
+					type = "toggle",
+					name = L["Prédictif"],
+					desc = L["Affiche les deux prochains sorts et pas uniquement le suivant"],
+					get = function(info) return OvaleOptions.db.profile.apparence.predictif end,
+					set = function(info, value) OvaleOptions.db.profile.apparence.predictif = value; Ovale:UpdateFrame() end
+				},
+				moving =
+				{
+					order = 11,
+					type = "toggle",
+					name = L["Défilement"],
+					desc = L["Les icônes se déplacent"],
+					get = function(info) return OvaleOptions.db.profile.apparence.moving end,
+					set = function(info, value) OvaleOptions.db.profile.apparence.moving = value; Ovale:UpdateFrame() end
+				},
+				hideEmpty =
+				{
+					order = 12,
+					type = "toggle",
+					name = L["Cacher bouton vide"],
+					get = function(info) return OvaleOptions.db.profile.apparence.hideEmpty end,
+					set = function(info, value) OvaleOptions.db.profile.apparence.hideEmpty = value; Ovale:UpdateFrame() end
+				},
+				targetHostileOnly =
+				{
+					order = 13,
+					type = "toggle",
+					name = L["Cacher si cible amicale ou morte"],
+					get = function(info) return OvaleOptions.db.profile.apparence.targetHostileOnly end,
+					set = function(info, value) OvaleOptions.db.profile.apparence.targetHostileOnly = value; Ovale:UpdateFrame() end
+				},
+				highlightIcon =
+				{
+					order = 14,
+					type = "toggle",
+					name = L["Illuminer l'icône"],
+					desc = L["Illuminer l'icône quand la technique doit être spammée"],
+					get = function(info) return OvaleOptions.db.profile.apparence.highlightIcon end,
+					set = function(info, value) OvaleOptions.db.profile.apparence.highlightIcon = value; Ovale:UpdateFrame() end
+				},
+				clickThru =
+				{
+					order = 15,
+					type = "toggle",
+					name = L["Ignorer les clics souris"],
+					get = function(info) return OvaleOptions.db.profile.apparence.clickThru end,
+					set = function(info, value) OvaleOptions.db.profile.apparence.clickThru = value; Ovale:UpdateFrame() end
+				},
+				latencyCorrection =
+				{
+					order = 16,
+					type = "toggle",
+					name = L["Correction de la latence"],
+					get = function(info) return OvaleOptions.db.profile.apparence.latencyCorrection end,
+					set = function(info, value) OvaleOptions.db.profile.apparence.latencyCorrection = value end
+				},
+				hideVehicule =
+				{
+					order = 17,
+					type = "toggle",
+					name = L["Cacher dans les véhicules"],
+					get = function(info) return OvaleOptions.db.profile.apparence.hideVehicule end,
+					set = function(info, value) OvaleOptions.db.profile.apparence.hideVehicule = value end
+				},
+				flashIcon =
+				{
+					order = 18,
+					type = "toggle",
+					name = L["Illuminer l'icône quand le temps de recharge est écoulé"],
+					get = function(info) return OvaleOptions.db.profile.apparence.flashIcon end,
+					set = function(info, value) OvaleOptions.db.profile.apparence.flashIcon = value; Ovale:UpdateFrame() end
+				},
+				targetText =
+				{
+					order = 19,
+					type = "input",
+					name = L["Caractère de portée"],
+					desc = L["Ce caractère est affiché dans un coin de l'icône pour indiquer si la cible est à portée"],
+					get = function(info) return OvaleOptions.db.profile.apparence.targetText end,
+					set = function(info, value) OvaleOptions.db.profile.apparence.targetText = value; Ovale:UpdateFrame() end
+				}
+			}
+		},
+		code =
+		{
+			name = L["Code"],
+			type = "group",
+			args =
+			{
+				code =
+				{
+					order = 1,
+					type = "input",
+					multiline = 15,
+					name = L["Code"],
+					get = function(info)
+						return string.gsub(OvaleOptions.db.profile.code, "\t", "    ")
+					end,
+					set = function(info,v)
+						OvaleOptions.db.profile.code = v
+						Ovale.needCompile = true
+					end,
+					width = "full"
+				}
+			}
+		},
+		actions =
+		{
+			name = "Actions",
+			type = "group",
+			args =
+			{
+				show =
+				{
+					order = -1,
+					type = "execute",
+					name = L["Afficher la fenêtre"],
+					guiHidden = true,
+					func = function()
+						OvaleOptions.db.profile.display = true
+						Ovale:UpdateVisibility()
+					end
+				},
+				hide =
+				{
+					order = -2,
+					type = "execute",
+					name = L["Cacher la fenêtre"],
+					guiHidden = true,
+					func = function()
+						OvaleOptions.db.profile.display = false
+						Ovale.frame:Hide()
+					end
+				},
+				config  =
+				{
+					name = "Configuration",
+					type = "execute",
+					func = function() Ovale:AfficherConfig() end
+				},
+				code  =
+				{
+					name = "Code",
+					type = "execute",
+					func = function() Ovale:AfficherCode() end
+				},
+				debug =
+				{
+					order = -3,
+					name = "Debug",
+					type = "execute",
+					func = function()
+						for i=1,10 do Ovale:Print(i.."="..UnitPower("player", i)) end
+						Ovale:Print(OvaleState.state.eclipse)
+					end
+				},
+				talent =
+				{
+					order = -4,
+					name = "List talent id",
+					type = "execute",
+					func = function()
+						for k,v in pairs(OvaleData.talentNameToId) do
+							Ovale:Print(k.."="..v)
+						end
+					end
+				},
+				targetbuff =
+				{
+					order = -5,
+					name = "List target buff and debuff spell id",
+					type = "execute",
+					func = function()
+						Ovale:DebugListAura("target", "HELPFUL")
+						Ovale:DebugListAura("target", "HARMFUL")
+					end
+				},
+				buff =
+				{
+					order = -6,
+					name = "List player buff and debuff spell id",
+					type = "execute",
+					func = function()
+						Ovale:DebugListAura("player", "HELPFUL")
+						Ovale:DebugListAura("player", "HARMFUL")
+					end
+				},
+				glyph =
+				{
+					order = -7,
+					name = "List player glyphs",
+					type = "execute",
+					func = function()
+						for i=1,GetNumGlyphs() do
+							local name, level, enabled, texture, spellId = GetGlyphInfo(i)
+							if spellId then	Ovale:Print(name..": "..spellId.." ("..tostring(enabled)..")") end
+						end
+					end
+				},
+				spell =
+				{
+					order = -8,
+					name = "List player spells",
+					type = "execute",
+					func = function()
+						local book=BOOKTYPE_SPELL
+						while true do
+							local i=1
+							while true do
+								local skillType, spellId = GetSpellBookItemInfo(i, book)
+								if not spellId then
+									break
+								end
+								local spellName = GetSpellBookItemName(i, book)
+								Ovale:Print(spellName..": "..spellId)
+								i = i + 1
+							end
+							if book == BOOKTYPE_SPELL then
+								book = BOOKTYPE_PET
+							else
+								break
+							end
+						end
+					end
+				}
+			}
+		}
+	}
+}
+--</private-static-properties>
+
+--<public-static-methods>
+function OvaleOptions:OnInitialize()
+
+end
+
+function OvaleOptions:FirstInit()
+	if self.firstInit then
+		return
+	end
+
+	self.firstInit = true
+
+	local localizedClass, englishClass = UnitClass("player")
+	self.db = LibStub("AceDB-3.0"):New("OvaleDB",
+	{
+		profile =
+		{
+			display = true,
+			code = Ovale.defaut[englishClass],
+			left = 500,
+			top = 500,
+			check = {},
+			list = {},
+			apparence = {enCombat=false, iconScale = 2, secondIconScale = 1, margin = 4, fontScale = 0.5, iconShiftX = 0, iconShiftY = 0,
+				smallIconScale=1, raccourcis=true, numeric=false, avecCible = false,
+				verrouille = false, vertical = false, predictif=false, highlightIcon = true, clickThru = false,
+				latencyCorrection=true, hideVehicule=false, flashIcon=true, targetText = "●", alpha = 1,
+				optionsAlpha = 1, updateInterval=0.1}
+		}
+	})
+
+	options.args.profile = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db)
+	AceConfig:RegisterOptionsTable("Ovale", options.args.code)
+	AceConfig:RegisterOptionsTable("Ovale Actions", options.args.actions, "Ovale")
+	AceConfig:RegisterOptionsTable("Ovale Profile", options.args.profile)
+	AceConfig:RegisterOptionsTable("Ovale Apparence", options.args.apparence)
+
+	AceConfigDialog:AddToBlizOptions("Ovale", "Ovale")
+	AceConfigDialog:AddToBlizOptions("Ovale Profile", "Profile", "Ovale")
+	AceConfigDialog:AddToBlizOptions("Ovale Apparence", "Apparence", "Ovale")
+
+	self.db.RegisterCallback( self, "OnNewProfile", "HandleProfileChanges" )
+	self.db.RegisterCallback( self, "OnProfileReset", "HandleProfileChanges" )
+	self.db.RegisterCallback( self, "OnProfileChanged", "HandleProfileChanges" )
+	self.db.RegisterCallback( self, "OnProfileCopied", "HandleProfileChanges" )
+
+	if self.db.profile.code then
+		Ovale.needCompile = true
+	end
+end
+
+function OvaleOptions:HandleProfileChanges()
+	if self.firstInit then
+		if (self.db.profile.code) then
+			Ovale.needCompile = true
+		end
+	end
+end
+
+function OvaleOptions:OnEnable()
+	self:FirstInit()
+
+end
+
+function OvaleOptions:GetApparence()
+	self:FirstInit()
+	return self.db.profile.apparence
+end
+
+function OvaleOptions:GetProfile()
+	self:FirstInit()
+	return self.db.profile
+end
+
+--</public-static-methods>
diff --git a/OvaleSpellDamage.lua b/OvaleSpellDamage.lua
new file mode 100644
index 0000000..6dea01a
--- /dev/null
+++ b/OvaleSpellDamage.lua
@@ -0,0 +1,35 @@
+-- Add-on that registers how many damage made the last spell cast by the player
+
+OvaleSpellDamage = LibStub("AceAddon-3.0"):NewAddon("OvaleSpellDamage", "AceEvent-3.0")
+
+--<public-static-properties>
+OvaleSpellDamage.value = {}
+OvaleSpellDamage.playerGUID = nil
+--</public-static-properties>
+
+-- Events
+--<public-static-methods>
+function OvaleSpellDamage:OnEnable()
+	self.playerGUID = UnitGUID("player")
+	self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
+end
+
+function OvaleSpellDamage:OnDisable()
+	self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
+end
+
+function OvaleSpellDamage:COMBAT_LOG_EVENT_UNFILTERED(event, ...)
+	local time, event, hideCaster, sourceGUID, sourceName, sourceFlags, sourceRaidFlags, destGUID, destName, destFlags, destRaidFlags = select(1, ...)
+
+	if sourceGUID == self.playerGUID then
+		if string.find(event, "SPELL_PERIODIC_DAMAGE")==1 or string.find(event, "SPELL_DAMAGE")==1 then
+			local spellId, spellName, spellSchool, amount = select(12, ...)
+			self.value[spellId] = amount
+		end
+	end
+end
+
+function OvaleSpellDamage:Get(spellId)
+	return self.value[spellId]
+end
+--</public-static-methods>
diff --git a/OvaleState.lua b/OvaleState.lua
new file mode 100644
index 0000000..854c943
--- /dev/null
+++ b/OvaleState.lua
@@ -0,0 +1,404 @@
+-- Keep the current state in the simulation
+
+OvaleState = {}
+
+--<public-static-properties>
+--the state in the current frame
+OvaleState.state = {rune={}, cd = {}, counter={}}
+OvaleState.aura = {}
+OvaleState.serial = 0
+for i=1,6 do
+	OvaleState.state.rune[i] = {}
+end
+--The spell being cast
+OvaleState.currentSpellId = nil
+--Allows to debug auras
+OvaleState.traceAura = false
+OvaleState.maintenant = nil
+OvaleState.currentTime = nil
+OvaleState.attenteFinCast = nil
+OvaleState.startCast = nil
+OvaleState.endCast = nil
+OvaleState.gcd = 1.5
+--</public-static-properties>
+
+--<private-static-properties>
+local UnitGUID = UnitGUID
+--</private-static-properties>
+
+--<private-static-methods>
+local function nilstring(text)
+	if text == nil then
+		return "nil"
+	else
+		return text
+	end
+end
+--</private-static-methods>
+
+--<public-static-methods>
+function OvaleState:StartNewFrame()
+	self.maintenant = GetTime()
+	self.gcd = OvaleData:GetGCD()
+end
+
+function OvaleState:Reset()
+	self.serial = self.serial + 1
+	self.currentTime = self.maintenant
+	self.currentSpellId = nil
+	self.attenteFinCast = self.maintenant
+	self.state.combo = GetComboPoints("player")
+	self.state.mana = UnitPower("player")
+	self.state.shard = UnitPower("player", 7)
+	self.state.eclipse = UnitPower("player", 8)
+	self.state.holy = UnitPower("player", 9)
+	if OvaleData.className == "DEATHKNIGHT" then
+		for i=1,6 do
+			self.state.rune[i].type = GetRuneType(i)
+			local start, duration, runeReady = GetRuneCooldown(i)
+			if runeReady then
+				self.state.rune[i].cd = start
+			else
+				self.state.rune[i].cd = duration + start
+				if self.state.rune[i].cd<0 then
+					self.state.rune[i].cd = 0
+				end
+			end
+		end
+	end
+	for k,v in pairs(self.state.cd) do
+		v.start = nil
+		v.duration = nil
+		v.enable = 0
+		v.toggled = nil
+	end
+
+	for k,v in pairs(self.state.counter) do
+		self.state.counter[k] = OvaleFuture.counter[k]
+	end
+end
+
+-- Cast a spell in the simulator
+-- spellId : the spell id
+-- startCast : temps du cast
+-- endCast : fin du cast
+-- nextCast : temps auquel le prochain sort peut tre lanc (>=endCast, avec le GCD)
+-- nocd : le sort ne dclenche pas son cooldown
+function OvaleState:AddSpellToStack(spellId, startCast, endCast, nextCast, nocd, targetGUID)
+	if not spellId or not targetGUID then
+		return
+	end
+
+	local newSpellInfo = OvaleData.spellInfo[spellId]
+
+	--On enregistre les infos sur le sort en cours
+	self.attenteFinCast = nextCast
+	self.currentSpellId = spellId
+	self.startCast = startCast
+	self.endCast = endCast
+	--Temps actuel de la simulation : un peu aprs le dernier cast (ou maintenant si dans le pass)
+	if startCast>=self.maintenant then
+		self.currentTime = startCast+0.1
+	else
+		self.currentTime = self.maintenant
+	end
+
+	if Ovale.trace then
+		Ovale:Print("add spell "..spellId.." at "..startCast.." currentTime = "..self.currentTime.. " nextCast="..self.attenteFinCast .. " endCast="..endCast)
+	end
+
+	--Effet du sort au moment du dbut du cast
+	--(donc si cast dj commenc, on n'en tient pas compte)
+	if startCast >= self.maintenant then
+		if newSpellInfo then
+			if newSpellInfo.inccounter then
+				local id = newSpellInfo.inccounter
+				self.state.counter[id] = self:GetCounterValue(id) + 1
+			end
+
+			if newSpellInfo.resetcounter then
+				self.state.counter[newSpellInfo.resetcounter] = 0
+			end
+		end
+	end
+
+	--Effet du sort au moment o il est lanc
+	--(donc si il est dj lanc, on n'en tient pas compte)
+	if endCast >= self.maintenant then
+		--Mana
+		local _, _, _, cost = GetSpellInfo(spellId)
+		if cost then
+			self.state.mana = self.state.mana - cost
+		end
+
+		if newSpellInfo then
+
+			if newSpellInfo.mana then
+				self.state.mana = self.state.mana - newSpellInfo.mana
+			end
+
+			--Points de combo
+			if newSpellInfo.combo then
+				self.state.combo = self.state.combo + newSpellInfo.combo
+				if self.state.combo<0 then
+					self.state.combo = 0
+				end
+			end
+			--Runes
+			if newSpellInfo.frost then
+				self:AddRune(startCast, 3, newSpellInfo.frost)
+			end
+			if newSpellInfo.death then
+				self:AddRune(startCast, 4, newSpellInfo.death)
+			end
+			if newSpellInfo.blood then
+				self:AddRune(startCast, 1, newSpellInfo.blood)
+			end
+			if newSpellInfo.unholy then
+				self:AddRune(startCast, 2, newSpellInfo.unholy)
+			end
+			if newSpellInfo.holy then
+				self.state.holy = self.state.holy + newSpellInfo.holy
+				if self.state.holy < 0 then
+					self.state.holy = 0
+				elseif self.state.holy > 3 then
+					self.state.holy = 3
+				end
+			end
+			if newSpellInfo.shard then
+				self.state.shard = self.state.shard + newSpellInfo.shard
+				if self.state.shard < 0 then
+					self.state.shard = 0
+				elseif self.state.shard > 3 then
+					self.state.shard = 3
+				end
+			end
+		end
+	end
+
+	-- Effets du sort au moment o il atteint sa cible
+	if newSpellInfo then
+		-- Cooldown du sort
+		local cd = self:GetCD(spellId)
+		if cd then
+			cd.start = startCast
+			cd.duration = newSpellInfo.cd
+			--Pas de cooldown
+			if nocd then
+				cd.duration = 0
+			end
+			--On vrifie si le buff "buffnocd" est prsent, auquel cas le CD du sort n'est pas dclench
+			if newSpellInfo.buffnocd and not nocd then
+				local buffAura = self:GetAura("player", newSpellInfo.buffnocd)
+				if self.traceAura then
+					if buffAura then
+						Ovale:Print("buffAura stacks = "..buffAura.stacks.." start="..nilstring(buffAura.start).." ending = "..nilstring(buffAura.ending))
+						Ovale:Print("startCast = "..startCast)
+					else
+						Ovale:Print("buffAura = nil")
+					end
+					self.traceAura = false
+				end
+				if buffAura and buffAura.stacks>0 and buffAura.start and buffAura.start<=startCast and (not buffAura.ending or buffAura.ending>startCast) then
+					cd.duration = 0
+				end
+			end
+			if newSpellInfo.targetlifenocd and not nocd then
+				--TODO
+				if UnitHealth("target")/UnitHealthMax("target")*100<newSpellInfo.targetlifenocd then
+					cd.duration = 0
+				end
+			end
+			cd.enable = 1
+			if newSpellInfo.toggle then
+				cd.toggled = 1
+			end
+		end
+
+		if newSpellInfo.eclipse then
+			self.state.eclipse = self.state.eclipse + newSpellInfo.eclipse
+			if self.state.eclipse < -100 then
+				self.state.eclipse = -100
+				self:AddEclipse(endCast, 48518)
+			elseif self.state.eclipse > 100 then
+				self.state.eclipse = 100
+				self:AddEclipse(endCast, 48517)
+			end
+		end
+		if newSpellInfo.starsurge then
+			local buffAura = self:GetAura("player", 48517) --Solar
+			if buffAura and buffAura.stacks>0 then
+				Ovale:Log("starsurge with solar buff = " .. (- newSpellInfo.starsurge))
+				self.state.eclipse = self.state.eclipse - newSpellInfo.starsurge
+			else
+				buffAura = self:GetAura("player", 48518) --Lunar
+				if buffAura and buffAura.stacks>0 then
+					Ovale:Log("starsurge with lunar buff = " .. newSpellInfo.starsurge)
+					self.state.eclipse = self.state.eclipse + newSpellInfo.starsurge
+				elseif self.state.eclipse < 0 then
+					Ovale:Log("starsurge with eclipse < 0 = " .. (- newSpellInfo.starsurge))
+					self.state.eclipse = self.state.eclipse - newSpellInfo.starsurge
+				else
+					Ovale:Log("starsurge with eclipse > 0 = " .. newSpellInfo.starsurge)
+					self.state.eclipse = self.state.eclipse + newSpellInfo.starsurge
+				end
+			end
+			if self.state.eclipse < -100 then
+				self.state.eclipse = -100
+				self:AddEclipse(endCast, 48518)
+			elseif self.state.eclipse > 100 then
+				self.state.eclipse = 100
+				self:AddEclipse(endCast, 48517)
+			end
+		end
+
+		--Auras causs par le sort
+		if newSpellInfo.aura then
+			for target, targetInfo in pairs(newSpellInfo.aura) do
+				for filter, filterInfo in pairs(targetInfo) do
+					for auraSpellId, spellData in pairs(filterInfo) do
+						local newAura
+						if target == "target" then
+							newAura = self:NewAura(targetGUID, auraSpellId)
+						else
+							newAura = self:NewAura(UnitGUID(target), auraSpellId)
+						end
+						newAura.mine = true
+						local duration = spellData
+						local stacks = duration
+						--Optionnellement, on va regarder la dure du buff
+						if auraSpellId and OvaleData.spellInfo[auraSpellId] and OvaleData.spellInfo[auraSpellId].duration then
+							duration = OvaleData.spellInfo[auraSpellId].duration
+						elseif stacks~="refresh" and stacks > 0 then
+							stacks = 1
+						end
+						if stacks=="refresh" then
+							if newAura.ending then
+								newAura.ending = endCast + duration
+							end
+						elseif stacks<0 and newAura.ending then
+							--Buff are immediatly removed when the cast ended, do not need to do it again
+							if filter~="HELPFUL" or target~="player" or endCast>=self.maintenant then
+								newAura.stacks = newAura.stacks + stacks
+								if Ovale.trace then
+									Ovale:Print("removing one stack of "..auraSpellId.." because of ".. spellId.." to ".. newAura.stacks)
+								end
+								--Plus de stacks, on supprime l'aura
+								if newAura.stacks<=0 then
+									Ovale:Log("Aura is completly removed")
+									newAura.stacks = 0
+									newAura.ending = 0
+								end
+							end
+						elseif newAura.ending and newAura.ending >= endCast then
+							newAura.ending = endCast + duration
+							newAura.stacks = newAura.stacks + stacks
+						else
+							newAura.start = endCast
+							newAura.ending = endCast + duration
+							newAura.stacks = stacks
+						end
+						if Ovale.trace then
+							if auraSpellId then
+								Ovale:Print(spellId.." adding "..stacks.." aura "..auraSpellId.." to "..target.." "..filter.." "..newAura.start..","..newAura.ending)
+							else
+								Ovale:Print("adding nil aura")
+							end
+						end
+					end
+				end
+			end
+		end
+	end
+end
+
+function OvaleState:AddRune(time, type, value)
+	if value<0 then
+		for i=1,6 do
+			if (self.state.rune[i].type == type or self.state.rune[i].type==4)and self.state.rune[i].cd<=time then
+				self.state.rune[i].cd = time + 10
+			end
+		end
+	else
+
+	end
+end
+
+function OvaleState:GetCounterValue(id)
+	if self.state.counter[id] then
+		return self.state.counter[id]
+	else
+		return 0
+	end
+end
+
+function OvaleState:GetCD(spellId)
+	if not spellId then
+		return nil
+	end
+
+	if OvaleData.spellInfo[spellId] and OvaleData.spellInfo[spellId].cd then
+		local cdname
+		if OvaleData.spellInfo[spellId].sharedcd then
+			cdname = OvaleData.spellInfo[spellId].sharedcd
+		else
+			cdname = spellId
+		end
+		if not self.state.cd[cdname] then
+			self.state.cd[cdname] = {}
+		end
+		return self.state.cd[cdname]
+	else
+		return nil
+	end
+end
+
+function OvaleState:AddEclipse(endCast, spellId)
+	local newAura = self:NewAura(OvaleGUID.player, spellId)
+	newAura.start = endCast + 0.5
+	newAura.stacks = 1
+	newAura.ending = nil
+end
+
+function OvaleState:GetAura(target, spellId, mine)
+	local guid = UnitGUID(target)
+
+	if self.aura[guid] and self.aura[guid][spellId] and self.aura[guid][spellId].serial == self.serial then
+		return self.aura[guid][spellId]
+	else
+		return OvaleAura:GetAuraByGUID(guid, spellId, mine, target)
+	end
+end
+
+function OvaleState:GetExpirationTimeOnAnyTarget(spellId)
+	local starting, ending = OvaleAura:GetExpirationTimeOnAnyTarget(spellId)
+	for unitId,auraTable in pairs(self.aura) do
+		local aura = auraTable[spellId]
+		if aura and aura.serial == self.serial then
+			local newEnding = aura.ending
+			local newStarting = aura.start
+			if newStarting and (not staring or newStarting < starting) then
+				starting = newStarting
+			end
+			if newEnding and (not ending or newEnding > ending) then
+				ending = newEnding
+			end
+		end
+	end
+	return starting, ending
+end
+
+function OvaleState:NewAura(guid, spellId)
+	if not self.aura[guid] then
+		self.aura[guid] = {}
+	end
+	if not self.aura[guid][spellId] then
+		self.aura[guid][spellId] = {}
+	end
+	local myAura = self.aura[guid][spellId]
+	myAura.serial = self.serial
+	myAura.mine = true
+	myAura.gain = self.currentTime
+	return myAura
+end
+--</public-static-methods>
diff --git a/OvaleSwing.lua b/OvaleSwing.lua
index c372d67..37b410a 100644
--- a/OvaleSwing.lua
+++ b/OvaleSwing.lua
@@ -18,6 +18,22 @@
 	Modifed for Ovale
 ]]

+OvaleSwing = LibStub("AceAddon-3.0"):NewAddon("OvaleSwing", "AceEvent-3.0")
+
+--<public-static-properties>
+OvaleSwing.ohNext = nil
+OvaleSwing.dual = false
+OvaleSwing.starttime = nil
+OvaleSwing.duration = nil
+OvaleSwing.ohStartTime = nil
+OvaleSwing.ohDuration = nil
+OvaleSwing.delay = nil
+OvaleSwing.startdelay = nil
+OvaleSwing.swingmode = nil
+
+--</public-static-properties>
+
+--<private-static-properties>
 local autoshotname = GetSpellInfo(75)
 local resetspells = {
 }
@@ -31,11 +47,10 @@ local _, playerclass = UnitClass('player')
 local unpack = unpack
 local math_abs = math.abs
 local GetTime = GetTime
-
-OvaleSwing = LibStub("AceAddon-3.0"):NewAddon("OvaleSwing", "AceEvent-3.0")
-
 local BOOKTYPE_SPELL = BOOKTYPE_SPELL
+--</private-static-properties>

+--<public-static-methods>
 function OvaleSwing:OnEnable()
 	self.ohNext = false
 	-- fired when autoattack is enabled/disabled.
@@ -86,7 +101,7 @@ end
 function OvaleSwing:COMBAT_LOG_EVENT_UNFILTERED(event, timestamp, eventName, srcGUID, srcName, srcFlags, dstName, dstGUID, dstFlags, ...)
 	if srcName == UnitName("player") then
 		if eventName == "SWING_DAMAGE" or eventName == "SWING_MISSED" then
-			self:MeleeSwing(Ovale.maintenant)
+			self:MeleeSwing(OvaleState.maintenant)
 		end
 	end
 end
@@ -108,7 +123,7 @@ end
 function OvaleSwing:UNIT_SPELLCAST_SUCCEEDED(event, unit, spell)
 	if unit == "player" then
 		if resetspells[spell] then
-			self:MeleeSwing(Ovale.maintenant)
+			self:MeleeSwing(OvaleState.maintenant)
 		end
 		if delayspells[spell] and self.startdelay then
 			self.delay = GetTime() - self.startdelay
@@ -203,3 +218,4 @@ function OvaleSwing:GetNext(which)
 		end
 	end
 end
+--</public-static-methods>
diff --git a/compiler.pl b/compiler.pl
new file mode 100644
index 0000000..2cfce11
--- /dev/null
+++ b/compiler.pl
@@ -0,0 +1,228 @@
+=c
+--<private-static-properties>
+--</private-static-properties>
+
+--<private-static-methods>
+--</private-static-methods>
+
+--<public-static-properties>
+--</public-static-properties>
+
+--<public-static-methods>
+--</public-static-methods>
+
+--<public-methods>
+--</public-methods>
+
+--<public-properties>
+--</public-properties>
+=cut
+
+$p{Skada}{current} = true;
+$p{Skada}{total} = true;
+$m{Skada}{NewModule} = true;
+$m{Skada}{RemoveMode} = true;
+$m{Skada}{get_player} = true;
+$m{Skada}{AddMode} = true;
+
+$p{Recount}{db} = true;
+$p{Recount}{db2} = true;
+$m{Recount}{AddModeTooltip} = true;
+$m{Recount}{AddAmount} = true;
+$m{Recount}{AddSortedTooltipData} = true;
+
+$m{AceGUI}{RegisterWidgetType} = true;
+$m{AceGUI}{RegisterAsContainer} = true;
+$m{AceGUI}{ClearFocus} = true;
+
+$m{DEFAULT_CHAT_FRAME}{AddMessage} = true;
+
+$m{LRC}{GetRange} = true;
+
+$m{AceConfigDialog}{Open} = true;
+$m{AceConfigDialog}{AddToBlizOptions} = true;
+$m{AceConfigDialog}{SetDefaultSize} = true;
+
+$m{Masque}{Group} = true;
+
+$m{AceConfig}{RegisterOptionsTable} = true;
+
+$m{GameTooltip}{SetText} = true;
+$m{GameTooltip}{ClearLines} = true;
+$m{GameTooltip}{SetOwner} = true;
+$m{GameTooltip}{AddDoubleLine} = true;
+$m{GameTooltip}{Show} = true;
+$m{GameTooltip}{Hide} = true;
+$m{GameTooltip}{AddLine} = true;
+
+opendir(DIR, ".");
+while (defined($r = readdir(DIR)))
+{
+	if ($r =~ m/(Ovale.*)\.lua$/)
+	{
+		my $class = $1;
+		open(F, "<", $r);
+		undef $/;
+		my $content = <F>;
+		close(F);
+
+		my %psp = {};
+		my %psm = {};
+		my %pp = {};
+		my %pm = {};
+
+		if ($content =~ m/--inherits (\w+)/)
+		{
+			if ($1 eq 'ActionButtonTemplate')
+			{
+				$m{$class}{Show} = true;
+				$m{$class}{Hide} = true;
+				$m{$class}{SetChecked} = true;
+				$m{$class}{CreateFontString} = true;
+				$m{$class}{RegisterForClicks} = true;
+				$m{$class}{EnableMouse} = true;
+				$m{$class}{GetName} = true;
+			}
+			if ($1 eq 'Frame')
+			{
+				$m{$class}{StartMoving} = true;
+				$m{$class}{StopMovingOrSizing} = true;
+				$m{$class}{GetLeft} = true;
+				$m{$class}{GetTop} = true;
+			}
+		}
+
+		if ($content =~ m/$class\s*=\s*LibStub/)
+		{
+			$pm{'RegisterEvent'} = true;
+			$pm{'UnregisterEvent'} = true;
+			$m{$class}{Print} = true;
+		}
+
+		if ($content =~ m/<private-static-properties>(.*)<\/private-static-properties>/s)
+		{
+			my $psp = $1;
+			while ($psp =~ m/local (\w+)\s*=/g)
+			{
+				$psp{$1} = true;
+			}
+		}
+
+		if ($content =~ m/<private-static-methods>(.*)<\/private-static-methods>/s)
+		{
+			my $psm = $1;
+			while ($psm =~ m/local function (\w+)\s*=/g)
+			{
+				$psm{$1} = true;
+			}
+		}
+
+		if ($content =~ m/<public-static-properties>(.*)<\/public-static-properties>/s)
+		{
+			my $sp = $1;
+			while ($sp =~ m/${class}\.(\w+)\s*=/g)
+			{
+				$sp{$class}{$1} = true;
+			}
+		}
+
+		if ($content =~ m/<public-static-methods>(.*)<\/public-static-methods>/s)
+		{
+			my $sm = $1;
+			while ($sm =~ m/function\s+$class:(\w+)\s*\(/g)
+			{
+				$sm{$class}{$1} = true;
+				delete $m{$class}{$1}
+			}
+		}
+
+		if ($content =~ m/<public-methods>(.*)<\/public-methods>/s)
+		{
+			my $m = $1;
+			while ($m =~ m/local function (\w+)\(self/g)
+			{
+				$m{$class}{$1} = true;
+				delete $sm{$class}{$1}
+			}
+		}
+
+		if ($content =~ m/<public-properties>(.*)<\/public-properties>/s)
+		{
+			my $p = $1;
+			while ($p =~ m/self\.(\w+)/g)
+			{
+				$p{$class}{$1} = true;
+			}
+		}
+
+		while ($content =~ m/\b([A-Z]\w+)\.(\w+)/g)
+		{
+			unless ($sp{$1}{$2} or $p{$1}{$2})
+			{
+				$sp{$1}{$2} = $class;
+			}
+		}
+
+		while ($content =~ m/\b([A-Z]\w+)\:(\w+)/g)
+		{
+			unless ($sm{$1}{$2} or $m{$1}{$2})
+			{
+				$sm{$1}{$2} = $class;
+			}
+		}
+
+		while ($content =~ m/self\.([a-z]\w*)/g)
+		{
+			#if ($class eq 'OvaleSwing')
+			#{
+			#	print $1," ",$sp{$class}{$1}," ",$pp{$1}, " ", $p{$class}{$1},"\n";
+			#}
+			unless ($sp{$class}{$1} eq true or $pp{$1} eq true or $p{$class}{$1} eq true)
+			{
+				print "La classe $class ne contient pas la proprit $1\n";
+			}
+		}
+
+		while ($content =~ m/self\:(\w+)/g)
+		{
+			unless ($sm{$class}{$1} eq true or $pm{$1} eq true or $m{$class}{$1} eq true)
+			{
+				print "La classe $class ne contient pas la mthode $1\n";
+			}
+		}
+	}
+}
+
+for my $class (keys %sm)
+{
+	for my $method (keys %{$sm{$class}})
+	{
+		unless ($sm{$class}{$method} eq true)
+		{
+			print "public static $class:$method $sm{$class}{$method}\n";
+		}
+	}
+}
+
+for my $class (keys %m)
+{
+	for my $method (keys %{$m{$class}})
+	{
+		unless ($m{$class}{$method} eq true)
+		{
+			print "public $class:$method $m{$class}{$method}\n";
+		}
+	}
+}
+
+for my $class (keys %sp)
+{
+	for my $prop (keys %{$sp{$class}})
+	{
+		unless ($sp{$class}{$prop} eq true)
+		{
+			print "public static $class.$prop $sp{$class}{$prop}\n";
+		}
+	}
+}
+
diff --git a/defaut/Pretre.lua b/defaut/Pretre.lua
index 1f8f513..f3742b9 100644
--- a/defaut/Pretre.lua
+++ b/defaut/Pretre.lua
@@ -120,10 +120,10 @@ AddIcon help=mana mastery=3
 }

 #    Add Focus Target Monitor for Multi Dotting
-AddIcon mastery=3
+AddIcon mastery=3 target=focus
 {
-        if TargetDebuffExpires(SHADOWWORDPAIN 1.5 mine=1 target=focus ) and TargetDeadIn(more 10) Spell(SHADOWWORDPAIN target=focus)
-        if TargetDebuffExpires(VAMPIRICTOUCH 3 mine=1 haste=spell target=focus) and TargetDeadIn(more 8) Spell(VAMPIRICTOUCH target=focus)
+        if TargetDebuffExpires(SHADOWWORDPAIN 1.5 mine=1) and TargetDeadIn(more 10) Spell(SHADOWWORDPAIN)
+        if TargetDebuffExpires(VAMPIRICTOUCH 3 mine=1 haste=spell) and TargetDeadIn(more 8) Spell(VAMPIRICTOUCH)
 }

 ]]