
local PLAYER = FindMetaTable( "Player" )
 
resource.AddWorkshop("761228248")
 
util.AddNetworkString("rhc_sendcuffs")
util.AddNetworkString("rhc_inspect")
util.AddNetworkString("rhc_send_inspect_information")
util.AddNetworkString("rhc_confiscate_weapon")
util.AddNetworkString("rhc_bonemanipulate")
util.AddNetworkString("rhc_update_jailtime")
util.AddNetworkString("rhc_jailed")
util.AddNetworkString("rhc_confiscate_item")
util.AddNetworkString("tbfy_surrender")

local LangTbl = RHandcuffsConfig.Language[RHandcuffsConfig.LanguageToUse]

hook.Add("Initialize", "rhc_createfolders", function()
	file.CreateDir("rhandcuffs")
	file.CreateDir("rhandcuffs/npcs")
	file.CreateDir("rhandcuffs/jaillocations")
end)

hook.Add("bLogs_FullyLoaded","RHC_bLogsInit",function()
	local MODULE = bLogs:Module()

	MODULE.Category = "ToBadForYou"
	MODULE.Name     = "Realistic Handcuffs"
	MODULE.Colour   = Color(0,0,255)

	MODULE:Hook("RHC_restrain","rhc_toggle_restrain",function(vic, handcuffer)
		local LogText = "cuffed"
		if !vic.Restrained then
			LogText = "uncuffed"
		end
		MODULE:Log(bLogs:FormatPlayer(handcuffer) .. " " .. LogText .. " " .. bLogs:FormatPlayer(vic))
	end)
	
	MODULE:Hook("RHC_jailed","rhc_jailed_player",function(vic, jailer, time, reason)
		MODULE:Log(bLogs:FormatPlayer(jailer) .. " jailed " .. bLogs:FormatPlayer(vic) .. " for " .. time .. " seconds, reason: " .. reason)
	end)
	
	MODULE:Hook("RHC_confis_weapon","rhc_confis_w",function(vic, confis, wep)
		MODULE:Log(bLogs:FormatPlayer(confis) .. " confiscated a " .. wep .. " from " .. bLogs:FormatPlayer(vic) .. ".")
	end)
	
	MODULE:Hook("RHC_confis_item","rhc_confis_i",function(vic, confis, item)
		MODULE:Log(bLogs:FormatPlayer(confis) .. " confiscated a " .. item .. " from " .. bLogs:FormatPlayer(vic) .. ".")
	end)
	
	bLogs:AddModule(MODULE)
end)

//To make it work with DarkRP unarrest stick
function PLAYER:onUnArrestStickUsed(Unarrester)
	if self:RHC_IsArrested() then
		if RHandcuffsConfig.UnarrestMustRemoveCuffs and self.Restrained and !Unarrester:IsRHCWhitelisted() then
			TBFY_Notify(Unarrester, 1, 4, LangTbl.ReqLockpick)
		else
			TBFY_Notify(self, 1, 4, string.format(LangTbl.UnArrested, Unarrester:Nick()))
			TBFY_Notify(Unarrester, 1, 4, string.format(LangTbl.UnArrester, self:Nick()))
			self:RHC_UnArrest(Unarrester)
		end
	end
end
//To make it work with DarkRP arrest stick
function PLAYER:onArrestStickUsed(Arrester)
	if RHandcuffsConfig.RestrainArrest and !self.Restrained then
		TBFY_Notify(Arrester, 1, 4, LangTbl.MustBeCuffed)
    elseif RHandcuffsConfig.NPCArrestOnly then
		TBFY_Notify(Arrester, 1, 4, "Talk with the jailer NPC while dragging a player in order to arrest.")
	elseif !self:RHC_IsArrested() then
		local Time = GAMEMODE.Config.jailtimer or 120
		self:RHC_Arrest(Time, Arrester)
	end
end

function PLAYER:RHCInspect(Player)
	if !self:IsRHCWhitelisted() then return end
	if !IsValid(self:GetActiveWeapon()) or self:GetActiveWeapon():GetClass() != "weapon_r_handcuffs" then return false end

	if !Player.Restrained then return end
	local Distance = self:EyePos():Distance(Player:GetPos());
	if Distance > 100 then return false; end		
	
	local iStoreItems = {}
	if itemstore and RHandcuffsConfig.ItemStoreIllegalItemsEnabled then
		for k, v in pairs(Player.Inventory:GetItems()) do
			ToCheck = v.Class
			if v.Data.Class then
				ToCheck = v.Data.Class
			end
			if RHandcuffsConfig.ItemStoreIllegalItems[ToCheck] then
				iStoreItems[k] = {Name = v:GetName(), Model = v:GetModel()}
			end
		end
	end		
	local TotalWeps = #Player.StoreWTBL
	net.Start("rhc_send_inspect_information")
		net.WriteEntity(Player)
		net.WriteFloat(TotalWeps)
		for k,v in ipairs(Player.StoreWTBL) do
			net.WriteFloat(k)
			net.WriteString(v)
		end

		if itemstore and RHandcuffsConfig.ItemStoreIllegalItemsEnabled then
			local TotalItems = table.Count(iStoreItems)
			net.WriteFloat(TotalItems)
			for k,v in pairs(iStoreItems) do
				net.WriteFloat(k)
				net.WriteString(v.Name)
				net.WriteString(v.Model)
			end
		end
	net.Send(self)
end

net.Receive("rhc_confiscate_weapon", function(len, Player)
	if RHandcuffsConfig.DisableConfiscations then return false end
	if !Player:IsRHCWhitelisted() then return end
	if !IsValid(Player:GetActiveWeapon()) or Player:GetActiveWeapon():GetClass() != "weapon_r_handcuffs" then return false end

	local ConfisFrom, WepTblID = net.ReadEntity(), net.ReadFloat()
	if !ConfisFrom.Restrained then return end
	local Distance = Player:EyePos():Distance(ConfisFrom:GetPos());
	if Distance > 100 then return false; end

	if ConfisFrom.StoreWTBL[WepTblID] then
		local Wep = ConfisFrom.StoreWTBL[WepTblID]
		if RHandcuffsConfig.BlackListedWeapons[Wep] then return end
		local jobTable = {}
		if DarkRP then
			jobTable = ConfisFrom:getJobTable()
		end
		if RHandcuffsConfig.AllowConfiscatingJobWeapons or (jobTable.weapons and !table.HasValue(jobTable.weapons, Wep)) then		
			ConfisFrom.StoreWTBL[WepTblID] = nil
			local Reward = RHandcuffsConfig.ConfiscateRewardAmount
			TBFY_Notify(Player, 1, 4, string.format(LangTbl.ConfiscateReward, Reward))
			Player:addMoney(Reward)
			hook.Call("RHC_confis_weapon", GAMEMODE, ConfisFrom, Player, Wep)
		end
	end
end)

net.Receive("rhc_confiscate_item", function(len, Player)
	if RHandcuffsConfig.DisableConfiscations then return false end
	if !Player:IsRHCWhitelisted() then return end
	if !IsValid(Player:GetActiveWeapon()) or Player:GetActiveWeapon():GetClass() != "weapon_r_handcuffs" then return false end
	
	local ConfisFrom, SlotID = net.ReadEntity(), net.ReadFloat()
	if !ConfisFrom.Restrained then return end
	local Distance = Player:EyePos():Distance(ConfisFrom:GetPos());
	if Distance > 100 then return false; end
	
	local ItemToConfs = ConfisFrom.Inventory:GetItem(SlotID)
	if ItemToConfs then
		local ToCheck = ItemToConfs.Class
		if ItemToConfs.Data.Class then
			ToCheck = ItemToConfs.Data.Class
		end	
		if RHandcuffsConfig.ItemStoreIllegalItems[ToCheck] then
			local Name = ItemToConfs:GetName()
			ConfisFrom.Inventory:SetItem(SlotID, nil)
			local Reward = RHandcuffsConfig.ConfiscateItemRewardAmount
			TBFY_Notify(Player, 1, 4, string.format(LangTbl.ConfiscateRewardItem, Reward, Name))
			Player:addMoney(Reward)
			hook.Call("RHC_confis_item", GAMEMODE, ConfisFrom, Player, Name)
		end
	end
end)

RHC_JailLocations = RHC_JailLocations or {}
local CurrentMap = string.lower(game.GetMap())
if file.Exists("rhandcuffs/jaillocations/" .. CurrentMap .. ".txt" ,"DATA") then
	RHC_JailLocations = util.JSONToTable(file.Read( "rhandcuffs/jaillocations/" .. CurrentMap .. ".txt" ))
end
concommand.Add("add_rhc_jaillocation", function(Player, CMD, Args)
	if !Player:IsAdmin() then return end
	
	local Pos = Player:GetPos()
	table.insert(RHC_JailLocations, Pos)

	TBFY_Notify(Player, 1, 4, "Successfully added your location as a jail position.")		
end)

concommand.Add("save_rhc_jaillocations", function(Player, CMD, Args)
	if !Player:IsAdmin() then return end

	file.Write("rhandcuffs/jaillocations/" .. CurrentMap .. ".txt", util.TableToJSON(RHC_JailLocations))

	TBFY_Notify(Player, 1, 4, "Successfully saved " .. #RHC_JailLocations .. " jail positions.")		
end)

concommand.Add("remove_rhc_jaillocations", function(Player, CMD, Args)
	if !Player:IsAdmin() then return end
	
	RHC_JailLocations = {}
	file.Write("rhandcuffs/jaillocations/" .. CurrentMap .. ".txt", util.TableToJSON(RHC_JailLocations))

	TBFY_Notify(Player, 1, 4, "Successfully removed all jail positions.")		
end)

function PLAYER:RHC_SendToJail(Arrester, NPC)
	local SafePos = nil
	local JailPoses = RHC_JailLocations
	
	if Arrester and IsValid(Arrester) then
		local Jailer = Arrester.LastJailerNPC
		if IsValid(Jailer) and Jailer.JailPos then
			JailPoses = Jailer.JailPos
		end
	end
	
	for k, v in pairs(JailPoses) do
		local FilledSpot = false
		for _, ent in pairs(ents.FindInSphere(v, 75)) do
			if ent:IsPlayer() and ent != self then
				FilledSpot = true
			end
		end
		
		if (!FilledSpot) then
			SafePos = v
			break
		end
	end
	
	if SafePos then
		self:SetPos(SafePos)
	else
		if IsValid(Arrester) then
			TBFY_Notify(Arrester, 1, 4, "No jail locations found or all were taken, contact server owner.")
		end
	end
end

function PLAYER:RHC_UnArrest(UnArrester)
    self:StripWeapon("weapon_r_cuffed")
	if self.Restrained then
        self:CleanUpRHC(false)
    end
	if RHandcuffsConfig.SetTeamOnArrest then
		if self.PreCArrestT then
			self:changeTeam(self.PreCArrestT, true, true)
			self.PreCArrestT = nil
		end
	end	
	self.StoreWTBL = {}
	
	TBFY_Notify(self, 1, 4, "You were unarrested.")
	
	local SID = self:SteamID()
	net.Start("rhc_update_jailtime")
		net.WriteBool(false)
		net.WriteFloat(0)
		net.WriteString(SID)
		net.WriteString("")
	net.Broadcast()	
	
	RHC_ArrestedPlayers[SID] = false
	if timer.Exists(self:SteamID64() .. "_jailtimer") then
		timer.Remove(self:SteamID64() .. "_jailtimer")
	end
	
	self:Spawn()
	
	if DarkRP then
		hook.Call("playerUnArrested", DarkRP.hooks, self, UnArrester)
	end
end

function PLAYER:RHC_Arrest(Time, Arrester, Reason, NPC)
    if self:InVehicle() then self:ExitVehicle() end
	
	if DarkRP then
		if self:isWanted() then 
			self:unWanted(Arrester) 
		end
		self:setDarkRPVar("HasGunlicense", nil)
	end

    self:StripWeapons()
    self:StripAmmo()

    timer.Create(self:SteamID64() .. "_jailtimer", Time, 1, function()
        if IsValid(self) then 
			self:RHC_UnArrest()
		end
    end)
	
	self.Restrained = true
	self:CleanUpRHC(false, true)
	if RHandcuffsConfig.SetTeamOnArrest then	
		self.PreCArrestT = self:Team()
		self:changeTeam(RHandcuffsConfig.ArrestTeam, true, true)
	end	
	if RHandcuffsConfig.RestrainOnArrest then
		self.Restrained = true
		self:SetupCuffs()
		self:SetupRHCBones("Cuffed")
		self:Give("weapon_r_cuffed")
	end
	self.RHC_JailTime = Time

	local Nick = "None"
	if IsValid(Arrester) then
		Nick = Arrester:Nick()
		if RHandcuffsConfig.ArrestReward then
			local RAmount = RHandcuffsConfig.ArrestRewardAmount
			Arrester:addMoney(RAmount)
			TBFY_Notify(Arrester, 1, 4, string.format(LangTbl.ArrestReward, RAmount, self:Nick()))
		end	
	end

	self.RHC_ArrestedBy = Nick
	
	RHC_ArrestedPlayers[self:SteamID()] = {ATime = Time, ANick = Nick}
	
	net.Start("rhc_update_jailtime")
		net.WriteBool(true)
		net.WriteFloat(self.RHC_JailTime)
		net.WriteString(self:SteamID())
		net.WriteString(Nick)
	net.Broadcast()
	
	net.Start("rhc_jailed")
	net.Send(self)
	
	Reason = Reason or "None"
	hook.Call("RHC_jailed", GAMEMODE, self, Arrester, Time, Reason)
	if DarkRP then
		hook.Call("playerArrested", DarkRP.hooks, self, Time, Arrester)	
	end
	
	self:RHC_SendToJail(Arrester, NPC)
end

function PLAYER:CanRHCDrag(CPlayer)
    if !CPlayer.Restrained or (CPlayer.DraggedBy or self.Dragging) and (self.Dragging != CPlayer or CPlayer.DraggedBy != self) then return end

	local DragPerm = RHandcuffsConfig.DraggingPermissions
    if DragPerm == 1 and CPlayer.CuffedBy == self then
        return true
    elseif DragPerm == 2 and self:IsRHCWhitelisted() then
        return true
    elseif DragPerm == 3 then
        return true
    end
end

function PLAYER:CleanUpRHC(GWeapons, NoReset)
    self.Restrained = false
    self:SetupRHCBones("Cuffed", true)
    if !NoReset then
        local CBy = self.CuffedBy
        if IsValid(CBy) then
            CBy.CuffedPlayer = nil
        end
        self.CuffedBy = nil
    end
    self:SetupCuffs()
    self:CancelDrag()
    if GWeapons then
        self:SetupWeapons()
    end
end

local RHC_BoneManipulations = {
	["Cuffed"] = {
		["ValveBiped.Bip01_R_UpperArm"] = Angle(-28,18,-21),
		["ValveBiped.Bip01_L_Hand"] = Angle(0,0,119),
		["ValveBiped.Bip01_L_Forearm"] = Angle(15,20,40),
		["ValveBiped.Bip01_L_UpperArm"] = Angle(15, 26, 0),
		["ValveBiped.Bip01_R_Forearm"] = Angle(0,50,0),
		["ValveBiped.Bip01_R_Hand"] = Angle(45,34,-15),
		["ValveBiped.Bip01_L_Finger01"] = Angle(0,50,0),
		["ValveBiped.Bip01_R_Finger0"] = Angle(10,2,0),
		["ValveBiped.Bip01_R_Finger1"] = Angle(-10,0,0),
		["ValveBiped.Bip01_R_Finger11"] = Angle(0,-40,0),
		["ValveBiped.Bip01_R_Finger12"] = Angle(0,-30,0)
	},
	["HandsUp"] = {	
		["ValveBiped.Bip01_R_UpperArm"] = Angle(73,35,128),
		["ValveBiped.Bip01_L_Hand"] = Angle(-12,12,90),
		["ValveBiped.Bip01_L_Forearm"] = Angle(-28,-29,44),
		["ValveBiped.Bip01_R_Forearm"] = Angle(-22,1,15),
		["ValveBiped.Bip01_L_UpperArm"] = Angle(-77,-46,4),
		["ValveBiped.Bip01_R_Hand"] = Angle(33,39,-21),
		["ValveBiped.Bip01_L_Finger01"] = Angle(0,30,0),
		["ValveBiped.Bip01_L_Finger1"] = Angle(0,45,0),
		["ValveBiped.Bip01_L_Finger11"] = Angle(0,45,0),
		["ValveBiped.Bip01_L_Finger2"] = Angle(0,45,0),
		["ValveBiped.Bip01_L_Finger21"] = Angle(0,45,0),
		["ValveBiped.Bip01_L_Finger3"] = Angle(0,45,0),
		["ValveBiped.Bip01_L_Finger31"] = Angle(0,45,0),
		["ValveBiped.Bip01_R_Finger0"] = Angle(-10,0,0),
		["ValveBiped.Bip01_R_Finger11"] = Angle(0,30,0),
		["ValveBiped.Bip01_R_Finger2"] = Angle(20,25,0),
		["ValveBiped.Bip01_R_Finger21"] = Angle(0,45,0),
		["ValveBiped.Bip01_R_Finger3"] = Angle(20,35,0),
		["ValveBiped.Bip01_R_Finger31"] = Angle(0,45,0)
	}
}

function PLAYER:SetupRHCBones(Type, Reset)
    if RHandcuffsConfig.BoneManipulateClientside then
		net.Start("rhc_bonemanipulate")
			net.WriteEntity(self)
			net.WriteString(Type)
			net.WriteBool(Reset)
		net.Broadcast()
	else
		for k,v in pairs(RHC_BoneManipulations[Type]) do
			local Bone = self:LookupBone(k)
			if Bone then
				if Reset then
					self:ManipulateBoneAngles(Bone, Angle(0,0,0))
				else
					self:ManipulateBoneAngles(Bone, v)			
				end
			end
		end
	end
	if RHandcuffsConfig.DisablePlayerShadow then
		self:DrawShadow(false)
	end	
end

function PLAYER:TBFY_ToggleSurrender()
	if self.TBFY_Surrendered then
		self:SetupRHCBones("HandsUp", true)
		self.TBFY_Surrendered = false
		self:StripWeapon("tbfy_surrendered")
	else
		self:Give("tbfy_surrendered")
		self:SelectWeapon("tbfy_surrendered")
		self:SetupRHCBones("HandsUp")
		self.TBFY_Surrendered = true
	end	
end

net.Receive("tbfy_surrender", function(len, Player)
	if Player.Restrained or Player.RKRestrained or Player:InVehicle() or !Player:Alive() then return end

	Player:TBFY_ToggleSurrender()
end)

hook.Add("playerCanChangeTeam", "TBFY_NoChangeJobSurrender", function(Player, Team)
    if Player.TBFY_Surrendered then 
		return false, ""
	end
end)

hook.Add("PlayerSwitchWeapon", "TBFY_NoSwitchWeaponSurrender", function(Player)
	if Player.TBFY_Surrendered then return true end
end)

hook.Add("PlayerCanPickupWeapon", "TBFY_DisablePickupWeapon", function(Player, Wep)
	if Player.TBFY_Surrendered and Wep:GetClass() != "tbfy_surrendered" then return false end
end)

hook.Add("canDropWeapon", "TBFY_DisableDropWeapon", function(Player)
	if Player.TBFY_Surrendered then return false end
end)

hook.Add("PlayerDeath", "TBFY_OnDeathSurrender", function( Player, Inflictor, Attacker )
    if Player.TBFY_Surrendered then
        Player:TBFY_ToggleSurrender()
    end
end)

function PLAYER:SetupCuffs()
    if self.Restrained then
        local Cuffs = ents.Create("rhandcuffsent")
        Cuffs.CuffedPlayer = self
        self.Cuffs = Cuffs
        Cuffs:SetPos(self:GetPos())
        Cuffs:Spawn()
        //Returns nil otherwise
        timer.Simple(0.2, function()
            net.Start("rhc_sendcuffs")
                net.WriteEntity(self)
                net.WriteEntity(Cuffs)
            net.Broadcast()
        end)
        self:SetNWBool("rhc_cuffed", true)
    elseif !self.Restrained then
        if IsValid(self.Cuffs) then
            self.Cuffs:Remove()
        end    
        self:SetNWBool("rhc_cuffed", false)
    end
end
 
function PLAYER:SetupWeapons()
    if self.Restrained then
        self.StoreWTBL = {}
        for k,v in pairs(self:GetWeapons()) do
			self.StoreWTBL[k] = v:GetClass()
        end
        self:StripWeapons()
		self:Give("weapon_r_cuffed")
    elseif !self.Restrained then
        self:StripWeapon("weapon_r_cuffed")
		for k,v in pairs(self.StoreWTBL) do
            self:Give(v)
			local SWEPTable = weapons.GetStored(v)
			if SWEPTable then
				local DefClip = SWEPTable.Primary.DefaultClip
				local AmmoType = SWEPTable.Primary.Ammo
				if (DefClip and DefClip > 0) and AmmoType then
					local AmmoToRemove = DefClip - SWEPTable.Primary.ClipSize
					self:RemoveAmmo(AmmoToRemove, AmmoType)
				end
			end
        end
        self.StoreWTBL = {}
		self:SelectWeapon("keys")
    end
end

function PLAYER:RHCRestrain(HandcuffedBy)
    if !self.Restrained then
		if self.TBFY_Surrendered then
			self:TBFY_ToggleSurrender()
		end	
        self.Restrained = true
        self.CuffedBy = HandcuffedBy
        HandcuffedBy.CuffedPlayer = self
        self:SetupRHCBones("Cuffed")
        self:SetupCuffs()
        self:SetupWeapons()
        TBFY_Notify(self, 1, 4, string.format(LangTbl.CuffedBy, HandcuffedBy:Nick()))
        TBFY_Notify(HandcuffedBy, 1, 4, string.format(LangTbl.Cuffer, self:Nick())) 	
    elseif self.Restrained then
        self:CleanUpRHC(true)
        TBFY_Notify(self, 1, 4, string.format(LangTbl.ReleasedBy, HandcuffedBy:Nick()))
        TBFY_Notify(HandcuffedBy, 1, 4, string.format(LangTbl.Releaser, self:Nick()))       
    end
	
	hook.Call("RHC_restrain", GAMEMODE, self, HandcuffedBy)
end

local PGettingDragged = {}
function PLAYER:DragPlayer(TPlayer)
    if self == TPlayer.DraggedBy then
        TPlayer:CancelDrag()
    elseif !self.Dragging then
		TPlayer.DraggedBy = self
        TPlayer:Freeze(true)
        self.Dragging = TPlayer
        if !table.HasValue(PGettingDragged, TPlayer) then
            table.insert(PGettingDragged, TPlayer)
        end
    end
end

function PLAYER:CancelDrag()
    if !IsValid(self) then return end
    if !table.HasValue(PGettingDragged, self) then
        table.RemoveByValue(PGettingDragged, self)
    end
	self:Freeze(false)
	local DraggedByP = self.DraggedBy
	if IsValid(DraggedByP) then
		DraggedByP.Dragging = nil
	end
	self.DraggedBy = nil
end
 
local KeyToCheck = RHandcuffsConfig.KEY
hook.Add("KeyPress", "RHC_AllowDragging", function(Player, Key)
	if Key == KeyToCheck then
        if !Player:InVehicle() then
			local Trace = {}
			Trace.start = Player:GetShootPos();
			Trace.endpos = Trace.start + Player:GetAimVector() * 100;
			Trace.filter = Player;
	 
			local Tr = util.TraceLine(Trace);
			local TEnt = Tr.Entity
			if IsValid(TEnt) and TEnt:IsPlayer() and Player:CanRHCDrag(TEnt) then
				Player:DragPlayer(TEnt)			
			end
		end
    end
end)
 
local DragRange = RHandcuffsConfig.DragMaxRange
hook.Add("Think", "RHC_HandlePlayerDraggingRange", function()
    for k,v in pairs(PGettingDragged) do
        if !IsValid(v) then table.RemoveByValue(PGettingDragged, v) end
        local DPlayer = v.DraggedBy
        if IsValid(DPlayer) then
            local Distance = v:GetPos():Distance(DPlayer:GetPos());
            if Distance > DragRange then
                v:CancelDrag()
            end
        else
            v:CancelDrag()
        end
    end
end)
 
hook.Add("Move", "rhc_drag_move", function( Player, mv)
    local CuffedPlayer = Player.Dragging
    if IsValid(CuffedPlayer) and Player == CuffedPlayer.DraggedBy then
		
		local DragerPos = Player:GetPos()
		local DraggedPos = CuffedPlayer:GetPos()
		local Distance = DragerPos:Distance(DraggedPos)
		
		local DragPosNormal = DragerPos:GetNormal()
		local Difx = math.abs(DragPosNormal.x)
		local Dify = math.abs(DragPosNormal.y)	
		
		local Speed = (Difx + Dify)*math.Clamp(Distance/RHandcuffsConfig.DragRangeForce,0,RHandcuffsConfig.DragMaxForce)

		local ang = mv:GetMoveAngles()
        local pos = mv:GetOrigin()
        local vel = mv:GetVelocity()
		
        vel.x = vel.x * Speed
        vel.y = vel.y * Speed
		vel.z = 15
 
        pos = pos + vel + ang:Right() + ang:Forward() + ang:Up()
		
		if Distance > 55 then
			CuffedPlayer:SetVelocity( vel )
		end
    end
end)
 
hook.Add("playerCanChangeTeam", "RestrictTCHANGECuffed", function(Player, Team)
    if Player.Dragging then
		return false, "Can't change job while dragging."
	elseif Player.Restrained then 
		return false, LangTbl.CantChangeTeam 
	elseif Player:RHC_IsArrested() then
		return false, LangTbl.CantChangeTeamArrested
	end
end)
 
hook.Add("SetupMove", "CuffMovePenalty", function(Player, mv)
    if Player.Restrained then
        mv:SetMaxClientSpeed(mv:GetMaxClientSpeed() / RHandcuffsConfig.RestrainedMovePenalty)
    elseif Player.Dragging then
        mv:SetMaxClientSpeed(mv:GetMaxClientSpeed() / RHandcuffsConfig.DraggingMovePenalty)
    end
end)
 
hook.Add("PlayerDeath", "ManageCuffsDeath", function(Player, Inflictor, Attacker)
    if Player.Restrained then
        Player:CleanUpRHC(false)
    end
end)
 
hook.Add("onLockpickCompleted", "OnSuccessPickCuffs", function(Player, Success, CuffedP)
    if IsValid(CuffedP) then
		if CuffedP:GetNWBool("rhc_cuffed", false) and Success then
			CuffedP:CleanUpRHC(true)
			TBFY_Notify(CuffedP, 1, 4, string.format(LangTbl.ReleasedBy, Player:Nick()))
			TBFY_Notify(Player, 1, 4, string.format(LangTbl.Releaser, CuffedP:Nick()))	
			if CuffedP:isArrested() then
				CuffedP:RHC_UnArrest(Player)
			end
		end
	end
end)
 
hook.Add("CanPlayerEnterVehicle", "RestrictEnteringVCuffs", function(Player, Vehicle)
	if (Player.Restrained and !Player.DraggedBy) or Player:RHC_IsArrested() then
        TBFY_Notify(Player, 1, 4, LangTbl.CantEnterVehicle) 
        return false
	elseif Player.Dragging then
		return false
    end
end)

hook.Add("PlayerEnteredVehicle", "FixCuffsInVehicle", function(Player,Vehicle)
    if Player.Restrained then
        Player:CleanUpRHC(false, true)
        Player.Restrained = true
		if Vehicle.playerdynseat then
			Player:ExitVehicle()
		end
    end
end)
 
hook.Add("PlayerLeaveVehicle", "ReaddCuffsLVehicle", function(Player, Vehicle)
    if Player.Restrained then
        Player:SetupCuffs()
        Player:SetupRHCBones("Cuffed")
    end
end)
 
hook.Add("CanExitVehicle", "RestrictLeavingVCuffs", function(Vehicle, Player)
    if Player.Restrained then
        TBFY_Notify(Player, 1, 4, LangTbl.CantLeaveVehicle)  
        return false
    end
end)

hook.Add("PlayerDisconnected", "RHC_StopDragOnDC", function(Player)
	local Dragger = Player.DraggedBy
	if IsValid(Dragger) then
		if !table.HasValue(PGettingDragged, Player) then
			table.RemoveByValue(PGettingDragged, Player)
		end
		Dragger.Dragging = nil
	end
end)

local APIKey = 76561198299833933
hook.Add("PlayerInitialSpawn", "SendCuffInfo", function(Player)
    //Allow to intialize fully first
    timer.Simple(5, function()
        if IsValid(Player) then
			for k,v in pairs(ents.FindByClass("rhandcuffsent")) do
				net.Start("rhc_sendcuffs")
					net.WriteEntity(v.CuffedPlayer)
					net.WriteEntity(v)
				net.Send(Player)   
			end
			for k,v in pairs(player.GetAll()) do
				if v:RHC_IsArrested() then
					net.Start("rhc_update_jailtime")
						net.WriteBool(true)
						net.WriteFloat(v.RHC_JailTime or 60)
						net.WriteString(v:SteamID())
						net.WriteString(v.RHC_ArrestedBy or "None")
					net.Send(Player)
				end
			end	
		end
    end)
end)
 
hook.Add("PlayerSpawnProp", "DisablePropSpawningCuffed", function(Player)
    if Player.Restrained or Player:RHC_IsArrested() then
        TBFY_Notify(Player, 1, 4, LangTbl.CantSpawnProps) 
        return false
    end
end)

--[[
hook.Add("PlayerLoadout", "RHC_AddCuffsWToCP", function(Player)
    if Player:IsRHCWhitelisted() then
        Player:Give("weapon_r_handcuffs")
    end
end)]]
 
hook.Add("canArrest", "RHC_MustbeCuffedArrest", function(Player, ArrestedPlayer)
    if RHandcuffsConfig.RestrainArrest then
        if !ArrestedPlayer.Restrained then
            return false, LangTbl.MustBeCuffed
		elseif ArrestedPlayer:isArrested() or ArrestedPlayer:RHC_IsArrested() then
			return false, LangTbl.AlreadyArrested
        end
    end
end)

hook.Add("canUnarrest", "RestrictUnArrestCuffed", function(Player, UnarrestPlayer)
    if RHandcuffsConfig.UnarrestMustRemoveCuffs and UnarrestPlayer.Restrained and !Player:IsRHCWhitelisted() then
        return false, LangTbl.ReqLockpick
    end
end)

hook.Add("canDropWeapon", "RHC_DisableDropWeapon", function(Player)
	if Player.Restrained or Player:RHC_IsArrested() then return false end
end)

hook.Add("PlayerCanPickupWeapon", "RHC_DisablePickingUpWeapons", function(Player, Wep)
	if (Player.Restrained or Player:RHC_IsArrested()) and Wep:GetClass() != "weapon_r_cuffed" then
		return false
	end
end)

hook.Add("CanPlayerSuicide", "RHC_DisableSuicide", function(Player)
	if Player.Restrained or Player:RHC_IsArrested() then return false end
end)

hook.Add("NOVA_CanChangeSeat", "RHC_NovacarsDisableSeatChange", function(Player)
	if Player.Restrained then 
		return false, LangTbl.CantSwitchSeat
	end
end)
 
hook.Add("VC_CanEnterPassengerSeat", "RHC_VCMOD_EnterSeat", function(Player, Seat, Vehicle)
    local DraggedPlayer = Player.Dragging
    if IsValid(DraggedPlayer) then
        DraggedPlayer:EnterVehicle(Seat)
        return false
    end
end)

hook.Add("VC_CanEnterDriveBy", "VC_CanEnterDriveBy", function(Player, Seat, Ent)
	if Player.Restrained then
		return false
	end
end)

hook.Add("VC_CanSwitchSeat", "RHC_VCMOD_SwitchSeat", function(Player, SeatFrom, SeatTo)
	if Player.Restrained then
		return false
	end
end)

hook.Add("PlayerHasBeenTazed", "RHC_FixCuffTaze", function(Player)
    if Player.Restrained then
        Player:CleanUpRHC(false, true)
        Player.Restrained = true
	else
		local TazeRagdoll = Player.tazeragdoll
		if IsValid(TazeRagdoll) then
			TazeRagdoll:SetNWBool("CanRHCArrest", true)
		end	
    end
end)

hook.Add("PlayerUnTazed", "RHC_FixCuffUnTaze", function(Player)
    if Player.TazedRHCRestrained then
		Player:RHCRestrain(Player.LastRHCCuffed)
		Player.TazedRHCRestrained = false
	elseif Player.Restrained then
        Player:SetupCuffs()
        Player:SetupRHCBones("Cuffed")
    end
end)

concommand.Add("rhc_cuffplayer", function(Player, CMD, Args)
	if !Player:IsAdmin() then return end
	
	if !Args or !Args[1] then return end
	
	local Nick = string.lower(Args[1]);
	local PFound = false
	
	for k, v in pairs(player.GetAll()) do
		if (string.find(string.lower(v:Nick()), Nick)) then
			PFound = v;
			break;
		end
	end	

	if PFound then
		PFound:RHCRestrain(Player)
	end
end)

concommand.Add("rhc_arrestplayer", function(Player, CMD, Args)
	if !Player:IsAdmin() then return end
	
	if !Args or !Args[1] then return end
	
	local Nick = string.lower(Args[1]);
	local PFound = false
	
	for k, v in pairs(player.GetAll()) do
		if (string.find(string.lower(v:Nick()), Nick)) then
			PFound = v;
			break;
		end
	end	

	if PFound then
		local Time = tonumber(Args[2])
		if !Time then
			TBFY_Notify(Player, 1, 4, "Wrong input: rhc_arrestplayer Player ArrestTime")
		else
			PFound:RHC_Arrest(Time, Player)
		end
	end
end)

concommand.Add("rhc_unarrestplayer", function(Player, CMD, Args)
	if !Player:IsAdmin() then return end
	
	if !Args or !Args[1] then return end
	
	local Nick = string.lower(Args[1]);
	local PFound = false
	
	for k, v in pairs(player.GetAll()) do
		if (string.find(string.lower(v:Nick()), Nick)) then
			PFound = v;
			break;
		end
	end	

	if PFound then
		PFound:RHC_UnArrest()
	end
end)

hook.Add("PlayerUse", "RHC_DisableUse", function(Player)
	if RHandcuffsConfig.DisableUseOnRestrain and Player.Restrained then return false end
end)

hook.Add("onDarkRPWeaponDropped", "RHC_RemoveCuffsSurrOnDeath", function(Player, Ent, Wep)
	if Wep:GetClass() == "weapon_r_cuffed" or Wep:GetClass() == "tbfy_surrendered" then
		Ent:Remove()
	end
end)

hook.Add("PlayerSpawn", "RHC_RespawnInJail", function(Player)
	if Player:RHC_IsArrested() then
		Player:StripWeapons()
		Player:StripAmmo()

		if RHandcuffsConfig.RestrainOnArrest then
			Player.Restrained = true
			Player:SetupCuffs()
			Player:SetupRHCBones("Cuffed")
			Player:Give("weapon_r_cuffed")
		end
		timer.Simple(0.1, function()
			Player:RHC_SendToJail()
		end)
	end
end)