local vector_origin = Vector()

local timed_blocking_cv = GetConVar("sv_tfa_melee_blocking_timed")
local angle_mult_cv = GetConVar("sv_tfa_melee_blocking_anglemult")
local deflect_cv = GetConVar("sv_tfa_melee_blocking_deflection")
local stun_cv = GetConVar("sv_tfa_melee_blocking_stun_enabled")
local stuntime_cv = GetConVar("sv_tfa_melee_blocking_stun_time")

local bul = {
	HullSize = 5,
	Num = 1,
	Tracer = 1,
	AmmoType = "",
	TracerName = "Tracer",
	Spread = Vector(0.05,0.05,0),
	Distance = 56756
}

local function CanDeflect()
	return true
end

local function DeflectBullet( ent, dmginfo, olddmg )
	if dmginfo:IsDamageType( DMG_BULLET ) and CanDeflect() and ent.FireBullets then
		bul.Src = ent:GetShootPos()
		bul.Dir = ent:EyeAngles():Forward()
		bul.Damage = olddmg
		bul.Force = olddmg / 10
		local atk = dmginfo:GetAttacker()
		if IsValid( atk ) and atk.TFALastTracer then
			bul.Tracer = atk.TFALastTracer
		end
		ent:FireBullets( bul, false )
		dmginfo:ScaleDamage(0)
	end
end

local stuntime = 0.65

local function StunNPC( npc, ply )
	if stun_cv and not stun_cv:GetBool() then return end
	if ( not IsValid( npc ) ) or ( not npc:IsNPC() ) then
		return
	end
	if npc.ClearSchedule then
		npc:ClearSchedule()
	end
	if npc.SetEnemy then
		npc:SetEnemy(nil)
	end
	if npc.AddEntityRelationship and IsValid(ply) then
		local oldrel = npc.GetRelationship and npc:GetRelationship(ply) or D_HT
		npc:AddEntityRelationship( ply, D_NU, 99)
		stuntime = stuntime_cv:GetFloat()
		timer.Simple( stuntime , function()
			if IsValid(npc) and npc:IsNPC() and IsValid(ply) then
				npc:AddEntityRelationship( ply, oldrel, 99)
			end
		end)
	end
	if npc.ClearEnemyMemory then
		npc:ClearEnemyMemory()
	end
end

local function BlockDamageNew( ent, dmginfo )
	if not ent:IsPlayer() then return end
	if dmginfo:IsDamageType( DMG_DROWNRECOVER ) or dmginfo:IsDamageType(DMG_DIRECT) then return end
	local wep
	wep = ent:GetActiveWeapon()

	if (wep.IsTFAWeapon and wep.BlockDamageTypes and wep:GetStatus() == TFA.Enum.STATUS_BLOCKING) then
		local canblock = false
		for _,v in ipairs(wep.BlockDamageTypes) do
			if dmginfo:IsDamageType(v) then canblock = true end
		end

		local retVal = hook.Run("TFA_MeleeCanBlockDamage", wep, ent, dmginfo, canblock)
		if retVal ~= nil then
			canblock = retVal
		end

		if canblock then
			local damageinflictor, blockthreshold
			damageinflictor = dmginfo:GetInflictor()

			if (not IsValid(damageinflictor)) then
				damageinflictor = dmginfo:GetAttacker()
			end

			blockthreshold = ( wep.BlockCone or 135 ) / 2
			if angle_mult_cv then
				blockthreshold = blockthreshold * angle_mult_cv:GetFloat()
			end
			if ( IsValid(damageinflictor) and ( math.abs(math.AngleDifference( ent:EyeAngles().y, ( damageinflictor:GetPos() - ent:GetPos() ):Angle().y )) <= blockthreshold)) then
				local fac = math.Clamp( ( CurTime() - wep:GetBlockStart() - wep.BlockTimeWindow ) / wep.BlockTimeFade, 0, 1)
				local dmgscale
				if ( not timed_blocking_cv ) or timed_blocking_cv:GetBool() then
					dmgscale = Lerp(fac, wep.BlockDamageMaximum, wep.BlockDamageMinimum)
				else
					dmgscale = wep.BlockDamageMaximum
				end
				local olddmg = dmginfo:GetDamage()
				dmgscale = math.min( dmgscale, wep.BlockDamageCap / dmginfo:GetDamage() )
				--print(fac)
				dmginfo:ScaleDamage(dmgscale)
				dmginfo:SetDamagePosition(vector_origin)
				dmginfo:SetDamageType( bit.bor( dmginfo:GetDamageType(), DMG_DROWNRECOVER ) )
				wep:EmitSound(wep.BlockSound or "")

				if wep.ChooseBlockAnimation then
					wep:ChooseBlockAnimation()
				end

				if deflect_cv and deflect_cv:GetInt() == 2 then
					DeflectBullet( ent, dmginfo, olddmg )
				end

				if dmginfo:GetDamage() < 1 then
					if deflect_cv and deflect_cv:GetInt() == 1 and wep.BlockCanDeflect then
						DeflectBullet( ent, dmginfo, olddmg )
					end
					StunNPC( dmginfo:GetAttacker(), ent )
					return true
				end
				return
			end
		end
	end
end


hook.Add("EntityFireBullets","TFA_Melee_LogTracer",function(ent,bulv) --Record tracer for blocking
	ent.TFALastTracer = bulv.TracerName or ""
end)

local npc_dmg_scale_cv = GetConVar("sv_tfa_melee_damage_npc")
local ply_dmg_scale_cv = GetConVar("sv_tfa_melee_damage_ply")

hook.Add("EntityTakeDamage", "TFA_Melee_Scaling", function( ent, dmginfo )
	local wep = dmginfo:GetInflictor()
	if not IsValid(wep) then return end

	if wep:IsPlayer() then wep = wep:GetActiveWeapon() end
	if not IsValid(wep) or not wep:IsWeapon() or not wep.IsTFAWeapon or not wep.IsMelee then return end

	if ent:IsNPC() then
		dmginfo:ScaleDamage( npc_dmg_scale_cv:GetFloat() )
	elseif ent:IsPlayer() then
		dmginfo:ScaleDamage( ply_dmg_scale_cv:GetFloat() )
	end
end) --Cancel
hook.Add("EntityTakeDamage", "aaa_TFA_Melee_Block", function( ent, dmginfo )
	return BlockDamageNew( ent, dmginfo )
end) --Cancel
hook.Add("ScalePlayerDamage", "aaa_TFA_Melee_Block", function( ent, _, dmginfo ) --Cancel
	return BlockDamageNew( ent, dmginfo )
end)

game.AddAmmoType({
	name = "TFMSwordHitGenericSlash",
	dmgtype = DMG_SLASH,
	tracer = TRACER_NONE
})

local function TFMPlayerSpawn(ply)
	ply:SetNW2Vector("TFM_SwordPosition", Vector(1, 1, 1))
	ply:SetNW2Vector("TFM_SwordNormal", Vector(1, 1, 1))
	ply:SetNW2Bool("TFM_IsSprinting", false)
	ply:SetNW2Bool("TFM_IsBlocking", false)
	ply:SetNW2Bool("TFM_IsSwinging", false)
	ply:SetNW2Float("TFM_SwingStart", CurTime())
end

hook.Add("PlayerSpawn", "TFM_PlayerSpawn", TFMPlayerSpawn)

hook.Add("EntityTakeDamage", "TFM_Block", function(ent, dmginfo) --Legacy
	if ent:IsPlayer() then
		local wep
		wep = ent:GetActiveWeapon()

		if (wep.IsTFAWeapon and wep.BlockAngle) and (dmginfo:IsDamageType(DMG_SLASH) or dmginfo:IsDamageType(DMG_CLUB) or (wep.NinjaMode and wep.NinjaMode == true and (dmginfo:IsDamageType(DMG_CRUSH) or dmginfo:IsDamageType(DMG_BULLET)))) and wep:GetIronSights() then
			local damageinflictor, blockthreshold
			damageinflictor = dmginfo:GetInflictor()

			if (not IsValid(damageinflictor)) then
				damageinflictor = dmginfo:GetAttacker()
			end

			blockthreshold = wep.BlockAngle / 2 or 90

			if (IsValid(damageinflictor) and (math.abs((ent:GetAimVector():Angle() - (damageinflictor:GetPos() - ent:GetPos()):Angle()).y) <= blockthreshold)) or (math.abs((ent:GetAimVector():Angle() - (dmginfo:GetDamagePosition() - ent:GetPos()):Angle()).y) <= blockthreshold) then
				local fac = math.Clamp((CurTime() - wep:GetBlockStart() - wep.BlockWindow) / wep.BlockFadeTime, 0, 1)
				local dmgscale
				if ( not timed_blocking_cv ) or timed_blocking_cv:GetBool() then
					dmgscale = Lerp(fac, wep.BlockMaximum, wep.BlockMinimum)
				else
					dmgscale = wep.BlockMaximum
				end
				--print(fac)
				dmginfo:ScaleDamage(dmgscale)
				dmginfo:SetDamagePosition(vector_origin)
				wep:EmitSound(wep.Primary.Sound_Impact_Metal)

				if wep.BlockAnim then
					wep:BlockAnim()
				end
			end
		end
	end
end)
--Getting the position and angle of an attachment and sending it back to the server is wayyy too laggy.  Must be pre-coded.
--[[
if SERVER then
	util.AddNetworkString( "TFM_SAPacket" )
	net.Receive("TFM_SAPacket", function()
		local ply;
		ply = net.ReadEntity()
		local pos;
		pos = net.ReadVector()
		local norm;
		norm = net.ReadNormal()
		if IsValid(ply) then
			if pos and norm then
				ply:SetNW2Vector("TFM_SwordPosition",pos)
				ply:SetNW2Vector("TFM_SwordNormal",norm)
			end
		end
	end)
end
]]
--
