
local DAMAGE_TAKEN_MEMORY_LENGTH = 4
local EARLY_CAST_EXPIRE_MARGIN = 3
local MINIMUM_TIME_BETWEEN_INTERRUPTS = .3

local dbmTimers = {}
local combatStartTime = 0
local lastInterrupt = 0
local checkResults = {}
local inCombat = false
local lastBlockableAttackTaken = 0
local lastUnblockableAttackTaken = 0
local lastNotBehindTarget = 0
local enemyCount = 0
local categories
local knownEnemies = {}
local powerGeneration = {
	["Revenge"] = 15,
	["Shield Slam"] = 20,
	["Berserker Rage"] = 10,
	["Battle Shout"] = 20,
	["Commanding Shout"] = 20,
	["Sword and Board"] = 5,
	["Mangle"] = 5,
}

if DBM then
	local tempMod = DBM:NewMod("temp")
	local tempTimer = tempMod:NewTimer(-1,"temp",-1)
	local meta = getmetatable(tempTimer).__index
	local Start = meta.Start
	meta.Start = function(self,timer, ...)
		local bar = Start(self,timer,...)
		if bar then
			if not dbmTimers[bar.frame] then
				local OnHide = bar.frame:GetScript("OnHide")
				bar.frame:SetScript("OnHide",function(self) 
					if OnHide then 
						OnHide(self)
					end
					dbmTimers[bar] = ""
				end)
			end
			dbmTimers[bar] = _G[bar.frame:GetName().."BarName"]:GetText()
		end
		return bar
	end
end
	
local function GetItemID(itemName)
	return select(5, string.find(select(2, GetItemInfo(itemName)), "|?c?f?f?(%x*)|?H?([^:]*):?(%d+):?(%d*):?(%d*):?(%d*):?(%d*):?(%d*):?(%-?%d*):?(%-?%d*):?(%d*)|?h?%[?([^%[%]]*)%]?|?h?|?r?"))
end



function WhatsNext_GetPowerGeneration(spellName)
	local rtnAmount = powerGeneration[spellName] or 0
	if spellName == "Shield Slam" and UnitBuff("player", "Sword and Board") then
		rtnAmount = rtnAmount + powerGeneration["Sword and Board"] 
	end
	
	return rtnAmount
end

local function GetRangeToRaidMember(unit)
	local _, zx1, zy1, zx2, zy2 = GetCurrentMapZone();
	local px,py = GetPlayerMapPosition("player");
	local rx,ry = GetPlayerMapPosition(unit);
	if (rx == 0 and ry == 0) or (px == 0 and py == 0) or not ( zx1 and zx2 and zy1 and zy2) then
		return 100000
	end
	local dx,dy = math.abs(px - rx) * (zx1 - zx2), math.abs(py - ry) * (zy1 - zy2)
	return math.sqrt(dx*dx + dy*dy)
end

local checks = {
	["Combat Time"] = {
		category = "Basic",
		test = function(checkTable)
			return GetTime() - combatStartTime >= tonumber(checkTable.time)
		end,
		text = function(checkTable) 
			return "player has been in combat for at least " .. tostring(checkTable.time) .. " seconds."
		end,
		arguments = {
			time = {type = "integer", default = 3}
		}
	},
	["Minimum Power"] = {
		category = "Power",
		test = function(checkTable) 
			return UnitPower("player") >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's power is at or above " .. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0}
		}
	},
	["Minimum Power Percent"] = {
		category = "Power",
		test = function(checkTable) 
			local maxPower = UnitPowerMax("player")
			return tonumber(checkTable.amount) <= (maxPower and maxPower > 0 and (UnitPower("player") / maxPower) or 0)
		end,
		text = function(checkTable) 
			local amountNum = tonumber(checkTable.amount)
			return "player's power is at or above " .. tostring(amountNum and amountNum*100 or checkTable.amount) .. "%."
		end,
		arguments = {
			amount = {type = "percent", default = .5}
		}
	},
	["Excess Power"] = {
		category = "Power",
		test = function(checkTable) 
			return UnitPowerMax("player") < UnitPower("player") + WhatsNext_GetPowerGeneration(checkTable.spellName)
		end,
		text = function(checkTable) 
			return tostring(checkTable.spellName) .. " would generate excess power."
		end,
		arguments = {
			spellName = {type = "text", default = ""}
		}
	},
	["Maximum Power"] = {
		category = "Power",
		test = function(checkTable) 
			return UnitPower("player") <= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's power is at or below " .. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0}
		}
	},
	["Maximum Power Percent"] = {
		category = "Power",
		test = function(checkTable) 
			local maxPower = UnitPowerMax("player")
			return tonumber(checkTable.amount) >= (maxPower and maxPower > 0 and (UnitPower("player") / maxPower) or 0)
		end,
		text = function(checkTable) 
			local amountNum = tonumber(checkTable.amount)
			return "player's power is at or below " .. tostring(amountNum and amountNum*100 or checkTable.amount) .. "%."
		end,
		arguments = {
			amount = {type = "percent", default = .5}
		}
	},
	["Minimum Missing Power"] = {
		category = "Power",
		test = function(checkTable) 
			return UnitPowerMax("player") - UnitPower("player") >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's power is at least " .. tostring(checkTable.amount) .. " below their maximum power."
		end,
		arguments = {
			amount = {type = "integer", default = 0}
		}
	},
	["Maximum Missing Power"] = {
		category = "Power",
		test = function(checkTable) 
			return UnitPowerMax("player") - UnitPower("player") <= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's power is no more than " .. tostring(checkTable.amount) .. " below their maximum power."
		end,
		arguments = {
			amount = {type = "integer", default = 0}
		}
	},
	["Minimum Resultant Power"] = {
		category = "Power",
		test = function(checkTable) 
			local _, _, _, cost, _, _, _, _, _ = GetSpellInfo(checkTable.spellName)
			return UnitPower("player") - cost >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's power would be at least " .. tostring(checkTable.amount) .. " after casting ".. tostring(checkTable.spellName) .."."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
			spellName = {type = "text", default = ""}
		}
	},
	["Maximum Resultant Power"] = {
		category = "Power",
		test = function(checkTable) 
			local _, _, _, cost, _, _, _, _, _ = GetSpellInfo(checkTable.spellName)
			return UnitPowerMax("player") - cost <= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's power would be no more than " .. tostring(checkTable.amount) .. " after casting ".. tostring(checkTable.spellName) .."."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
			spellName = {type = "text", default = ""}
		}
	},
	["Minimum Cost"] = {
		category = "Ability",
		test = function(checkTable) 
			local _, _, _, cost, _, _, _, _, _ = GetSpellInfo(checkTable.spellName)
			return cost >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return tostring(checkTable.spellName) .. " costs at least " .. tostring(checkTable.amount) .. " power."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
			spellName = {type = "text", default = ""}
		}
	},
	["Maximum Cost"] = {
		category = "Ability",
		test = function(checkTable) 
			local _, _, _, cost, _, _, _, _, _ = GetSpellInfo(checkTable.spellName)
			return cost <= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return tostring(checkTable.spellName) .. " costs no more than " .. tostring(checkTable.amount) .. " power."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
			spellName = {type = "text", default = ""}
		}
	},
	["Target Is Interruptable"] = {
		category = "Casting",
		test = function(checkTable) 
			local spell1, _, _, _, _, _, _, _, notInterruptible1  = UnitCastingInfo("target")
			local spell2, _, _, _, _, _, _, notInterruptible2  = UnitChannelInfo("target")
			return (spell1 and not notInterruptible1)  or (spell2 and not notInterruptible2)
		end,
		text = function(checkTable) 
			return "target is casting an interruptable spell."
		end,
		arguments = {}
	},
	["Target Is Casting"] = {
		category = "Casting",
		test = function(checkTable) 
			return UnitCastingInfo("target") or UnitChannelInfo("target")
		end,
		text = function(checkTable) 
			return "target is casting a spell."
		end,
		arguments = {}
	},
	["Target Is Casting Specific"] = {
		category = "Casting",
		test = function(checkTable) 
			return UnitCastingInfo("target") == checkTable.spellName or UnitChannelInfo("target") == checkTable.spellName
		end,
		text = function(checkTable) 
			return "target is casting " .. tostring(checkTable.spellName)  .. "."
		end,
		arguments = {
			spellName = {type = "text", default = ""}
		}
	},
	["Target Is Uninterruptable"] = {
		category = "Casting",
		test = function(checkTable) 
			local spell1, _, _, _, _, _, _, _, notInterruptible1  = UnitCastingInfo("target")
			local spell2, _, _, _, _, _, _, notInterruptible2  = UnitChannelInfo("target")
			return (spell1 and notInterruptible1)  or (spell2 and notInterruptible2)
		end,
		text = function(checkTable) 
			return "target is casting an uninterruptable spell."
		end,
		arguments = {}
	},
	["Tanking"] = {
		category = "Tanking",
		test = function(checkTable) 
			local status = UnitThreatSituation("player")
			return status and status > 1
		end,
		text = function(checkTable) 
			return "player is tanking at least 1 enemy NPC."
		end,
		arguments = {}
	},
	["Tanking Target"] = {
		category = "Tanking",
		test = function(checkTable) 
			local status = UnitThreatSituation("player")
			return status and status > 1
		end,
		text = function(checkTable) 
			return "player is tanking the target NPC."
		end,
		arguments = {}
	},
	["Player Crowd Controlled"] = {
		category = "Basic",
		test = function(checkTable) 
			return HasFullControl() ~= 1
		end,
		text = function(checkTable) 
			return "player is suffering from some form of crowd control."
		end,
		arguments = {}
	}, 
	["Target Has Debuff"] = {
		category = "Buff/Debuff",
		test = function(checkTable)
			local name, _, _, count, _, _, _  = UnitDebuff("target", checkTable.debuffName)
			return UnitName("target") and name and math.max(count,1) >= tonumber(checkTable.count or 1)
		end,
		text = function(checkTable)
			return "target has " .. tostring(checkTable.count  or 1) .. " stack(s) of the " .. tostring(checkTable.debuffName) .. " debuff."
		end,
		arguments = {
			debuffName = {type = "text", default = ""},
			count = {type = "integer", default = 1}
		}
	},
	["Target Target Has Debuff"] = {
		category = "Buff/Debuff",
		test = function(checkTable)
			local name, _, _, count, _, _, _  = UnitDebuff("targettarget", checkTable.debuffName)
			return UnitName("targettarget") and not UnitIsUnit("targettarget","player") and name and math.max(count,1) >= tonumber(checkTable.count or 1)
		end,
		text = function(checkTable)
			return "targettarget has " .. tostring(checkTable.count  or 1) .. " stack(s) of the " .. tostring(checkTable.debuffName) .. " debuff."
		end,
		arguments = {
			debuffName = {type = "text", default = ""},
			count = {type = "integer", default = 1}
		}
	},
	["Target Target Need Debuff"] = {
		category = "Buff/Debuff",
		test = function(checkTable) 
			local name, _, _, count, _, _, expirationTime  = UnitDebuff("targettarget", checkTable.debuffName)
			return UnitName("targettarget") and (not name or (math.max(count,1) < tonumber(checkTable.count or 1)) or expirationTime - tonumber(checkTable.time or EARLY_CAST_EXPIRE_MARGIN) < GetTime())
		end,
		text = function(checkTable) 
			return "targettarget does not have " .. tostring(checkTable.count or 1) .. " stack(s) of " .. tostring(checkTable.debuffName) .. " or it will drop off within " .. tostring(checkTable.time or EARLY_CAST_EXPIRE_MARGIN) .. " seconds." 
		end,
		arguments = {
			debuffName = {type = "text", default = ""},
			time = {type = "integer", default = 3},
			count = {type = "integer", default = 1}
		}
	},
	["Target Need Debuff"] = {
		category = "Buff/Debuff",
		test = function(checkTable) 
			local name, _, _, count, _, _, expirationTime  = UnitDebuff("target", checkTable.debuffName)
			return UnitName("target") and (not name or (math.max(count,1) < tonumber(checkTable.count or 1)) or expirationTime - tonumber(checkTable.time or EARLY_CAST_EXPIRE_MARGIN) < GetTime())
		end,
		text = function(checkTable) 
			return "target does not have " .. tostring(checkTable.count or 1) .. " stack(s) of " .. tostring(checkTable.debuffName) .. " or it will drop off within " .. tostring(checkTable.time or EARLY_CAST_EXPIRE_MARGIN) .. " seconds." 
		end,
		arguments = {
			debuffName = {type = "text", default = ""},
			time = {type = "integer", default = 3},
			count = {type = "integer", default = 1}
		}
	},
	["Target Has Debuff From Player"] = {
		category = "Buff/Debuff",
		test = function(checkTable)
			for i=1,40 do
				local name, _, _, count, _, _, _, unit  = UnitDebuff("target", i)
				if checkTable.debuffName == name and unit and UnitIsUnit(unit,"player") then
					return math.max(count,1) >= tonumber(checkTable.count or 1)
				end			
			end
		end,
		text = function(checkTable)
			return "target has " .. tostring(checkTable.count  or 1) .. " stack(s) of the " .. tostring(checkTable.debuffName) .. " debuff from player."
		end,
		arguments = {
			debuffName = {type = "text", default = ""},
			count = {type = "integer", default = 1}
		}
	},
	["Target Need Debuff From Player"] = {
		category = "Buff/Debuff",
		test = function(checkTable) 
			for i=1,40 do
				local name, _, _, count, _, _, expirationTime, unit  = UnitDebuff("target", i)
				if name and checkTable.debuffName == name and unit and UnitIsUnit(unit,"player") then
					return math.max(count,1) < tonumber(checkTable.count or 1) or expirationTime - tonumber(checkTable.time or EARLY_CAST_EXPIRE_MARGIN) < GetTime()
				end			
			end
			return true
		end,
		text = function(checkTable) 
			return "target does not have " .. tostring(checkTable.count or 1) .. " stack(s) of " .. tostring(checkTable.debuffName) .. " from player or they will drop off within " .. tostring(checkTable.time or EARLY_CAST_EXPIRE_MARGIN) .. " seconds." 
		end,
		arguments = {
			debuffName = {type = "text", default = ""},
			time = {type = "integer", default = 3},
			count = {type = "integer", default = 1}
		}
	},
	["Unit Is Unit"] = {
		category = "Basic",
		test = function(checkTable)
			return UnitExists(checkTable.unit1) and UnitExists(checkTable.unit2) and UnitIsUnit(checkTable.unit1,checkTable.unit2)
		end,
		text = function(checkTable)
			return tostring(checkTable.unit1) .. " is the same unit as " .. tostring(checkTable.unit2) .. "."
		end,
		arguments = {
			unit1 = {type = "unit", default = "target"},
			unit2 = {type = "unit", default = "player"},
		},
	},
	["Unit Exists"] = {
		category = "Basic",
		test = function(checkTable)
			return UnitExists(checkTable.unit)
		end,
		text = function(checkTable)
			return tostring(checkTable.unit1) .. " exists."
		end,
		arguments = {
			unit = {type = "unit", default = "target"},
		},
	},
	["Player Need Buff From Player"] = {
		category = "Buff/Debuff",
		test = function(checkTable) 
			for i=1,40 do
				local name, _, _, count, _, _, expirationTime, unit = UnitBuff("player",i) 
				if checkTable.buffName == name and unit and UnitIsUnit(unit,"player") then
					return math.max(count,1) < tonumber(checkTable.count or 1) or expirationTime - tonumber(checkTable.time or EARLY_CAST_EXPIRE_MARGIN) < GetTime()
				end			
			end
			return true
		end,
		text = function(checkTable) 
			return "player does not have " .. tostring(checkTable.count or 1) .. " stack(s) of " .. tostring(checkTable.buffName) .. " from player or they will drop off within " .. tostring(checkTable.time or EARLY_CAST_EXPIRE_MARGIN) .. " seconds." 
		end,
		arguments = {
			buffName = {type = "text", default = ""},
			time = {type = "integer", default = 3},
			count = {type = "integer", default = 1}
		}
	},
	["Player Need Buff"] = {
		category = "Buff/Debuff",
		test = function(checkTable) 
			for i=1,40 do
				local name, _, _, count, _, _, expirationTime = UnitBuff("player",i) 
				if checkTable.buffName == name then
					return math.max(count,1) < tonumber(checkTable.count or 1) or expirationTime - tonumber(checkTable.time or EARLY_CAST_EXPIRE_MARGIN) < GetTime()
				end			
			end
			return true
		end,
		text = function(checkTable) 
			return "player does not have " .. tostring(checkTable.count or 1) .. " stack(s) of " .. tostring(checkTable.buffName) .. " or they will drop off within " .. tostring(checkTable.time or EARLY_CAST_EXPIRE_MARGIN) .. " seconds." 
		end,
		arguments = {
			buffName = {type = "text", default = ""},
			time = {type = "integer", default = 3},
			count = {type = "integer", default = 1}
		}
	},
	["In Range"] = {
		category = "Position",
		test = function(checkTable) 
			return UnitName("target") and IsSpellInRange(checkTable.spellName,"target") == 1
		end,
		text = function(checkTable) 
			return "target is within range of " .. tostring(checkTable.spellName) .. "."
		end,
		arguments = {
			spellName = {type = "text", default = ""}
		}
	},
	["Target In 9.9 Yards"] = {
		category = "Position",
		test = function(checkTable) 
			return UnitName("target") and CheckInteractDistance("target", 3)
		end,
		text = function(checkTable) 
			return "target is within 9.9 yards."
		end,
		arguments = {}
	},
	["Target In 11.11 Yards"] = {
		category = "Position",
		test = function(checkTable) 
			return UnitName("target") and CheckInteractDistance("target", 2)
		end,
		text = function(checkTable) 
			return "target is within 11.11 yards."
		end,
		arguments = {}
	},
	["Target In 28 Yards"] = {
		category = "Position",
		test = function(checkTable) 
			return UnitName("target") and CheckInteractDistance("target", 1)
		end,
		text = function(checkTable) 
			return "target is within 28 yards."
		end,
		arguments = {}
	},
	["In Combat"] = {
		category = "Basic",
		test = function(checkTable) 
			return inCombat
		end,
		text = function(checkTable) 
			return "player is in combat."
		end,
		arguments = {}
	},
	["Taking Damage"] = {
		category = "Tanking",
		test = function(checkTable) 
			return GetTime() - math.max(lastBlockableAttackTaken,lastUnblockableAttackTaken) <= tonumber(checkTable.time or DAMAGE_TAKEN_MEMORY_LENGTH) 
		end,
		text = function(checkTable) 
			return "player has taken damage from an enemy unit in the last " .. tostring(checkTable.time or DAMAGE_TAKEN_MEMORY_LENGTH)  .. " seconds."
		end,
		arguments = {
			time = {type = "integer", default = 4}
		}
	},
	["Taking Blockable Damage"] = {
		category = "Tanking",
		test = function(checkTable) 
			return GetTime() - lastBlockableAttackTaken <= tonumber(checkTable.time or DAMAGE_TAKEN_MEMORY_LENGTH) 
		end,
		text = function(checkTable) 
			return "player has taken blockable damage from an enemy unit in the last " .. tostring(checkTable.time or DAMAGE_TAKEN_MEMORY_LENGTH) .. " seconds."
		end,
		arguments = {
			time = {type = "integer", default = 4}
		}
	},
	["Taking Unblockable Damage"] = {
		category = "Tanking",
		test = function(checkTable) 
			return GetTime() - lastUnblockableAttackTaken <= tonumber(checkTable.time or DAMAGE_TAKEN_MEMORY_LENGTH) 
		end,
		text = function(checkTable) 
			return "player has taken unblockable damage from an enemy unit in the last " .. tostring(checkTable.time or DAMAGE_TAKEN_MEMORY_LENGTH)  .. " seconds."
		end,
		arguments = {
			time = {type = "integer", default = 4}
		}
	},
	["Maximum Health Percent"] = {
		category = "Health",
		test = function(checkTable) 
			return UnitHealth("player")/UnitHealthMax("player") <= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			local amountNum = tonumber(checkTable.amount)
			return "player's health percent is less than or equal to " .. tostring(amountNum and amountNum*100 or checkTable.amount) .. "%."
		end,
		arguments = {
			amount = {type = "percent", default = .5}
		}
	},
	["Target Maximum Health Percent"] = {
		category = "Health",
		test = function(checkTable) 
			return UnitHealth("target")/UnitHealthMax("target") <= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			local amountNum = tonumber(checkTable.amount)
			return "target's health percent is less than or equal to " .. tostring(amountNum and amountNum*100 or checkTable.amount) .. "%."
		end,
		arguments = {
			amount = {type = "percent", default = .5}
		}
	},
	["Maximum Enemy Count"] = {
		category = "Basic",
		test = function(checkTable) 
			return enemyCount <= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "exists " .. tostring(checkTable.amount) .. " or fewer known enemies."
		end,
		arguments = {
			amount = {type = "integer", default = 2}
		}
	},
	["Minimum Enemy Count"] = {
		category = "Basic",
		test = function(checkTable) 
			return enemyCount >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "exists " .. tostring(checkTable.amount) .. " or more known enemies."
		end,
		arguments = {
			amount = {type = "integer", default = 2}
		}
	},
	["Attackable Target"] = {
		category = "Basic",
		test = function(checkTable) 
			return UnitName("target") and UnitCanAttack("player","target") and not UnitIsDeadOrGhost("target") 
		end,
		text = function(checkTable) 
			return "target can be attacked."
		end,
		arguments = {}
	},
	["In Boss Fight"] = {
		category = "Basic",
		test = function(checkTable) 
			return UnitExists("boss1") or UnitExists("boss2") or UnitExists("boss3") or UnitExists("boss4")
		end,
		text = function(checkTable) 
			return "player is in combat with an boss enemy unit."
		end,
		arguments = {}
	},
	["Target Is"] = {
		category = "Basic",
		test = function(checkTable) 
			return UnitName("target") == checkTable.name
		end,
		text = function(checkTable) 
			return "target's name is ".. tostring(checkTable.name) .. "."
		end,
		arguments = {
			name = {type = "text", default = ""}
		}
	},
	["Player Buffed"] = {
		category = "Buff/Debuff",
		test = function(checkTable) 
			local name, _, _, count, _, _, _, unit = UnitBuff("player", checkTable.buffName) 
			return name and math.max(count,1) >=  tonumber(checkTable.count or 1) 
		end,
		text = function(checkTable) 
			return "player has " .. tostring(checkTable.count or 1) .. " stack(s) of the " .. tostring(checkTable.buffName) .. " buff."
		end,
		arguments = {
			buffName = {type = "text", default = ""},
			count = {type = "integer", default = 1}
		}
	},
	["Player Buffed By Player"] = {
		category = "Buff/Debuff",
		test = function(checkTable) 
			local name, _, _, count, _, _, _, unit = UnitBuff("player", checkTable.buffName) 
			return name and unit and UnitIsUnit(unit,"player") and math.max(count,1) >=  tonumber(checkTable.count or 1) 
		end,
		text = function(checkTable) 
			return "player has " .. tostring(checkTable.count or 1) .. " stack(s) of their own " .. tostring(checkTable.buffName) .. " buff."
		end,
		arguments = {
			buffName = {type = "text", default = ""},
			count = {type = "integer", default = 1}
		}
	},
	["Raid Buffed"] = {
		category = "Buff/Debuff",
		test = function(checkTable) 
			if not UnitBuff("player", checkTable.buffName) then
				return false
			end
			for i = 1,40 do
				local raidUnit = "raid"..i
				if UnitExists(raidUnit) and GetRangeToRaidMember(i) <= tonumber(checkTable.yards) and not UnitBuff(raidUnit, checkTable.buffName) then
					return false
				end				
			end
			return true
		end,
		text = function(checkTable) 
			return "all raid members in " .. tostring(checkTable.yards) .. " yards have the " .. tostring(checkTable.buffName) .. " buff."
		end,
		arguments = {
			buffName = {type = "text", default = ""},
			yards = {type = "integer", default = 1}
		}
	},
	["Raid Buffed By Player"] = {
		category = "Buff/Debuff",
		test = function(checkTable)
			local name, _, _, _, _, _, _, unit = UnitBuff("player", checkTable.buffName) 
			if not name or not unit or not UnitIsUnit(unit,"player") then
				return false
			end	
			for i = 1,40 do
				local raidUnit = "raid"..i
				if UnitExists(raidUnit) then
					local name, _, _, _, _, _, _, unit = UnitBuff(raidUnit, checkTable.buffName) 
					if GetRangeToRaidMember(raidUnit) <= tonumber(checkTable.yards) and (not name or not unit or not UnitIsUnit(unit,"player")) then
						return false
					end				
				end
			end
			return true
		end,
		text = function(checkTable) 
			return "all raid members in " .. tostring(checkTable.yards) .. " yards have player's " .. tostring(checkTable.buffName) .. " buff."
		end,
		arguments = {
			buffName = {type = "text", default = ""},
			yards = {type = "integer", default = 1}
		}
	},
	["Ability Cooldown"] = {
		category = "Ability",
		test = function(checkTable)
			local start, duration = GetSpellCooldown(checkTable.spellName)
			return start and start > 0 and start + duration - GetTime() > tonumber(checkTable.time)
		end,
		text = function(checkTable)
			return tostring(checkTable.spellName) .. " will be on cooldown for more than " .. tostring(checkTable.time) .. " second(s)."
		end,
		arguments = {
			spellName = {type = "text", default = ""},
			time = {type = "integer", default = 3}
		}
	},
	["Combo Points"] = {
		category = "Rogue",
		test = function(checkTable) 
			return GetComboPoints("player","target") >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player has at least "..tostring(checkTable.amount).." combo points on target."
		end,
		arguments = {
			amount = {type = "integer", default = 5},
		}
	},
	["Stealthed"] = {
		category = "Basic",
		test = function(checkTable) 
			return IsStealthed() == 1
		end,
		text = function(checkTable) 
			return "player is stealthed."
		end,
		arguments = {}
	},
	["Strength"] = {
		category = "Primary Stats",
		test = function(checkTable) 
			return select(2,UnitStat("player",1)) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's total strength is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Base Strength"] = {
		category = "Primary Stats",
		test = function(checkTable) 
			return select(1,UnitStat("player",1)) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's base strength is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Agility"] = {
		category = "Primary Stats",
		test = function(checkTable) 
			return select(2,UnitStat("player",2)) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's total agility is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Base Agility"] = {
		category = "Primary Stats",
		test = function(checkTable) 
			return select(1,UnitStat("player",2)) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's base agility is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Stamina"] = {
		category = "Primary Stats",
		test = function(checkTable) 
			return select(2,UnitStat("player",3)) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's total stamina is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Base Stamina"] = {
		category = "Primary Stats",
		test = function(checkTable) 
			return select(1,UnitStat("player",3)) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's base stamina is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Intellect"] = {
		category = "Primary Stats",
		test = function(checkTable) 
			return select(2,UnitStat("player",4)) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's total intellect is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Base Intellect"] = {
		category = "Primary Stats",
		test = function(checkTable) 
			return select(1,UnitStat("player",4)) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's base intellect is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Spirit"] = {
		category = "Primary Stats",
		test = function(checkTable) 
			return select(2,UnitStat("player",5)) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's total spirit is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Base Spirit"] = {
		category = "Primary Stats",
		test = function(checkTable) 
			return select(1,UnitStat("player",5)) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's base spirit is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Dodge Rating"] = {
		category = "Secondary Stats",
		test = function(checkTable) 
			return GetCombatRating(3) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's dodge rating is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Parry Rating"] = {
		category = "Secondary Stats",
		test = function(checkTable) 
			return GetCombatRating(4) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's parry rating is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Melee Hit Rating"] = {
		category = "Secondary Stats",
		test = function(checkTable) 
			return GetCombatRating(6) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's melee hit rating is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Range Hit Rating"] = {
		category = "Secondary Stats",
		test = function(checkTable) 
			return GetCombatRating(7) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's range hit rating is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Spell Hit Rating"] = {
		category = "Secondary Stats",
		test = function(checkTable) 
			return GetCombatRating(8) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's spell hit rating is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Melee Crit Rating"] = {
		category = "Secondary Stats",
		test = function(checkTable) 
			return GetCombatRating(9) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's melee crit rating is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Range Crit Rating"] = {
		category = "Secondary Stats",
		test = function(checkTable) 
			return GetCombatRating(10) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's range crit rating is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Spell Crit Rating"] = {
		category = "Secondary Stats",
		test = function(checkTable) 
			return GetCombatRating(11) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's spell crit rating is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Melee Haste Rating"] = {
		category = "Secondary Stats",
		test = function(checkTable) 
			return GetCombatRating(19) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's melee haste rating is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Range Haste Rating"] = {
		category = "Secondary Stats",
		test = function(checkTable) 
			return GetCombatRating(19) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's range haste rating is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Spell Haste Rating"] = {
		category = "Secondary Stats",
		test = function(checkTable) 
			return GetCombatRating(20) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's spell haste rating is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Expertise Rating"] = {
		category = "Secondary Stats",
		test = function(checkTable) 
			return GetCombatRating(24) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's expertise rating is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Mastery Rating"] = {
		category = "Secondary Stats",
		test = function(checkTable) 
			return GetCombatRating(26) >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player's mastery rating is at or above ".. tostring(checkTable.amount) .. "."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Rune Ready Count"] = {
		category = "Death Knight",
		test = function(checkTable) 
			local count = 0
			for i = 1,6 do
				if select(3,GetRuneCooldown(i)) then
					count = count + 1
				end
			end
			return  count >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player has at least ".. tostring(checkTable.amount) .. " runes off cooldown."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Blood Rune Ready Count"] = {
		category = "Death Knight",
		test = function(checkTable) 
			local count = 0
			for i = 1,6 do
				local type = GetRuneType(i)
				if select(3,GetRuneCooldown(i)) and (type == 1 or type == 4) then
					count = count + 1
				end
			end
			return  count >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player has at least ".. tostring(checkTable.amount) .. " blood/death runes off cooldown."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Frost Rune Ready Count"] = {
		category = "Death Knight",
		test = function(checkTable) 
			local count = 0
			for i = 1,6 do
				local type = GetRuneType(i)
				if select(3,GetRuneCooldown(i)) and (type == 3 or type == 4) then
					count = count + 1
				end
			end
			return  count >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player has at least ".. tostring(checkTable.amount) .. " frost/death runes off cooldown."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Unholy Rune Ready Count"] = {
		category = "Death Knight",
		test = function(checkTable) 
			local count = 0
			for i = 1,6 do
				local type = GetRuneType(i)
				if select(3,GetRuneCooldown(i)) and (type == 2 or type == 4) then
					count = count + 1
				end
			end
			return  count >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player has at least ".. tostring(checkTable.amount) .. " unholy/death runes off cooldown."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Blood Rune Simple Ready Count"] = {
		category = "Death Knight",
		test = function(checkTable) 
			local count = 0
			for i = 1,6 do
				local type = GetRuneType(i)
				if select(3,GetRuneCooldown(i)) and type == 1 then
					count = count + 1
				end
			end
			return  count >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player has at least ".. tostring(checkTable.amount) .. " blood runes off cooldown."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Frost Rune Simple Ready Count"] = {
		category = "Death Knight",
		test = function(checkTable) 
			local count = 0
			for i = 1,6 do
				local type = GetRuneType(i)
				if select(3,GetRuneCooldown(i)) and type == 3 then
					count = count + 1
				end
			end
			return  count >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player has at least ".. tostring(checkTable.amount) .. " frost runes off cooldown."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Unholy Rune Simple Ready Count"] = {
		category = "Death Knight",
		test = function(checkTable) 
			local count = 0
			for i = 1,6 do
				local type = GetRuneType(i)
				if select(3,GetRuneCooldown(i)) and type == 2 then
					count = count + 1
				end
			end
			return  count >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player has at least ".. tostring(checkTable.amount) .. " unholy runes off cooldown."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Death Rune Ready Count"] = {
		category = "Death Knight",
		test = function(checkTable) 
			local count = 0
			for i = 1,6 do
				local type = GetRuneType(i)
				if select(3,GetRuneCooldown(i)) and type == 4 then
					count = count + 1
				end
			end
			return  count >= tonumber(checkTable.amount)
		end,
		text = function(checkTable) 
			return "player has at least ".. tostring(checkTable.amount) .. " death runes off cooldown."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Rune Type Ready Count"] = {
		category = "Death Knight",
		test = function(checkTable) 
			local rmBlood = tonumber(checkTable.bloodAmount)
			local rmFrost = tonumber(checkTable.frostAmount)
			local rmUnholy = tonumber(checkTable.unholyAmount)
			local deathCount = 0
			for i = 1,6 do
				local type = GetRuneType(i)
				if select(3,GetRuneCooldown(i)) then
					if type == 1 then
						rmBlood = math.max(0,rmBlood - 1)
					elseif type == 2 then
						rmUnholy = math.max(0,rmUnholy - 1)
					elseif type == 2 then
						rmFrost = math.max(0,rmFrost - 1)
					else
						deathCount = deathCount + 1
					end
				end
			end
			return  rmBlood + rmFrost + rmUnholy <= deathCount
		end,
		text = function(checkTable) 
			return "player has at least ".. tostring(checkTable.bloodAmount) .. " blood " .. tostring(checkTable.frostAmount) .. " frost and " .. tostring(checkTable.unholyAmount) .. " unholy rune(s) off cooldown."
		end,
		arguments = {
			unholyAmount = {type = "integer", default = 0},
			bloodAmount = {type = "integer", default = 0},
			frostAmount = {type = "integer", default = 0},
		}
	},
	["Time Till Death"] = {
		category = "Death Clocks",
		test = function(checkTable) 
			local unit = DeathClock and DeathClock.units and DeathClock.units[UnitGUID("target")]
			return unit and unit.time and unit.time <= tonumber(checkTable.time) 
		end,
		text = function(checkTable) 
			return "target is expected to die within " .. tostring(checkTable.time) .. " seconds. (Requires the Death Clocks addon)"
		end,
		arguments = {
			time = {type = "integer", default = 0},
		}
	},
	["Life Expectancy"] = {
		category = "Death Clocks",
		test = function(checkTable) 
			local unit = DeathClock and DeathClock.units and DeathClock.units[UnitGUID("target")]
			return unit and unit.time and unit.time >= tonumber(checkTable.time) 
		end,
		text = function(checkTable) 
			return "target is expected to live for at least " .. tostring(checkTable.time) .. " seconds. (Requires the Death Clocks addon)"
		end,
		arguments = {
			time = {type = "integer", default = 0},
		}
	},
	["Holy Power"] = {
		category = "Paladin",
		test = function(checkTable) 
			return tonumber(checkTable.amount) <= UnitPower("player" ,9)
		end,
		text = function(checkTable) 
			return "player has at least " .. tostring(checkTable.amount) .. " holy power."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Holy Power Percent"] = {
		category = "Paladin",
		test = function(checkTable) 
			local maxPower = UnitPowerMax("player",9)
			return tonumber(checkTable.amount) <= (maxPower and maxPower > 0 and (UnitPower("player" ,9) / maxPower) or 0)
		end,
		text = function(checkTable) 
			local amountNum = tonumber(checkTable.amount)
			return "player has at least " .. tostring(amountNum and amountNum*100 or checkTable.amount) .. "% of their maximum holy power."
		end,
		arguments = {
			amount = {type = "percent", default = 0},
		}
	},
	["Soul Shards"] = {
		category = "Warlock",
		test = function(checkTable) 
			return tonumber(checkTable.amount) <= UnitPower("player" ,7)
		end,
		text = function(checkTable) 
			return "player has at least " .. tostring(checkTable.amount) .. " soul shards."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Soul Shards Percent"] = {
		category = "Warlock",
		test = function(checkTable) 
			local maxPower = UnitPowerMax("player",7)
			return tonumber(checkTable.amount) <= (maxPower and maxPower > 0 and (UnitPower("player" ,7) / maxPower) or 0)
		end,
		text = function(checkTable) 
			local amountNum = tonumber(checkTable.amount)
			return "player has at least " .. tostring(amountNum and amountNum*100 or checkTable.amount) .. "% of their maximum soul shards."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Burning Embers"] = {
		category = "Warlock",
		test = function(checkTable) 
			return tonumber(checkTable.amount) <= UnitPower("player" ,14)
		end,
		text = function(checkTable) 
			return "player has at least " .. tostring(checkTable.amount) .. " burning embers."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Burning Embers Percent"] = {
		category = "Warlock",
		test = function(checkTable) 
			local maxPower = UnitPowerMax("player",14)
			return tonumber(checkTable.amount) <= (maxPower and maxPower > 0 and (UnitPower("player" ,14) / maxPower) or 0)
		end,
		text = function(checkTable) 
			local amountNum = tonumber(checkTable.amount)
			return "player has at least " .. tostring(amountNum and amountNum*100 or checkTable.amount) .. "% of their maximum burning embers."
		end,
		arguments = {
			amount = {type = "percent", default = 0},
		}
	},
	["Demonic Fury"] = {
		category = "Warlock",
		test = function(checkTable) 
			return tonumber(checkTable.amount) <= UnitPower("player" ,15)
		end,
		text = function(checkTable) 
			return "player has at least " .. tostring(checkTable.amount) .. " demonic fury."
		end,
		arguments = {
			amount = {type = "integer", default = 0},
		}
	},
	["Demonic Fury Percent"] = {
		category = "Warlock",
		test = function(checkTable) 
			local maxPower = UnitPowerMax("player",15)
			return tonumber(checkTable.amount) <= (maxPower and maxPower > 0 and (UnitPower("player" ,15) / maxPower) or 0)
		end,
		text = function(checkTable) 
			local amountNum = tonumber(checkTable.amount)
			return "player has at least " .. tostring(amountNum and amountNum*100 or checkTable.amount) .. "% of their maximum demonic fury."
		end,
		arguments = {
			amount = {type = "percent", default = 0},
		}
	},
	["Not Behind Target Detected"] = {
		category = "Position",
		test = function(checkTable) 
			return GetTime() - lastNotBehindTarget <= tonumber(checkTable.time)
		end,
		text = function(checkTable) 
			return "has been detected player was not behind their target in the last " .. tostring(checkTable.time) .. " seconds."
		end,
		arguments = {
			time = {type = "integer", default = 0},
		}
	
	},
	
	["Item Equipped"] = {
		category = "Items",
		test = function(checkTable) 
			return IsEquippedItem(checkTable.itemName) == 1
		end,
		text = function(checkTable) 
			return "player is wearing " .. tostring(checkTable.itemName) .. "."
		end,
		arguments = {
			itemName = {type = "text", default = ""},
		}
	},
	["Item On Cooldown"] = {
		category = "Items",
		test = function(checkTable) 
			local start, duration, enable = GetItemCooldown(GetItemID(checkTable.itemName))
			return start + duration - GetTime() > tonumber(checkTable.time)
		end,
		text = function(checkTable) 
			return tostring(checkTable.itemName) .. " will be on cooldown for more than " .. tostring(checkTable.time) .. " seconds."
		end,
		arguments = {
			itemName = {type = "text", default = ""},
			time = {type = "integer", default = 3},
		}
	},
	["Item Disabled"] = {
		category = "Items",
		test = function(checkTable) 
			return select(3,GetItemCooldown(GetItemID(checkTable.itemName))) == 0
		end,
		text = function(checkTable) 
			return tostring(checkTable.itemName) .. "'s cooldown timer won't start until out of combat."
		end,
		arguments = {
			itemName = {type = "text", default = ""},
		}
	},
	["DBM Timer Shown"] = {
		category = "Deadly Boss Mods",
		test = function(checkTable)
			for bar,text in pairs(dbmTimers) do 
				if text == checkTable.timerLabel then
					return true
				end
			end
			return false
		end,
		text = function(checkTable) 
			return "timer "..tostring(checkTable.timerLabel) .. " is shown. (Requires DBM)"
		end,
		arguments = {
			timerLabel = {type = "text", default = ""},
		}
	},
	["DBM Timer Time Remaining"] = {
		category = "Deadly Boss Mods",
		test = function(checkTable)
			for bar,text in pairs(dbmTimers) do 
				if text == checkTable.timerLabel then
					return tonumber(bar.timer) > tonumber(checkTable.time) 
				end
			end
			return false
		end,
		text = function(checkTable) 
			return "timer "..tostring(checkTable.timerLabel) .. " has more than " .. tostring(checkTable.time) .. " seconds remaining. (Requires DBM)"
		end,
		arguments = {
			timerLabel = {type = "text", default = ""},
			time = {type = "integer", default = 3},
		}
	},
	["DBM Timer Less Time Remaining"] = {
		category = "Deadly Boss Mods",
		test = function(checkTable)
			for bar,text in pairs(dbmTimers) do 
				if text == checkTable.timerLabel then
					return tonumber(bar.timer) < tonumber(checkTable.time) 
				end
			end
			return false
		end,
		text = function(checkTable) 
			return "timer "..tostring(checkTable.timerLabel) .. " has less than " .. tostring(checkTable.time) .. " seconds remaining. (Requires DBM)"
		end,
		arguments = {
			timerLabel = {type = "text", default = ""},
			time = {type = "integer", default = 3},
		}
	},
	["Seal"] = {
		category="Paladin",
		test = function(checkTable)
			return checkTable.seal == (GetShapeshiftForm() and GetShapeshiftForm() > 0 and select(2,GetShapeshiftFormInfo(GetShapeshiftForm())) or 0)
		end,
		text = function(checkTable)
			return "player's active seal is "..tostring(checkTable.seal) .. "."
		end,
		arguments = {
			seal = {type = "text", default = ""},
		}
	},
	["Stance"] = {
		category="Warrior",
		test = function(checkTable)
			return checkTable.stance == (GetShapeshiftForm() and GetShapeshiftForm() > 0 and select(2,GetShapeshiftFormInfo(GetShapeshiftForm())) or 0)
		end,
		text = function(checkTable)
			return "player's active stance is "..tostring(checkTable.stance) .. "."
		end,
		arguments = {
			stance = {type = "text", default = ""},
		}
	},
	["Form"] = {
		category="Druid",
		test = function(checkTable)
			return checkTable.form == (GetShapeshiftForm() and GetShapeshiftForm() > 0 and select(2,GetShapeshiftFormInfo(GetShapeshiftForm())) or 0)
		end,
		text = function(checkTable)
			return "player's active form is "..tostring(checkTable.form) .. "."
		end,
		arguments = {
			form = {type = "text", default = ""},
		}
	}
}

function WhatsNext:ResetTestChecks()
	SetMapToCurrentZone() 
	wipe(checkResults)
end

function WhatsNext:TestCheck(checkTable)
	if not checkResults[self:GetCheckText(checkTable)] then
		if checks[checkTable.type].test(checkTable) then
			checkResults[self:GetCheckText(checkTable)] = checkTable.invert and 0 or 1
		else
			checkResults[self:GetCheckText(checkTable)] = checkTable.invert and 1 or 0
		end
	end
	return checkResults[self:GetCheckText(checkTable)] == 1
end

function WhatsNext:TestChecks(checkList)
	for i,checkTable in ipairs(checkList or {}) do
		if not self:TestCheck(checkTable) then
			return false
		end
	end
	return true
end

function WhatsNext:GetArguments(checkType)
	if checks[checkType] then
		return checks[checkType].arguments
	end
end

function WhatsNext:GetCheckText(checkTable)
	if checks[checkTable.type] then
		return (checkTable.invert and "Not if " or "If ") .. checks[checkTable.type].text(checkTable)
	end
end

function WhatsNext:GetSampleCheckTexts(category)
	local sampleTexts = {}
	for checkType, check in pairs(checks) do
		if not category or category == check.category then
			sampleTexts[checkType] = self:GetSampleCheckText(checkType)
		end
	end
	return sampleTexts
end

function WhatsNext:GetCheckCategories()
	if not categories then
		categories = {}
		for type, check in pairs(checks) do
			 if not tContains(categories,check.category) then
				tinsert(categories,check.category)
			 end
		end
	end
	return categories
end

function WhatsNext:GetSampleCheckText(checkType)
	local check = checks[checkType]
	local checkTable = {type = checkType}
	for var,varType in pairs(check.arguments) do
		checkTable[var] = "{"..var.."}"
	end
	return  "If " .. check.text(checkTable)
end

-------------------
-- Set up events
-------------------

local events = {}

function events:UI_ERROR_MESSAGE(message)
	if message == "You must be behind your target." then
		lastNotBehindTarget = GetTime()
	end
end	

function events:PLAYER_REGEN_ENABLED() 
	inCombat = false
	wipe(knownEnemies)
	enemyCount = 0
end

function events:PLAYER_REGEN_DISABLED() 
	inCombat = true
	combatStartTime = GetTime()
	enemyCount = 0
end

function events:ADDON_LOADED() 
end

function events:COMBAT_LOG_EVENT_UNFILTERED(timeStamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceRaidFlags,destGUID,destName,destFlags,destRaidFlags,spellID,spellName) 
	local damage = strsub(event,-6)  == "DAMAGE"
	if UnitGUID("player") == destGUID then
		if damage and sourceGUID and not knownEnemies[sourceGUID] then
			enemyCount = enemyCount + 1
			knownEnemies[sourceGUID] = 1
		end
		if event == "SWING_DAMAGE" or event == "SWING_MISSED" or event == "RANGE_DAMAGE" or event == "RANGE_MISSED" then
			lastBlockableAttackTaken = GetTime()
		elseif event == "SPELL_DAMAGE" or event == "SPELL_PERIODIC_DAMAGE" then
			lastUnblockableAttackTaken = GetTime()
		elseif event == "SPELL_MISSED" or event == "SPELL_PERIODIC_MISSED" then
			lastUnblockableAttackTaken = GetTime()
		end
	elseif damage and UnitGUID("player") == sourceGUID and destGUID and not knownEnemies[destGUID] then
		enemyCount = enemyCount + 1
		knownEnemies[destGUID] = 1
	end
	
	if (event == "UNIT_DIED" or event == "PARTY_KILL") and knownEnemies[destGUID] == 1 then
		knownEnemies[destGUID] = 0
		enemyCount = enemyCount - 1
	end
end

local function EventFunc(self, event, ...)
	events[event](event, ...)
end
local eventHandler = CreateFrame("Frame", nil, WorldFrame)
eventHandler:SetScript("OnEvent", EventFunc)
for eventname in pairs(events) do eventHandler:RegisterEvent(eventname) end	