if SERVER then
	AddCSLuaFile("shared.lua")
end

if CLIENT then
	SWEP.PrintName = "Handcuffs"
	SWEP.Slot = 2
	SWEP.SlotPos = 1
	SWEP.DrawAmmo = false
	SWEP.DrawCrosshair = false
end

SWEP.Author = "ToBadForYou"
SWEP.Instructions = "Left Click: Restrain/Release.\nRight Click: Force Players out of vehicle.\nReload: Drag Cuffed Player."
SWEP.Contact = ""
SWEP.Purpose = ""

SWEP.HoldType = "melee"
SWEP.UseHands = true

SWEP.ViewModelFOV = 62
SWEP.ViewModelFlip = false
SWEP.AnimPrefix	 = "melee"
SWEP.Category = "Military RP"
SWEP.Spawnable = true
SWEP.AdminSpawnable = true

SWEP.Primary.ClipSize = -1
SWEP.Primary.DefaultClip = 0
SWEP.Primary.Automatic = false
SWEP.Primary.Ammo = ""

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

SWEP.PlayBackRate = 2

function SWEP:Initialize()
	self:SetHoldType("melee")
	self.ViewModel = "models/tobadforyou/c_hand_cuffs.mdl"
	self.WorldModel = "models/tobadforyou/handcuffs.mdl"
end

function SWEP:CanPrimaryAttack() return true end
function SWEP:CanSecondaryAttack() return true end

function SWEP:PlayCuffSound(Time)
	timer.Simple(Time, function() if IsValid(self) then self:EmitSound("npc/metro-police/gear2.wav") end end)
	timer.Simple(Time+1, function() if IsValid(self) then self:EmitSound("npc/metro-police/gear2.wav") end end)
end

local function IsPolice(ply)
	return true
end

local function RestrainPlayer(target, cop)
	if not IsValid(target) or not target:IsPlayer() then return end
	
	target:SetNWBool("rhc_cuffed", true)
	if SERVER then
		target:EmitSound("npc/metro-police/gear2.wav")
		
		local serverlogsPlugin = ix.plugin.list["serverlogs"]
		if serverlogsPlugin then
			serverlogsPlugin:AddLog("HANDCUFF_RESTRAIN", cop:Nick() .. " restrained " .. target:Nick(), cop, {target = target})
		end
		
		-- Save weapons
		target.RHC_SavedWeapons = {}
		for _, w in ipairs(target:GetWeapons()) do
			table.insert(target.RHC_SavedWeapons, w:GetClass())
		end
		
		target:StripWeapons()
		target:Give("weapon_r_cuffed")
		target:SelectWeapon("weapon_r_cuffed")

		target.RHC_OldWalk = target:GetWalkSpeed()
		target.RHC_OldRun = target:GetRunSpeed()
		target:SetWalkSpeed(100)
		target:SetRunSpeed(100)
	end
end

local CuffedBones = {
	["ValveBiped.Bip01_R_UpperArm"] = true,
	["ValveBiped.Bip01_L_UpperArm"] = true,
	["ValveBiped.Bip01_R_Forearm"] = true,
	["ValveBiped.Bip01_L_Forearm"] = true,
	["ValveBiped.Bip01_R_Hand"] = true,
	["ValveBiped.Bip01_L_Hand"] = true,
}

local function UnrestrainPlayer(target, cop)
	if not IsValid(target) or not target:IsPlayer() then return end

	target:SetNWBool("rhc_cuffed", false)
	target:SetNWBool("MRP_Gagged", false)
	target:SetNWBool("MRP_Blindfolded", false)
	if SERVER then
		target:EmitSound("npc/metro-police/gear2.wav")
		
		local serverlogsPlugin = ix.plugin.list["serverlogs"]
		if serverlogsPlugin then
			serverlogsPlugin:AddLog("HANDCUFF_UNRESTRAIN", cop:Nick() .. " unrestrained " .. target:Nick(), cop, {target = target})
		end
		
		target:StripWeapon("weapon_r_cuffed")
		
		for boneName, _ in pairs(CuffedBones) do
			local boneId = target:LookupBone(boneName)
			if boneId then
				target:ManipulateBoneAngles(boneId, Angle(0,0,0))
			end
		end

		if target.RHC_SavedWeapons then
			for _, class in ipairs(target.RHC_SavedWeapons) do
				target:Give(class)
			end
			target.RHC_SavedWeapons = nil
		end

		if target.RHC_OldWalk then
			target:SetWalkSpeed(target.RHC_OldWalk)
			target:SetRunSpeed(target.RHC_OldRun)
		end
        
        if IsValid(target.DraggedBy) then
            target.DraggedBy.Dragging = nil
            target.DraggedBy = nil
        end
	end
end

function SWEP:Think()
	local target = self.AttemptToCuff
	if IsValid(target) then
		local vm = self.Owner:GetViewModel()
		local ResetSeq = vm:LookupSequence("Reset")
		
		local TraceEnt = self.Owner:GetEyeTrace().Entity
		local distance = target:GetPos():Distance(self.Owner:GetPos())
		
		if not IsValid(TraceEnt) or TraceEnt ~= target or distance > 150 then
			self.AttemptToCuff = nil
			target.RHC_BeingCuffed = false
			vm:SendViewModelMatchingSequence(ResetSeq)
			target:Freeze(false)
		elseif CurTime() >= self.AttemptCuffFinish then
			if SERVER then
				RestrainPlayer(target, self.Owner)
			end
			target.RHC_BeingCuffed = false
			self.AttemptToCuff = nil
			vm:SendViewModelMatchingSequence(ResetSeq)
			target:Freeze(false)
		end
	end
end

function SWEP:PrimaryAttack()
	self.Weapon:SetNextPrimaryFire(CurTime() + 1.5)
	
	if not IsPolice(self.Owner) then
		if SERVER then self.Owner:ChatPrint("Вам не разрешено использовать наручники.") end
		return
	end

	self.Weapon:EmitSound("npc/vort/claw_swing" .. math.random(1, 2) .. ".wav")
	self.Owner:SetAnimation(PLAYER_ATTACK1)

	local target = self.Owner:GetEyeTrace().Entity
	if not IsValid(target) or not target:IsPlayer() then return end
	if self.Owner:EyePos():Distance(target:GetPos()) > 150 then return end

	-- Uncuff
	if target:GetNWBool("rhc_cuffed", false) then
		if SERVER then UnrestrainPlayer(target, self.Owner) end
		return
	end

	-- Begin Cuffing
	self.AttemptToCuff = target
	self.AttemptCuffStart = CurTime()
	self.AttemptCuffFinish = CurTime() + 2
	target.RHC_BeingCuffed = true

	local vm = self.Owner:GetViewModel()
	local DeploySeq = vm:LookupSequence("Deploy")
	vm:SendViewModelMatchingSequence(DeploySeq)
	vm:SetPlaybackRate(self.PlayBackRate)
	
	self:PlayCuffSound(0.3)
	if SERVER then target:Freeze(true) end
end

-- Dragging logic mapped to Reload
function SWEP:Reload()
	if not IsFirstTimePredicted() then return end
	if self.NextRPress and self.NextRPress > CurTime() then return end
	self.NextRPress = CurTime() + 1
	if CLIENT then return end

	local target = self.Owner:GetEyeTrace().Entity
	
	-- Stop dragging
	if IsValid(self.Owner.Dragging) then
		self.Owner.Dragging.DraggedBy = nil
		self.Owner.Dragging = nil
		self.Owner:ChatPrint("Вы перестали тащить игрока.")
		return
	end

	-- Start dragging
	if IsValid(target) and target:IsPlayer() and target:GetNWBool("rhc_cuffed", false) then
		if self.Owner:EyePos():Distance(target:GetPos()) <= 150 then
			self.Owner.Dragging = target
			target.DraggedBy = self.Owner
			self.Owner:ChatPrint("Вы теперь тащите " .. target:Nick())
		end
	end
end

function SWEP:SecondaryAttack()
    -- Force out of vehicle
	self.Weapon:SetNextSecondaryFire(CurTime() + 1)
	if CLIENT then return end
	if not IsPolice(self.Owner) then return end

    -- Helper function to find an empty seat
    local function GetEmptySeat(veh)
        if veh.IsDriveable and veh:IsDriveable() then
            -- LVS Specific
            if veh.GetDriver and not IsValid(veh:GetDriver()) then
                local dSeat = veh:GetDriverSeat()
                if IsValid(dSeat) then return dSeat end
            end
            if veh.GetPassengerSeats then
                for _, seat in pairs(veh:GetPassengerSeats()) do
                    if IsValid(seat) and not IsValid(seat:GetDriver()) then
                        return seat
                    end
                end
            end
            return nil
        end
        
        -- Standard/Simfphys
        if veh.GetDriver and not IsValid(veh:GetDriver()) then return veh end
        if veh.pSeats then
            for _, seat in pairs(veh.pSeats) do
                if IsValid(seat) and not IsValid(seat:GetDriver()) then
                    return seat
                end
            end
        end
        return nil
    end

	local target = self.Owner:GetEyeTrace().Entity
	if not IsValid(target) then return end
	if self.Owner:GetPos():Distance(target:GetPos()) > 300 then return end

    local isVeh = target:IsVehicle() or (target.GetClass and string.find(string.lower(target:GetClass()), "lvs"))

    if IsValid(self.Owner.Dragging) then
        local dragged = self.Owner.Dragging
        if isVeh then
            local seat = GetEmptySeat(target)
            if IsValid(seat) then
                dragged:EnterVehicle(seat)
                self.Owner.Dragging = nil
                dragged.DraggedBy = nil
                self.Owner:ChatPrint("Игрок посажен в транспорт.")
            else
                self.Owner:ChatPrint("Нет свободных мест.")
            end
        end
    else
        if isVeh then
            local found = false
            
            -- LVS Specific
            if target.IsDriveable and target:IsDriveable() then
                local drv = target:GetDriver()
                if IsValid(drv) and drv:GetNWBool("rhc_cuffed", false) then
                    drv:ExitVehicle()
                    found = true
                end
                
                if target.GetPassengerSeats then
                    for _, seat in pairs(target:GetPassengerSeats()) do
                        local sDrv = IsValid(seat) and seat:GetDriver()
                        if IsValid(sDrv) and sDrv:GetNWBool("rhc_cuffed", false) then
                            sDrv:ExitVehicle()
                            found = true
                        end
                    end
                end
            else
                -- Standard/Simfphys
                local drv = target:GetDriver()
                if IsValid(drv) and drv:GetNWBool("rhc_cuffed", false) then
                    drv:ExitVehicle()
                    found = true
                end
                if target.pSeats then
                    for _, seat in pairs(target.pSeats) do
                        local sDrv = IsValid(seat) and seat:GetDriver()
                        if IsValid(sDrv) and sDrv:GetNWBool("rhc_cuffed", false) then
                            sDrv:ExitVehicle()
                            found = true
                        end
                    end
                end
            end
            
            if found then
                self.Owner:ChatPrint("Игрок(и) вытащен(ы) из транспорта.")
            end
        end
    end
end

if SERVER then
    hook.Add("PlayerTick", "MRP_Handcuffs_DragLogic", function(ply)
        local dragged = ply.Dragging
        if IsValid(dragged) and dragged:IsPlayer() and dragged:GetNWBool("rhc_cuffed", false) then
            local dist = ply:GetPos():Distance(dragged:GetPos())
            if dist > 300 then
                ply.Dragging = nil
                dragged.DraggedBy = nil
                ply:ChatPrint("Таскание прекращено: слишком далеко.")
            elseif dist > 100 then
                local dir = (ply:GetPos() - dragged:GetPos()):GetNormalized()
                dragged:SetVelocity(dir * (dist * 2))
            end
        elseif dragged then
            ply.Dragging = nil
            if IsValid(dragged) then dragged.DraggedBy = nil end
        end
    end)
    
    hook.Add("PlayerDisconnected", "MRP_Handcuffs_DragCleanup", function(ply)
        if IsValid(ply.DraggedBy) then ply.DraggedBy.Dragging = nil end
        if IsValid(ply.Dragging) then ply.Dragging.DraggedBy = nil end
    end)
end

if CLIENT then
	function SWEP:DrawWorldModel()
		if not IsValid(self.Owner) then return end
		local boneindex = self.Owner:LookupBone("ValveBiped.Bip01_R_Hand")
		if boneindex then
			local HPos, HAng = self.Owner:GetBonePosition(boneindex)
			local offset = HAng:Right() * 0.5 + HAng:Forward() * 3.3
			HAng:RotateAroundAxis(HAng:Right(), 0)
			HAng:RotateAroundAxis(HAng:Forward(), -90)
			self:SetRenderOrigin(HPos + offset)
			self:SetRenderAngles(HAng)
			self:DrawModel()
		end
	end

	function SWEP:DrawHUD()
		draw.SimpleText("ЛКМ: Надеть/Снять наручники", "default", ScrW()/2, 5, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_TOP, 2, color_black)
		draw.SimpleText("ПКМ: Посадить/Вытащить из транспорта", "default", ScrW()/2, 15, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_TOP, 2, color_black)
		draw.SimpleText("Перезарядка (R): Тащить закованного", "default", ScrW()/2, 25, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_TOP, 2, color_black)

		local target = self.AttemptToCuff
		if not IsValid(target) then return end

		local percent = math.Clamp((CurTime() - self.AttemptCuffStart) / (self.AttemptCuffFinish - self.AttemptCuffStart), 0, 1)
		draw.SimpleText("Заковываем " .. target:Nick() .. " (" .. math.Round(percent * 100) .. "%)", "DermaLarge", ScrW()/2, ScrH()/2, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
	end
end
