if SERVER then
	AddCSLuaFile()
end

DEFINE_BASECLASS("tfa_gun_base")
SWEP.DrawCrosshair = true
SWEP.Primary.Automatic = true
SWEP.Secondary.Automatic = true
SWEP.Primary.RPM = 120 --Primary Slashs per minute
SWEP.Secondary.RPM = 60 --Secondary stabs per minute
SWEP.Primary.Delay = 0.15 --Delay for hull (primary)
SWEP.Secondary.Delay = 0.33 --Delay for hull (secondary)
SWEP.Primary.Length = 32
SWEP.Secondary.Length = 48
SWEP.Primary.Sound = Sound("Weapon_Knife.Slash") --Sounds
SWEP.KnifeShink = "Weapon_Knife.HitWall" --Sounds
SWEP.KnifeSlash = "Weapon_Knife.Hit" --Sounds
SWEP.KnifeStab = "Weapon_Knife.Slash" --Sounds
SWEP.SlashTable = {"midslash1", "midslash2"} --Table of possible hull sequences
SWEP.StabTable = {"stab"} --Table of possible hull sequences
SWEP.StabMissTable = {"stab_miss"} --Table of possible hull sequences
SWEP.DisableIdleAnimations = false --Enable idles
--[[ Don't Edit Below ]]
--
SWEP.DamageType = DMG_SLASH
SWEP.MuzzleFlashEffect = "" --No muzzle
SWEP.DoMuzzleFlash = false --No muzzle
SWEP.WeaponLength = 1 --No nearwall
SWEP.Primary.Ammo = "" -- pistol, 357, smg1, ar2, buckshot, slam, SniperPenetratedRound, AirboatGun
SWEP.Primary.ClipSize = 1 -- Size of a clip
SWEP.Primary.DefaultClip = 1 -- Bullets you start with
SWEP.Primary.AmmoConsumption = 0
SWEP.Secondary.AmmoConsumption = 0
SWEP.data = {} --No ironsights
SWEP.Secondary.IronSightsEnabled = false --No ironsights
SWEP.Secondary.DisplaySpread = false
SWEP.IsMelee = true

SWEP.CrosshairConeRecoilOverride = .05

SWEP.HullData = {
	hullMin = Vector(-16, -16, -16),
	hullMax = Vector(16, 16, 16)
}

SWEP.Primary.DisplayFalloff = false

SWEP.SlashCounter = 1
SWEP.StabCounter = 1

function SWEP:Deploy()
	return BaseClass.Deploy(self)
end

local lim_up_vec = Vector(1,1,0.1)

function SWEP:ApplyForce(ent, force, posv, now)
	if not IsValid(ent) or not ent.GetPhysicsObjectNum then return end

	if now then
		if ent.GetRagdollEntity then
			ent = ent:GetRagdollEntity() or ent
		end

		if not IsValid(ent) then return end
		local phys = ent:GetPhysicsObjectNum(0)

		if IsValid(phys) then
			if ent:IsPlayer() or ent:IsNPC() then
				ent:SetVelocity( force * 0.1 * lim_up_vec )
				phys:SetVelocity(phys:GetVelocity() + force * 0.1 * lim_up_vec )
			else
				phys:ApplyForceOffset(force, posv)
			end
		end
	else
		timer.Simple(0, function()
			if IsValid(self) and self:OwnerIsValid() and IsValid(ent) then
				self:ApplyForce(ent, force, posv, true)
			end
		end)
	end
end

function SWEP:SlashSound(tr)
	if IsFirstTimePredicted() then
		if tr.Hit then
			if tr.MatType == MAT_FLESH or tr.MatType == MAT_ALIENFLESH then
				self:EmitSound(self.KnifeSlash)
			else
				self:EmitSound(self.KnifeShink)
			end
		else
			self:EmitSound(self.Primary_TFA.Sound)
		end
	end
end

local sp = game.SinglePlayer()

function SWEP:GetSlashTrace(tbl, fwd)
	local ow = self:GetOwner()

	if not sp and ow:IsPlayer() then
		ow:LagCompensation(true)
	end

	local traceRes = util.TraceLine(tbl)

	if (not traceRes.Hit) then
		if not self.HullData.Radius then
			self.HullData.Radius = self.HullData.hullMin:Distance(self.HullData.hullMax) / 2
		end

		local hd = self.HullData
		tbl.mins = -hd.hullMin
		tbl.maxs = hd.hullMax
		tbl.endpos = tbl.endpos - fwd * hd.Radius
		traceRes = util.TraceHull(tbl)
	end

	if not sp and ow:IsPlayer() then
		ow:LagCompensation(false)
	end

	return traceRes
end

function SWEP:SmackDamage(tr, fwd, primary)
	if not tr.Entity:IsValid() then return end
	if tr.Entity.LVS then return end
	local dmg, force

	if primary then
		dmg = self:GetStatL("Primary.Damage")
	else
		dmg = self:GetStatL("Secondary.Damage")
	end

	force = dmg * 25
	local dmginfo = DamageInfo()
	dmginfo:SetAttacker(self:GetOwner())
	dmginfo:SetInflictor(self)
	dmginfo:SetDamage(dmg)
	dmginfo:SetDamageType(self.DamageType)
	dmginfo:SetDamagePosition(tr.HitPos)
	dmginfo:SetReportedPosition(tr.StartPos)
	dmginfo:SetDamageForce(fwd * force)
	tr.Entity:DispatchTraceAttack(dmginfo, tr, fwd)
	self:ApplyForce( tr.Entity, dmginfo:GetDamageForce(), tr.HitPos )
end

function SWEP:SmackEffect(tr)
	local vSrc = tr.StartPos
	local bFirstTimePredicted = IsFirstTimePredicted()
	local bHitWater = bit.band(util.PointContents(vSrc), MASK_WATER) ~= 0
	local bEndNotWater = bit.band(util.PointContents(tr.HitPos), MASK_WATER) == 0

	local trSplash = bHitWater and bEndNotWater and util.TraceLine({
		start = tr.HitPos,
		endpos = vSrc,
		mask = MASK_WATER
	}) or not (bHitWater or bEndNotWater) and util.TraceLine({
		start = vSrc,
		endpos = tr.HitPos,
		mask = MASK_WATER
	})

	if (trSplash and bFirstTimePredicted) then
		local data = EffectData()
		data:SetOrigin(trSplash.HitPos)
		data:SetScale(1)

		if (bit.band(util.PointContents(trSplash.HitPos), CONTENTS_SLIME) ~= 0) then
			data:SetFlags(1) --FX_WATER_IN_SLIME
		end

		util.Effect("watersplash", data)
	end

	self:DoImpactEffect(tr, self.DamageType)

	if (tr.Hit and bFirstTimePredicted and not trSplash) then
		local data = EffectData()
		data:SetOrigin(tr.HitPos)
		data:SetStart(vSrc)
		data:SetSurfaceProp(tr.SurfaceProps)
		data:SetDamageType(self.DamageType)
		data:SetHitBox(tr.HitBox)
		data:SetEntity(tr.Entity)
		util.Effect("Impact", data)
	end
end

local tracedata = {}

function SWEP:Slash(bPrimary)
	local ow, gsp, ea, fw, tr, rpm, delay

	if bPrimary == nil then
		bPrimary = true
	end

	ow = self:GetOwner()
	gsp = ow:GetShootPos()
	ea = ow:EyeAngles()
	fw = ea:Forward()
	tracedata.start = gsp
	tracedata.endpos = gsp + fw * (bPrimary and self.Primary_TFA.Length or self.Secondary_TFA.Length)
	tracedata.filter = ow

	tr = self:GetSlashTrace(tracedata, fw)
	rpm = self:GetStatL("Primary.RPM")
	delay = self:GetStatL("Primary.Delay")
	self:SlashSound(tr)
	self:SmackDamage(tr, fw, bPrimary)
	self:SmackEffect(tr, fw, bPrimary)
	self:ScheduleStatus(TFA.Enum.STATUS_SHOOTING, 60 / rpm - delay)
end

function SWEP:CanAttack()
	return self:CanPrimaryAttack() -- UNUSED, kept for backwards compatibility
end

function SWEP:PrimaryAttack()
	local ply = self:GetOwner()
	if not ply:IsPlayer() or self:GetOwner():KeyDown(IN_RELOAD) then return end

	if not self:VMIV() then return end
	if not self:CanPrimaryAttack() then return end

	self:PrePrimaryAttack()

	if hook.Run("TFA_PrimaryAttack", self) then return end

	self.SlashCounter = self.SlashCounter + 1

	if self.SlashCounter > #self.SlashTable then
		self.SlashCounter = 1
	end

	self:SendViewModelSeq(self.SlashTable[self.SlashCounter])

	self:GetOwner():SetAnimation(PLAYER_ATTACK1)
	self:SetNextPrimaryFire(CurTime() + 1 / (self.Primary_TFA.RPM / 60))
	self:SetNextSecondaryFire(self:GetNextPrimaryFire())
	self:ScheduleStatus(TFA.Enum.STATUS_KNIFE_SLASH, self.Primary_TFA.Delay)

	self:PostPrimaryAttack()
	hook.Run("TFA_PostPrimaryAttack", self)
end

function SWEP:SecondaryAttack()
	local ply = self:GetOwner()
	if not ply:IsPlayer() or self:GetOwner():KeyDown(IN_RELOAD) then return end

	if not self:VMIV() then return end
	if not self:CanSecondaryAttack() then return end

	self:PreSecondaryAttack()

	if hook.Run("TFA_SecondaryAttack", self) then return end

	local ow, gsp, ea, fw, tr

	ow = self:GetOwner()
	gsp = ow:GetShootPos()
	ea = ow:EyeAngles()
	fw = ea:Forward()
	tracedata.start = gsp
	tracedata.endpos = gsp + fw * self.Secondary_TFA.Length
	tracedata.filter = ow

	tr = self:GetSlashTrace(tracedata, fw)

	if tr.Hit then
		self.StabIndex = self.StabIndex or 0
		self.StabIndex = self.StabIndex + 1

		if self.StabIndex > #self.StabTable then
			self.StabIndex = 1
		end

		self:SendViewModelSeq(self.StabTable[self.StabIndex])
	else
		self.StabMiss = self.StabMiss or 0
		self.StabMiss = self.StabMiss + 1

		if self.StabMiss > #self.StabMissTable then
			self.StabMiss = 1
		end

		self:SendViewModelSeq(self.StabMissTable[self.StabMiss])
	end

	self:GetOwner():SetAnimation(PLAYER_ATTACK1)
	self:SetNextPrimaryFire(CurTime() + 60 / self.Secondary_TFA.RPM)
	self:SetNextSecondaryFire(self:GetNextPrimaryFire())
	self:ScheduleStatus(TFA.Enum.STATUS_KNIFE_STAB, self.Secondary_TFA.Delay)

	self:PostSecondaryAttack()
	hook.Run("TFA_PostSecondaryAttack", self)
end

function SWEP:ThrowKnife()
	if not IsFirstTimePredicted() then return end
	self:EmitSound(self.Primary_TFA.Sound)

	if SERVER then
		local ent = ents.Create("tfa_thrown_blade")

		if ent:IsValid() then
			ent:SetPos(self:GetOwner():GetShootPos())
			ent:SetAngles(self:GetOwner():EyeAngles())
			ent:SetModel(self.Primary_TFA.ProjectileModel or self.WorldModel)
			ent:SetOwner(self:GetOwner())
			ent:SetPhysicsAttacker(self:GetOwner())
			ent:Spawn()
			ent:Activate()
			ent:SetNW2String("ClassName", self:GetClass())
			local phys = ent:GetPhysicsObject()

			if IsValid(phys) then
				phys:SetVelocity(self:GetOwner():GetAimVector() * 1250)
				phys:AddAngleVelocity(Vector(0, 480, 0))
			end

			self:GetOwner():SetAnimation(PLAYER_ATTACK1)
			self:GetOwner():StripWeapon(self:GetClass())
		end
	end
end

function SWEP:Reload()
	if not self:OwnerIsValid() and self:GetOwner():KeyDown(IN_RELOAD) then return end
	self:ThrowKnife()
end

function SWEP:Think2(...)
	if self:GetStatus() == TFA.Enum.STATUS_KNIFE_STAB and CurTime() > self:GetStatusEnd() then
		self:Slash(false)
	elseif self:GetStatus() == TFA.Enum.STATUS_KNIFE_SLASH and CurTime() > self:GetStatusEnd() then
		self:Slash(true)
	end

	BaseClass.Think2(self, ...)
end

SWEP.IsKnife = true
SWEP.WeaponLength = 8

TFA.FillMissingMetaValues(SWEP)
