----------------------------------------------------------------------------
--	Ranked Matchmaking AI v1.3 New Structure
--	Author: adamqqq		Email:adamqqq@163.com
----------------------------------------------------------------------------
--------------------------------------
-- General Initialization
--------------------------------------
local utility = require(GetScriptDirectory() .. "/utility")
local ability_item_usage_generic = require(GetScriptDirectory() .. "/ability_item_usage_generic")
local AbilityExtensions = require(GetScriptDirectory() .. "/util/AbilityAbstraction")
local ItemUsage = require(GetScriptDirectory() .. "/util/ItemUsage-New")

local debugmode = false
local npcBot = GetBot()
if npcBot == nil or npcBot:IsIllusion() then
	return
end

local Talents = {}
local Abilities = {}
local AbilitiesReal = {}

ability_item_usage_generic.InitAbility(Abilities, AbilitiesReal, Talents)

local AbilityToLevelUp =
{
	Abilities[3],
	Abilities[1],
	Abilities[3],
	Abilities[2],
	Abilities[2],
	Abilities[6],
	Abilities[2],
	Abilities[2],
	Abilities[1],
	"talent",
	Abilities[1],
	Abilities[6],
	Abilities[1],
	Abilities[3],
	"talent",
	Abilities[3],
	"nil",
	Abilities[6],
	"nil",
	"talent",
	"nil",
	"nil",
	"nil",
	"nil",
	"talent",
}
local TalentTree = {
	function()
		return Talents[1]
	end,
	function()
		return Talents[3]
	end,
	function()
		return Talents[5]
	end,
	function()
		return Talents[7]
	end
}
-- check skill build vs current level
utility.CheckAbilityBuild(AbilityToLevelUp)

function BuybackUsageThink()
	ability_item_usage_generic.BuybackUsageThink();
end

function CourierUsageThink()
	ability_item_usage_generic.CourierUsageThink();
end

function AbilityLevelUpThink()
	ability_item_usage_generic.AbilityLevelUpThink2(AbilityToLevelUp, TalentTree)
end

--------------------------------------
-- Ability Usage Thinking
--------------------------------------
local cast = {}
cast.Desire = {}
cast.Target = {}
cast.Type = {}
local Consider = {}
local CanCast = { utility.NCanCast, utility.NCanCast, utility.NCanCast, utility.UCanCast }
local enemyDisabled = utility.enemyDisabled

function GetComboDamage()
	return ability_item_usage_generic.GetComboDamage(AbilitiesReal)
end

function GetComboMana()
	return ability_item_usage_generic.GetComboMana(AbilitiesReal)
end

Consider[1] = function()
	local abilityNumber = 1
	--------------------------------------
	-- Generic Variable Setting
	--------------------------------------
	local ability = AbilitiesReal[abilityNumber];

	if not ability:IsFullyCastable() then
		return BOT_ACTION_DESIRE_NONE, 0;
	end

	local CastRange = 0
	local Damage = ability:GetAbilityDamage()
	local Radius = ability:GetAOERadius() - 50
	local CastPoint = ability:GetCastPoint()

	local blink = AbilityExtensions:GetAvailableBlink(npcBot)
	if (blink ~= nil and blink:IsFullyCastable())
	then
		CastRange = CastRange + 1200
		if (npcBot:GetActiveMode() == BOT_MODE_ATTACK)
		then
			local locationAoE = npcBot:FindAoELocation(true, true, npcBot:GetLocation(), CastRange, Radius, 0, 0);
			if (locationAoE.count >= 2)
			then
				ItemUsage.UseItemOnLocation(npcBot, blink, locationAoE.targetloc);
				return 0
			end
		end
	end


	local allys = npcBot:GetNearbyHeroes(1200, false, BOT_MODE_NONE);
	local enemys = npcBot:GetNearbyHeroes(Radius, true, BOT_MODE_NONE)
	local WeakestEnemy, HeroHealth = utility.GetWeakestUnit(enemys)
	local creeps = npcBot:GetNearbyCreeps(Radius + 300, true)
	local WeakestCreep, CreepHealth = utility.GetWeakestUnit(creeps)
	--------------------------------------
	-- Global high-priorty usage
	--------------------------------------
	-- Check for a channeling enemy
	for _, npcEnemy in pairs(enemys) do
		if (npcEnemy:IsChanneling() and CanCast[abilityNumber](npcEnemy))
		then
			return BOT_ACTION_DESIRE_HIGH --,npcEnemy
		end
	end

	--Try to kill enemy hero
	if (npcBot:GetActiveMode() ~= BOT_MODE_RETREAT)
	then
		if (WeakestEnemy ~= nil)
		then
			if (CanCast[abilityNumber](WeakestEnemy))
			then
				if (
					HeroHealth <= WeakestEnemy:GetActualIncomingDamage(Damage, DAMAGE_TYPE_MAGICAL) and
						GetUnitToUnitDistance(npcBot, WeakestEnemy) <= Radius - CastPoint * WeakestEnemy:GetCurrentMovementSpeed())
				then
					return BOT_ACTION_DESIRE_HIGH --,WeakestEnemy
				end
			end
		end
	end
	--------------------------------------
	-- Mode based usage
	--------------------------------------
	--protect myself
	if ((npcBot:WasRecentlyDamagedByAnyHero(2) and #enemys >= 1) or #enemys >= 2)
	then
		for _, npcEnemy in pairs(enemys) do
			if (CanCast[abilityNumber](npcEnemy))
			then
				return BOT_ACTION_DESIRE_HIGH --,"immediately"
			end
		end
	end

	-- If my mana is enough,use it at enemy
	if (npcBot:GetActiveMode() == BOT_MODE_LANING)
	then
		if ((ManaPercentage > 0.4 or npcBot:GetMana() > ComboMana) and ability:GetLevel() >= 2)
		then
			if (WeakestEnemy ~= nil)
			then
				if (CanCast[abilityNumber](WeakestEnemy))
				then
					if (GetUnitToUnitDistance(npcBot, WeakestEnemy) < Radius - CastPoint * WeakestEnemy:GetCurrentMovementSpeed())
					then
						return BOT_ACTION_DESIRE_LOW --,WeakestEnemy
					end
				end
			end
		end
	end

	-- If we're farming and can hit 2+ creeps
	if (npcBot:GetActiveMode() == BOT_MODE_FARM)
	then
		if (#creeps >= 2)
		then
			if CreepHealth <= WeakestCreep:GetActualIncomingDamage(Damage, DAMAGE_TYPE_MAGICAL) and
				ManaPercentage >= 0.8 + ability:GetManaCost() and #enemys == 0
			then
				return BOT_ACTION_DESIRE_LOW --,WeakestCreep
			end
		end
	end


	-- If we're going after someone
	if (npcBot:GetActiveMode() == BOT_MODE_ROAM or
		npcBot:GetActiveMode() == BOT_MODE_TEAM_ROAM or
		npcBot:GetActiveMode() == BOT_MODE_DEFEND_ALLY or
		npcBot:GetActiveMode() == BOT_MODE_ATTACK)
	then
		local npcEnemy = npcBot:GetTarget();

		if (npcEnemy ~= nil)
		then
			if (
				CanCast[abilityNumber](npcEnemy) and not enemyDisabled(npcEnemy) and
					GetUnitToUnitDistance(npcBot, npcEnemy) <= Radius - CastPoint * npcEnemy:GetCurrentMovementSpeed())
			then
				return BOT_ACTION_DESIRE_MODERATE --,npcEnemy
			end
		end
	end

	return BOT_ACTION_DESIRE_NONE

end

Consider[2] = function()
	local abilityNumber = 2
	--------------------------------------
	-- Generic Variable Setting
	--------------------------------------
	local ability = AbilitiesReal[abilityNumber];

	if not ability:IsFullyCastable() then
		return BOT_ACTION_DESIRE_NONE, 0;
	end

	local function PrioritiseStompFactor(enemy)
		if npcBot:HasModifier("modifier_centaur_stampede") and AbilitiesReal[1]:IsFullyCastable() and CanCast[1](enemy) then
			return 0.25
		else
			return 1
		end
	end

	local CastRange = ability:GetCastRange();
	local Damage = ability:GetAbilityDamage();
	local selfDamage = npcBot:GetActualIncomingDamage(Damage, DAMAGE_TYPE_MAGICAL)
	if HealthPercentage <= 0.2 + selfDamage then
		return 0
	end


	local allys = npcBot:GetNearbyHeroes(1200, false, BOT_MODE_NONE);
	local enemys = npcBot:GetNearbyHeroes(CastRange + 300, true, BOT_MODE_NONE)
	local WeakestEnemy, HeroHealth = utility.GetWeakestUnit(enemys)
	local creeps = npcBot:GetNearbyCreeps(CastRange + 300, true)
	local WeakestCreep, CreepHealth = utility.GetWeakestUnit(creeps)

	--try to kill enemy hero
	if (npcBot:GetActiveMode() ~= BOT_MODE_RETREAT)
	then
		if (WeakestEnemy ~= nil)
		then
			if (CanCast[abilityNumber](WeakestEnemy))
			then
				if (
					HeroHealth <= WeakestEnemy:GetActualIncomingDamage(Damage, DAMAGE_TYPE_MAGICAL) or
						(
						HeroHealth <= WeakestEnemy:GetActualIncomingDamage(GetComboDamage(), DAMAGE_TYPE_MAGICAL) and
							npcBot:GetMana() > ComboMana))
				then
					return BOT_ACTION_DESIRE_HIGH * PrioritiseStompFactor(WeakestEnemy), WeakestEnemy
				end
			end
		end
	end
	--------------------------------------
	-- Mode based usage
	--------------------------------------
	-- If we're farming and can hit 2+ creeps and kill 1+
	if (npcBot:GetActiveMode() == BOT_MODE_FARM)
	then
		if (#creeps >= 3)
		then
			if (
				CreepHealth <= WeakestCreep:GetActualIncomingDamage(Damage, DAMAGE_TYPE_MAGICAL) and HealthPercentage >=
					0.4 + selfDamage)
			then
				return BOT_ACTION_DESIRE_LOW, WeakestCreep
			end
		end
	end

	-- If we're pushing or defending a lane and can hit 3+ creeps, go for it
	if (npcBot:GetActiveMode() == BOT_MODE_PUSH_TOWER_TOP or
		npcBot:GetActiveMode() == BOT_MODE_PUSH_TOWER_MID or
		npcBot:GetActiveMode() == BOT_MODE_PUSH_TOWER_BOT)
	then
		if (#creeps >= 3)
		then
			if (
				CanCast[abilityNumber](creeps[1]) and GetUnitToUnitDistance(npcBot, creeps[1]) < CastRange + 75 * #allys and
					HealthPercentage >= 0.6 - 0.05 * #allys)
			then
				return BOT_ACTION_DESIRE_LOW, creeps[1]
			end
		end
	end

	-- If we're going after someone
	if (npcBot:GetActiveMode() == BOT_MODE_ROAM or
		npcBot:GetActiveMode() == BOT_MODE_TEAM_ROAM or
		npcBot:GetActiveMode() == BOT_MODE_DEFEND_ALLY or
		npcBot:GetActiveMode() == BOT_MODE_ATTACK)
	then
		local npcEnemy = npcBot:GetTarget();
		if (npcEnemy ~= nil)
		then
			if (
				CanCast[abilityNumber](npcEnemy) and GetUnitToUnitDistance(npcBot, npcEnemy) < CastRange + 75 * #allys and
					HealthPercentage >= 0.6 - 0.05 * #allys)
			then
				return BOT_ACTION_DESIRE_MODERATE * PrioritiseStompFactor(npcEnemy), npcEnemy
			end
		end
	end

	return BOT_ACTION_DESIRE_NONE, 0

end

-- retaliate is no longer an active ability
-- Consider[3]=function()

-- 	local abilityNumber=3
-- 	--------------------------------------
-- 	-- Generic Variable Setting
-- 	--------------------------------------
-- 	local ability=AbilitiesReal[abilityNumber];

-- 	if not ability:IsFullyCastable() then
-- 		return BOT_ACTION_DESIRE_NONE, 0;
-- 	end

-- 	-- Get some of its values
-- 	local nRadius = 300;
-- 	local maxStacks = ability:GetSpecialValueInt('max_stacks');
-- 	local stack = 0;
-- 	local modIdx = npcBot:GetModifierByName("modifier_centaur_return_counter");
-- 	if modIdx > -1 then
-- 		stack = npcBot:GetModifierStackCount(modIdx);
-- 	end
-- 	if stack <= maxStacks / 2 then
-- 		return BOT_ACTION_DESIRE_NONE;
-- 	end
-- 	--------------------------------------
-- 	-- Mode based usage
-- 	--------------------------------------
-- 	if ( npcBot:GetActiveMode() == BOT_MODE_ROSHAN  )
-- 	then
-- 		local npcTarget = npcBot:GetAttackTarget();
-- 		if ( utility.IsRoshan(npcTarget) and GetUnitToUnitDistance(npcBot,npcTarget) < nRadius )
-- 		then
-- 			return BOT_ACTION_DESIRE_LOW;
-- 		end
-- 	end

-- 	-- If we're going after someone
-- 	if ( npcBot:GetActiveMode() == BOT_MODE_ROAM or
-- 		 npcBot:GetActiveMode() == BOT_MODE_TEAM_ROAM or
-- 		 npcBot:GetActiveMode() == BOT_MODE_DEFEND_ALLY or
-- 		 npcBot:GetActiveMode() == BOT_MODE_ATTACK )
-- 	then
-- 		local npcTarget = npcBot:GetTarget();
-- 		if ( CanCast[abilityNumber]( npcTarget ) and GetUnitToUnitDistance(npcBot,npcTarget) < nRadius )
-- 		then
-- 			return BOT_ACTION_DESIRE_HIGH;
-- 		end
-- 	end
-- 	return BOT_ACTION_DESIRE_NONE;
-- end

Consider[6] = function()

	local abilityNumber = 6
	--------------------------------------
	-- Generic Variable Setting
	--------------------------------------
	local ability = AbilitiesReal[abilityNumber];

	if not ability:IsFullyCastable() then
		return BOT_ACTION_DESIRE_NONE, 0;
	end

	local CastRange = 1200
	local allys = npcBot:GetNearbyHeroes(1200, false, BOT_MODE_NONE);
	local enemys = npcBot:GetNearbyHeroes(CastRange, true, BOT_MODE_NONE)
	local WeakestEnemy, HeroHealth = utility.GetWeakestUnit(enemys)

	--try to kill enemy hero
	if (npcBot:GetActiveMode() ~= BOT_MODE_RETREAT)
	then
		if (WeakestEnemy ~= nil)
		then
			if (CanCast[abilityNumber](WeakestEnemy))
			then
				if (
					(
						HeroHealth <= WeakestEnemy:GetActualIncomingDamage(GetComboDamage(), DAMAGE_TYPE_MAGICAL) and
							npcBot:GetMana() > ComboMana) and GetUnitToUnitDistance(npcBot, WeakestEnemy) >= 600)
				then
					return BOT_ACTION_DESIRE_HIGH;
				end
			end
		end
	end

	-- If we're seriously retreating, see if we can land a stun on someone who's damaged us recently
	if (npcBot:GetActiveMode() == BOT_MODE_RETREAT and npcBot:GetActiveModeDesire() >= BOT_MODE_DESIRE_HIGH)
	then
		if (npcBot:WasRecentlyDamagedByAnyHero(2.0))
		then
			return BOT_ACTION_DESIRE_MODERATE
		end
	end
	--------------------------------------
	-- Mode based usage
	--------------------------------------

	-- If we're going after someone
	if (npcBot:GetActiveMode() == BOT_MODE_ROAM or
		npcBot:GetActiveMode() == BOT_MODE_TEAM_ROAM or
		npcBot:GetActiveMode() == BOT_MODE_DEFEND_ALLY or
		npcBot:GetActiveMode() == BOT_MODE_ATTACK)
	then
		if (#allys >= 2)
		then
			local npcEnemy = AbilityExtensions:GetTargetIfGood(npcBot)
			if (npcEnemy ~= nil)
			then
				if (CanCast[abilityNumber](npcEnemy) and GetUnitToUnitDistance(npcBot, npcEnemy) >= 600)
				then
					return BOT_ACTION_DESIRE_MODERATE;
				end
			end
		end
	end

	return BOT_ACTION_DESIRE_NONE, 0;

end

AbilityExtensions:AutoModifyConsiderFunction(npcBot, Consider, AbilitiesReal)

local stompLosingTarget

function AbilityUsageThink()

	-- Check if we're already using an ability
	if npcBot:IsUsingAbility() or npcBot:IsChanneling() or npcBot:IsSilenced() then
		if npcBot:IsCastingAbility() then
			if npcBot:GetCurrentActiveAbility() == AbilitiesReal[1] then
				if not AbilityExtensions:IsFarmingOrPushing(npcBot) then
					local nearbyEnemies = AbilityExtensions:GetNearbyEnemyUnits(npcBot, AbilitiesReal[1]:GetAOERadius() + 90)
					if AbilityExtensions:Count(nearbyEnemies, CanCast[1]) == 0 then
						if stompLosingTarget == nil then
							stompLosingTarget = DotaTime()
						elseif DotaTime() - stompLosingTarget > 0.15 then
							npcBot:Action_ClearActions(true)
						end
						return
					end
				end
			end
		end
		stompLosingTarget = nil
		return
	end

	ComboMana = GetComboMana()
	AttackRange = npcBot:GetAttackRange()
	ManaPercentage = npcBot:GetMana() / npcBot:GetMaxMana()
	HealthPercentage = npcBot:GetHealth() / npcBot:GetMaxHealth()

	cast = ability_item_usage_generic.ConsiderAbility(AbilitiesReal, Consider)
	---------------------------------debug--------------------------------------------
	if (debugmode == true)
	then
		ability_item_usage_generic.PrintDebugInfo(AbilitiesReal, cast)
	end
	ability_item_usage_generic.UseAbility(AbilitiesReal, cast)
end

function CourierUsageThink()
	ability_item_usage_generic.CourierUsageThink()
end
