local GUID_TYPE_MASK = 0x00F

local combatStartTime = 0
local combatEndTime = GetTime()
local inCombat = false
local lastBlockableAttackTaken = 0
local lastUnblockableAttackTaken = 0
local enemyCount = 0
local enemyPlayerCount = 0
local knownEnemies = {}
local knownPlayerEnemies = {}

local events = {}

function events:PLAYER_REGEN_ENABLED() 
	inCombat = false
	combatEndTime = GetTime()
	wipe(knownEnemies)
	wipe(knownPlayerEnemies)
	enemyCount = 0
	enemyPlayerCount = 0
end

function events:PLAYER_REGEN_DISABLED() 
	inCombat = true
	combatStartTime = GetTime()
	wipe(knownEnemies)
	wipe(knownPlayerEnemies)
	enemyCount = 0
	enemyPlayerCount = 0
end

function IsPlayerGUID(guid)
	local upper = tonumber(guid:sub(1,5))
	local type = bit.band(upper,GUID_TYPE_MASK)
	return type == 0
end

function events:COMBAT_LOG_EVENT_UNFILTERED(timeStamp,event,hideCaster,sourceGUID,sourceName,sourceFlags,sourceRaidFlags,destGUID,destName,destFlags,destRaidFlags,spellID,spellName) 
	if inCombat then
		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
				if IsPlayerGUID(sourceGUID) then
					enemyPlayerCount = enemyPlayerCount + 1
					knownPlayerEnemies[sourceGUID] = 1
				end
			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
			if IsPlayerGUID(destGUID) then
				enemyPlayerCount = enemyPlayerCount + 1
				knownPlayerEnemies[destGUID] = 1
			end
		end                                            
		
		if (event == "UNIT_DIED" or event == "PARTY_KILL") and knownEnemies[destGUID] == 1 then
			knownEnemies[destGUID] = 0
			enemyCount = enemyCount - 1
			if IsPlayerGUID(destGUID) then
				enemyPlayerCount = enemyPlayerCount - 1
				knownPlayerEnemies[destGUID] = 0
			end
		end
	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	

WhatsNext:AddConditionalStatement("Combat Time",{
	category = "Combat",
	test = function(checkTable)
		return inCombat and 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}
	}
})

WhatsNext:AddConditionalStatement("No Combat Time",{
	category = "Combat",
	test = function(checkTable)
		return not inCombat and GetTime() - combatEndTime >= tonumber(checkTable.time)
	end,
	text = function(checkTable) 
		return "player has been out of combat for at least " .. tostring(checkTable.time) .. " seconds."
	end,
	arguments = {
		time = {type = "integer", default = 3}
	}
})

WhatsNext:AddConditionalStatement("In Combat",{
	category = "Combat",
	test = function(checkTable) 
		return inCombat
	end,
	text = function(checkTable) 
		return "player is in combat."
	end,
	arguments = {}
})

WhatsNext:AddConditionalStatement("In PvP Combat",{
	category = "Combat",
	test = function(checkTable) 
		return inCombat and enemyPlayerCount > 0
	end,
	text = function(checkTable) 
		return "player is in PvP combat."
	end,
	arguments = {}
})

WhatsNext:AddConditionalStatement("NPC In Combat",{
	category = "Combat",
	test = function(checkTable) 
		return select(2,UnitDetailedThreatSituation(tostring(checkTable.unit),tostring(checkTable.npcUnit)))
	end,
	text = function(checkTable) 
		return tostring(checkTable.unit) .. " is in combat with " .. tostring(checkTable.npcUnit) .. " NPC."
	end,
	arguments = {
		unit = {type = "dropdown", default = "player", possibleValues = WhatsNext.valueLists.unitAny},
		npcUnit = {type = "dropdown", default = "target", possibleValues = WhatsNext.valueLists.unitAny},
	}
})

WhatsNext:AddConditionalStatement("Maximum Enemy Count",{
	category = "Combat",
	test = function(checkTable) 
		return enemyCount <= tonumber(checkTable.amount)
	end,
	text = function(checkTable) 
		return "player is in combat with " .. tostring(checkTable.amount) .. " or fewer known enemies."
	end,
	arguments = {
		amount = {type = "integer", default = 2}
	}
})

WhatsNext:AddConditionalStatement("Minimum Enemy Count",{
	category = "Combat",
	test = function(checkTable) 
		return enemyCount >= tonumber(checkTable.amount)
	end,
	text = function(checkTable) 
		return "player is in combat with " .. tostring(checkTable.amount) .. " or more known enemies."
	end,
	arguments = {
		amount = {type = "integer", default = 2}
	}
})

WhatsNext:AddConditionalStatement("Maximum Player Enemy Count",{
	category = "Combat",
	test = function(checkTable) 
		return enemyPlayerCount <= tonumber(checkTable.amount)
	end,
	text = function(checkTable) 
		return "player is in combat with " .. tostring(checkTable.amount) .. " or fewer known enemy players."
	end,
	arguments = {
		amount = {type = "integer", default = 2}
	}
})

WhatsNext:AddConditionalStatement("Minimum Player Enemy Count",{
	category = "Combat",
	test = function(checkTable) 
		return enemyPlayerCount >= tonumber(checkTable.amount)
	end,
	text = function(checkTable) 
		return "player is in combat with " .. tostring(checkTable.amount) .. " or more known enemy players."
	end,
	arguments = {
		amount = {type = "integer", default = 2}
	}
})

WhatsNext:AddConditionalStatement("In Boss Fight",{
	category = "Combat",
	test = function(checkTable) 
		return inCombat and (UnitExists("boss1") or UnitExists("boss2") or UnitExists("boss3") or UnitExists("boss4"))
	end,
	text = function(checkTable) 
		return "player is in combat with a boss enemy unit."
	end,
	arguments = {}
})

WhatsNext:AddConditionalStatement("Tanking", {
	category = "Tanking",
	test = function(checkTable) 
		local status = UnitThreatSituation(tostring(checkTable.unit))
		return status and status > 1
	end,
	text = function(checkTable) 
		return tostring(checkTable.unit) .. " is tanking at least 1 enemy NPC."
	end,
	arguments = {
		unit = {type = "dropdown", default = "player", possibleValues = WhatsNext.valueLists.unitGroup},
	}
})

WhatsNext:AddConditionalStatement("Tanking Target",{
	category = "Tanking",
	test = function(checkTable) 
		local status = UnitThreatSituation(tostring(checkTable.unit1),tostring(checkTable.unit2))
		return status and status > 1
	end,
	text = function(checkTable) 
		return tostring(checkTable.unit1) .. " is tanking the " .. tostring(checkTable.unit2) .. " NPC."
	end,
	arguments = {
		unit1 = {type = "dropdown", default = "player", possibleValues = WhatsNext.valueLists.unitGroup},
		unit2 = {type = "dropdown", default = "target", possibleValues = WhatsNext.valueLists.unitAny},
	}
})

WhatsNext:AddConditionalStatement("Taking Damage",{
	category = "Tanking",
	test = function(checkTable) 
		return GetTime() - math.max(lastBlockableAttackTaken,lastUnblockableAttackTaken) <= tonumber(checkTable.time) 
	end,
	text = function(checkTable) 
		return "player has taken damage from any enemy unit in the last " .. tostring(checkTable.time)  .. " seconds."
	end,
	arguments = {
		time = {type = "integer", default = 4}
	}
})

WhatsNext:AddConditionalStatement("Taking Blockable Damage",{
	category = "Tanking",
	test = function(checkTable) 
		return GetTime() - lastBlockableAttackTaken <= tonumber(checkTable.time) 
	end,
	text = function(checkTable) 
		return "player has taken melee or range damage from any enemy unit in the last " .. tostring(checkTable.time) .. " seconds."
	end,
	arguments = {
		time = {type = "integer", default = 4}
	}
})

WhatsNext:AddConditionalStatement("Taking Unblockable Damage",{
	category = "Tanking",
	test = function(checkTable) 
		return GetTime() - lastUnblockableAttackTaken <= tonumber(checkTable.time) 
	end,
	text = function(checkTable) 
		return "player has taken spell damage from any enemy unit in the last " .. tostring(checkTable.time)  .. " seconds."
	end,
	arguments = {
		time = {type = "integer", default = 4}
	}
})