if SERVER then
	AddCSLuaFile()
end

SWEP.Base = "weapon_base"

SWEP.Category = "Fesseln"
SWEP.DrawWeaponInfoBox = false

SWEP.Spawnable = false

SWEP.Slot = 3
SWEP.PrintName = "Fesseln"

SWEP.ViewModelFOV = 60
SWEP.Weight = 5
SWEP.AutoSwitchTo = false
SWEP.AutoSwitchFrom = false

SWEP.ViewModel = Model("models/weapons/c_bugbait.mdl")
SWEP.WorldModel = Model("models/mg_cuffs/cuffs_wm.mdl")
SWEP.UseHands = true

SWEP.Primary.Recoil = 1
SWEP.Primary.Damage = 5
SWEP.Primary.Cone = 0
SWEP.Primary.Delay = 0.25

SWEP.Primary.ClipSize = -1
SWEP.Primary.DefaultClip = -1
SWEP.Primary.Automatic = true
SWEP.Primary.Ammo = "none"
SWEP.Primary.ClipMax = -1

SWEP.Secondary.ClipSize = -1
SWEP.Secondary.DefaultClip = -1
SWEP.Secondary.Automatic = false
SWEP.Secondary.Ammo = "none"
SWEP.Secondary.ClipMax = -1

SWEP.DeploySpeed = 1.5

SWEP.Spawnable = false
SWEP.AdminSpawnable = false

SWEP.HoldType = "slam"

SWEP.CuffTime = 1
SWEP.CuffSound = Sound("buttons/lever7.wav")

SWEP.CuffMaterial = "phoenix_storms/metalfloor_2-3"
SWEP.CuffRope = "cable/cable2"

SWEP.CuffStrength = 1
SWEP.CuffRegen = 1

SWEP.CuffReusable = true
SWEP.CuffRecharge = 30

SWEP.CuffBlindfold = false
SWEP.CuffGag = false

SWEP.DragRestricted = false
SWEP.RopeTarget = true

SWEP.CuffStrengthVariance = 0
SWEP.CuffRegenVariance = 0

hook.Add("canDropWeapon", "Cuffs_Skin", function(ply, wep)
	if wep.Base == "weapon_cuff_base" or wep.IsLeash then
		wep.WorldModel = "models/mg_cuffs/cuffs_wm.mdl"
	end
end, HOOK_MONITOR_LOW or 2)

hook.Add("onDarkRPWeaponDropped", "Cuffs_Skin", function(ply, ent, wep)
	if wep.Base == "weapon_cuff_base" or wep.IsLeash then
		ent:SetMaterial(wep.CuffMaterial or "")
	end
end, HOOK_MONITOR_LOW or 2)

function SWEP:SetupDataTables()
	self:NetworkVar("Bool", 0, "IsCuffing")
	self:NetworkVar("Entity", 0, "Cuffing")
	self:NetworkVar("Float", 0, "CuffTime")
end

function SWEP:Initialize()
	self:SetHoldType(self.HoldType)
	self:SetDeploySpeed(1.3)
	self:DrawShadow(false)

	self.WorldModel = "models/weapons/w_toolgun.mdl"
end

function SWEP:PrimaryAttack()
	if self:GetIsCuffing() then return end
	self:SetNextPrimaryFire(CurTime() + self.Primary.Delay)
	self:SetNextSecondaryFire(CurTime() + self.Primary.Delay)
	if !SERVER then return end
	if self:GetCuffTime() > CurTime() then return end
	if self.Owner.LagCompensation then
		self.Owner:LagCompensation(true)
	end
	local tr = self:TargetTrace()
	if self.Owner.LagCompensation then
		self.Owner:LagCompensation(false)
	end
	if !tr then return end
	local ent = tr.Entity
	local ragdoll_entity = ent:GetDTEntity(0)
	if hook.Run("Cuffs_CanHandcuff", self.Owner, IsValid(ragdoll_entity) and ragdoll_entity or ent, self) == false then return end
	if RAGDOLL and IsValid(ragdoll_entity) then
		self:SetCuffing(ragdoll_entity)
		hook.Run("Cuffs_OnPlayerHandcuffing", self.Owner, ragdoll_entity, true, true)
	else
		self:SetCuffing(ent)
		hook.Run("Cuffs_OnPlayerHandcuffing", self.Owner, ent, false, true)
	end
	self:SetHoldType("pistol")
	self:SetCuffTime(CurTime() + self.CuffTime)
	self:SetIsCuffing(true)
	sound.Play(self.CuffSound, self.Owner:GetShootPos(), 75, 100, 1)
	hook.Run("Cuffs_ModifyCuffs", self.Owner, self, ragdoll_entity or ent)
end

function SWEP:SecondaryAttack()
end

function SWEP:Reload()
end

function SWEP:Holster()
	if self:GetIsCuffing() then
		self:SetCuffTime(0)
	end
	self:SetIsCuffing(false)
	self:SetCuffing(NULL)
	if SERVER then
		self.Cuffs_Failing = nil
	end
	SafeRemoveEntity(self.cmdl_RightCuff)
	SafeRemoveEntity(self.cmdl_LeftCuff)
	return true
end

SWEP.OnRemove = SWEP.Holster

function SWEP:DoHandcuff(target)
	local ply = self.Owner
	if !IsValid(ply) or !IsValid(target) then return end
	if target.IsGhost and target:IsGhost() then target:DisableGhost() end
	target:ExitVehicle()
	if RAGDOLL and target.IsRagdolled then
		RAGDOLL.ResetRagdoll(target, target.IsTasered and true or false)
		if target:IsHandcuffed() then
			target:SelectWeapon("weapon_handcuffed")
			hook.Run("Cuffs_OnPlayerHandcuffed", ply, target)
			if !self.CuffReusable then
				self:Remove()
				ply:ConCommand("lastinv")
			end
			return
		end
	end
	target:DropObject()
	local cuff = target:Give("weapon_handcuffed")
	if !IsValid(cuff) then return end
	cuff:SetCuffStrength(self.CuffStrength + math.Rand(-self.CuffStrengthVariance, self.CuffStrengthVariance))
	cuff:SetCuffRegen(self.CuffRegen + math.Rand(-self.CuffRegenVariance, self.CuffRegenVariance))
	cuff:SetCuffMaterial(self.CuffMaterial)
	if self.RopeTarget then
		cuff:SetKidnapper(ply)
	end
	cuff:SetDragRestricted(self.DragRestricted)
	cuff:SetRopeMaterial(self.CuffRope)
	cuff:SetRopeLength(self.RopeLength)
	cuff:SetCanBlind(self.CuffBlindfold)
	cuff:SetCanGag(self.CuffGag)
	if target:GetModel() == "models/falloutdog/falloutdog.mdl" then
		cuff:SetIsCollar(true)
	end
	cuff.CuffType = self:GetClass() or ""
	target:SelectWeapon("weapon_handcuffed")
	hook.Run("Cuffs_OnPlayerHandcuffed", ply, target, cuff)
	if !self.CuffReusable then
		self:Remove()
		ply:ConCommand("lastinv")
	end
end

function SWEP:Think()
	if !SERVER then return end

	local tb = self:GetTable()
	local dt = tb.dt
	if !dt then return end

	if dt.IsCuffing then
		local cur_time = CurTime()
		local tr = self:TargetTrace()
		local cuffing = dt.Cuffing
		local tr_ent = tr and tr.Entity
		local dt_ent = tr_ent and tr_ent:GetDTEntity(0)
		if !tr or (tr_ent != cuffing and dt_ent != cuffing) then
			tb.Cuffs_Failing = tb.Cuffs_Failing or cur_time + 0.1
			if tb.Cuffs_Failing < cur_time then
				tb.Cuffs_Failing = nil
				dt.IsCuffing = false
				dt.CuffTime = 0
				return
			end
		else
			tb.Cuffs_Failing = nil
		end
		local target_ply = tr_ent == cuffing and tr_ent or dt_ent == cuffing and dt_ent
		if IsValid(target_ply) then
			hook.Run("Cuffs_OnPlayerHandcuffing", self:GetOwner(), target_ply, true, false)
		end
		if cur_time > dt.CuffTime and tr and (tr_ent == cuffing or dt_ent == cuffing) then
			tb.Cuffs_Failing = nil
			dt.IsCuffing = false
			dt.CuffTime = cur_time + tb.CuffRecharge
			self:DoHandcuff(cuffing)
		end
	elseif self:GetHoldType() != tb.HoldType then
		self:SetHoldType(tb.HoldType)
	end
end

function SWEP:TargetTrace()
	local owner = self:GetOwner()
	if !IsValid(owner) then return end

	local shoot_pos = owner:GetShootPos()
	local shoot_dest = shoot_pos + (owner:EyeAngles():Forward() * 72)
	local tr = util.TraceLine({start = shoot_pos, endpos = shoot_dest, filter = owner, mask = MASK_SHOT_HULL})

	local tr = util.TraceLine({
		start = shoot_pos,
		endpos = shoot_dest,
		filter = owner,
		mask = MASK_SHOT_HULL
	})
	
	local ent = tr.Entity
	if !IsValid(ent) then
		tr = util.TraceHull({
			start = shoot_pos,
			endpos = shoot_dest,
			filter = owner,
			mins = Vector(-1, -1, -1),
			maxs = Vector(1, 1, 1),
			mask = MASK_SHOT_HULL
		})
		ent = tr.Entity
	end

	if !IsValid(ent) then return end

	if ent:IsPlayer() and !ent:IsHandcuffed() then
		if hook.Run("Cuffs_CanHandcuff", owner, ent, self, self.IsLeash) == false then return end
		return tr
	end

	if RAGDOLL and ent:IsRagdoll() then
		local ply = ent:GetDTEntity(0)
		if IsValid(ply) then
			if ply:IsPlayer() and ply:Alive() and !ply:isArrested() then
				if hook.Run("Cuffs_CanHandcuff", owner, ply, self, self.IsLeash) == false then return end
				return tr
			end
		end
	end
end

if !CLIENT then return end

local Col = {Text = Color(255, 255, 255), TextShadow = Color(0, 0, 0), BoxOutline = Color(0, 0, 0), BoxBackground = Color(255, 255, 255, 20), BoxLeft = Color(255, 0, 0), BoxRight = Color(0, 255, 0)}
local matGrad = Material("gui/gradient")
function SWEP:DrawHUD()
	local w, h = ScrW() / 2, ScrH() / 2
	if !self:GetIsCuffing() then
		if self:GetCuffTime() <= CurTime() then return end
		surface.SetDrawColor(Col.BoxOutline)
		surface.DrawOutlinedRect(w - 101, h + 54, 202, 22)
		surface.SetDrawColor(Col.BoxBackground)
		surface.DrawRect(w - 100, h + 55, 200, 20)
		local CuffingPercent = math.Clamp(((self:GetCuffTime() - CurTime()) / self.CuffRecharge), 0, 1)
		render.SetScissorRect(w - 100, h + 55, (w - 100) + (CuffingPercent * 200), h + 75, true)
		surface.SetDrawColor(Col.BoxRight)
		surface.DrawRect(w - 100, h + 55, 200,20)
		surface.SetMaterial(matGrad)
		surface.SetDrawColor(Col.BoxLeft)
		surface.DrawTexturedRect(w - 100, h + 55, 200, 20)
		render.SetScissorRect(0, 0, 0, 0, false)
		return
	end
	draw.SimpleText("Person wird gefesselt...", "HandcuffsText", w + 1, h + 31, Col.TextShadow, TEXT_ALIGN_CENTER)
	draw.SimpleText("Person wird gefesselt...", "HandcuffsText", w, h + 30, Col.Text, TEXT_ALIGN_CENTER)
	surface.SetDrawColor(Col.BoxOutline)
	surface.DrawOutlinedRect(w - 101, h + 54, 202, 22)
	surface.SetDrawColor(Col.BoxBackground)
	surface.DrawRect(w - 100, h + 55, 200, 20)
	local CuffingPercent = math.Clamp(1 - ((self:GetCuffTime() - CurTime()) / self.CuffTime), 0, 1)
	render.SetScissorRect(w - 100, h + 55, (w - 100) + (CuffingPercent * 200), h + 75, true)
		surface.SetDrawColor(Col.BoxRight)
		surface.DrawRect(w - 100, h + 55, 200, 20)
		surface.SetMaterial(matGrad)
		surface.SetDrawColor(Col.BoxLeft)
		surface.DrawTexturedRect(w - 100, h + 55, 200, 20)
	render.SetScissorRect(0, 0, 0, 0, false)
end

local RenderPos = {left = {pos = Vector(0, 0, 0), vel = Vector(0, 0, 0), gravity = 1, ang = Angle(0, 30, 90)}, right = {bone = "ValveBiped.Bip01_R_Hand", pos = Vector(3.2, 2.1, 0.4), ang = Angle(-2, 0, 80), scale = Vector(0.045, 0.045, 0.03)}, rope = {l = Vector(0, 0, 2), r = Vector(2.3, -1.9, 2.7)}}
local CuffMdl = Model("models/hunter/tubes/tube2x2x1.mdl")
local RopeCol = Color(255, 255, 255, 255)
local CollectRopeInfo = {}
function SWEP:PreDrawViewModel(vm)
	if !IsValid(vm) then return end
	vm:SetMaterial("engine/occlusionproxy")
	local left_cuff = self.cmdl_LeftCuff
	if !IsValid(left_cuff) then
		self.cmdl_LeftCuff = ClientsideModel(CuffMdl)
		left_cuff = self.cmdl_LeftCuff
		if !IsValid(left_cuff) then return end
		left_cuff:SetNoDraw(true)
		left_cuff:SetParent(vm)
		RenderPos.left.pos = self.Owner:GetPos()
	end
	local right_cuff = self.cmdl_RightCuff
	if !IsValid(right_cuff) then
		self.cmdl_RightCuff = ClientsideModel(CuffMdl)
		right_cuff = self.cmdl_RightCuff
		if !IsValid(right_cuff) then return end
		right_cuff:SetNoDraw(true)
		right_cuff:SetParent(vm)
	end
	local rpos, rang = self:GetBonePos(RenderPos.right.bone, vm)
	if !(rpos and rang) then return end
	local fixed_rpos = rpos + (rang:Forward() * RenderPos.right.pos.x) + (rang:Right() * RenderPos.right.pos.y) + (rang:Up() * RenderPos.right.pos.z)
	right_cuff:SetPos(fixed_rpos)
	local u, r, f = rang:Up(), rang:Right(), rang:Forward()
	rang:RotateAroundAxis(u, RenderPos.right.ang.y)
	rang:RotateAroundAxis(r, RenderPos.right.ang.p)
	rang:RotateAroundAxis(f, RenderPos.right.ang.r)
	right_cuff:SetAngles(rang)
	local matrix = Matrix()
	matrix:Scale(RenderPos.right.scale)
	right_cuff:EnableMatrix("RenderMultiply", matrix)
	right_cuff:SetMaterial(self.CuffMaterial)
	right_cuff:DrawModel()
	local ft = FrameTime()
	local target = (fixed_rpos - RenderPos.left.pos) * math.min(0.4 / (0.016 / ft), 1) -- Only works with 60 FPS, because I wasn't able to figure something else out.
	-- If someone wants to best me, make your own FPS independent calculation here, which works in 100% of the cases.
	local dist = RenderPos.left.pos:DistToSqr(fixed_rpos)
	local targetZ = ((fixed_rpos.z - 10) - RenderPos.left.pos.z) * RenderPos.left.gravity
	if dist > 10000 then
		RenderPos.left.pos = fixed_rpos
		RenderPos.left.vel = Vector(0, 0, 0)
	else
		RenderPos.left.vel = target
		RenderPos.left.vel.z = targetZ
	end
	RenderPos.left.pos:Add(RenderPos.left.vel)
	left_cuff:SetPos(RenderPos.left.pos)
	left_cuff:SetAngles(RenderPos.left.ang)
	local matrix = Matrix()
	matrix:Scale(RenderPos.right.scale)
	left_cuff:EnableMatrix("RenderMultiply", matrix)
	left_cuff:SetMaterial(self.CuffMaterial)
	left_cuff:DrawModel()
	local class = self:GetClass()
	local rope_info = CollectRopeInfo[class]
	if !rope_info then
		local mat_rope = Material(self.CuffRope)
		CollectRopeInfo[class] = mat_rope
		rope_info = mat_rope
	end
	render.SetMaterial(rope_info)
	render.DrawBeam(RenderPos.left.pos + RenderPos.rope.l, rpos + (rang:Forward() * RenderPos.rope.r.x) + (rang:Right() * RenderPos.rope.r.y) + (rang:Up() * RenderPos.rope.r.z), 0.7, 0, 5, RopeCol)
end

function SWEP:PostDrawViewModel(vm)
	if !IsValid(vm) then return end
	vm:SetMaterial("")
end

SWEP.WRender = {left = {pos = Vector(0, 0, 0), vel = Vector(0, 0, 0), gravity = 1, ang = Angle(0, 30, 90)}, right = {bone = "ValveBiped.Bip01_R_Hand", pos = Vector(2.95, 2.5, -0.2), ang = Angle(30, 0, 90), scale = Vector(0.044, 0.044, 0.044)}, rope = {l = Vector(0, 0, 2), r = Vector(3, -1.65, 1)}}
function SWEP:DrawWorldModel()
	if !IsValid(self.Owner) then return end
	local WRender = self.WRender
	local left_cuff = self.cmdl_LeftCuff
	if !IsValid(left_cuff) then
		self.cmdl_LeftCuff = ClientsideModel(CuffMdl)
		left_cuff = self.cmdl_LeftCuff
		if !IsValid(left_cuff) then return end
		left_cuff:SetNoDraw(true)
	end
	local right_cuff = self.cmdl_RightCuff
	if !IsValid(right_cuff) then
		self.cmdl_RightCuff = ClientsideModel(CuffMdl)
		right_cuff = self.cmdl_RightCuff
		if !IsValid(right_cuff) then return end
		right_cuff:SetNoDraw(true)
	end
	local rpos, rang = self:GetBonePos(WRender.right.bone, self.Owner)
	if !(rpos and rang) then return end
	local fixed_rpos = rpos + (rang:Forward() * WRender.right.pos.x) + (rang:Right() * WRender.right.pos.y) + (rang:Up() * WRender.right.pos.z)
	right_cuff:SetPos(fixed_rpos)
	local u, r, f = rang:Up(), rang:Right(), rang:Forward()
	rang:RotateAroundAxis(u, WRender.right.ang.y)
	rang:RotateAroundAxis(r, WRender.right.ang.p)
	rang:RotateAroundAxis(f, WRender.right.ang.r)
	right_cuff:SetAngles(rang)
	local matrix = Matrix()
	matrix:Scale(WRender.right.scale)
	right_cuff:EnableMatrix("RenderMultiply", matrix)
	right_cuff:SetMaterial(self.CuffMaterial)
	right_cuff:SetupBones()
	right_cuff:DrawModel()
	local ft = FrameTime()
	local target = (fixed_rpos - WRender.left.pos) * math.min(0.4 / (0.016 / ft), 1) -- Only works with 60 FPS, because I wasn't able to figure something else out.
	-- If someone wants to best me, make your own FPS independent calculation here, which works in 100% of the cases.
	local targetZ = ((fixed_rpos.z - 10) - WRender.left.pos.z) * WRender.left.gravity
	local dist = WRender.left.pos:DistToSqr(fixed_rpos)
	if dist > 10000 then
		WRender.left.pos = fixed_rpos
		WRender.left.vel = Vector(0, 0, 0)
	else
		WRender.left.vel = target
		WRender.left.vel.z = targetZ
	end
	WRender.left.pos:Add(WRender.left.vel)
	left_cuff:SetPos(WRender.left.pos)
	left_cuff:SetAngles(right_cuff:GetAngles())
	local matrix = Matrix()
	matrix:Scale(WRender.right.scale)
	left_cuff:EnableMatrix("RenderMultiply", matrix)
	left_cuff:SetMaterial(self.CuffMaterial)
	left_cuff:SetupBones()
	left_cuff:DrawModel()
	local class = self:GetClass()
	local rope_info = CollectRopeInfo[class]
	if !rope_info then
		local mat_rope = Material(self.CuffRope)
		CollectRopeInfo[class] = mat_rope
		rope_info = mat_rope
	end
	render.SetMaterial(rope_info)
	render.DrawBeam(WRender.left.pos + WRender.rope.l, rpos + (rang:Forward() * WRender.rope.r.x) + (rang:Right() * WRender.rope.r.y) + (rang:Up() * WRender.rope.r.z), 0.7, 0, 5, RopeCol)
end

function SWEP:GetBonePos(bonename, vm)
	local bone = vm:LookupBone(bonename)
	if !bone then return end
	local pos, ang = Vector(0, 0, 0), Angle(0, 0, 0)
	local matrix = vm:GetBoneMatrix(bone)
	if matrix then
		pos = matrix:GetTranslation()
		ang = matrix:GetAngles()
	end
	if self.ViewModelFlip then
		ang.r = -ang.r
	end
	return pos, ang
end