if SERVER then
	AddCSLuaFile()
end

SWEP.PrintName = "Weapon Checker"

if CLIENT then
	SWEP.Slot = 1
	SWEP.SlotPos = 9
	SWEP.DrawAmmo = false
	SWEP.DrawCrosshair = false
end

SWEP.Author = "DarkRP Developers"
SWEP.Instructions = "Left click to weapon check\nRight click to confiscate weapons\nReload to give back the weapons"
SWEP.Contact = ""
SWEP.Purpose = ""
SWEP.IsDarkRPWeaponChecker = true

SWEP.ViewModelFOV = 62
SWEP.ViewModelFlip = false
SWEP.AnimPrefix  = "rpg"

SWEP.Spawnable = true
SWEP.AdminOnly = true
SWEP.Category = "DarkRP (Utility)"
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.CheckTime = 4
SWEP.ConfiscateTime = 6

DarkRP.hookStub{
	name = "playerWeaponsChecked",
	description = "Called when a player with a weapon checker has checked another player's weapons. Note: Only called when the player looks at the weapons without confiscating. Please see playerWeaponsConfiscated for when weapons are actually confiscated.",
	parameters = {
		{
			name = "checker",
			description = "The player holding the weapon checker.",
			type = "Player"
		},
		{
			name = "target",
			description = "The player whose weapons have been checked.",
			type = "Player"
		},
		{
			name = "weapons",
			description = "The weapons that have been checked.",
			type = "table"
		},
	},
	returns = {},
	realm = "Shared"
}

DarkRP.hookStub{
	name = "playerWeaponsReturned",
	description = "Called when a player with a weapon checker has returned another player's weapons.",
	parameters = {
		{
			name = "checker",
			description = "The player holding the weapon checker.",
			type = "Player"
		},
		{
			name = "target",
			description = "The player whose weapons have been returned.",
			type = "Player"
		},
		{
			name = "weapons",
			description = "The weapons that have been returned.",
			type = "table"
		},
	},
	returns = {},
	realm = "Server"
}

DarkRP.hookStub{
	name = "playerWeaponsConfiscated",
	description = "Called when a player with a weapon checker has confiscated another player's weapons.",
	parameters = {
		{
			name = "checker",
			description = "The player holding the weapon checker.",
			type = "Player"
		},
		{
			name = "target",
			description = "The player whose weapons have been confiscated.",
			type = "Player"
		},
		{
			name = "weapons",
			description = "The weapons that have been confiscated.",
			type = "table"
		},
	},
	returns = {},
	realm = "Server"
}

if SERVER then
	util.AddNetworkString("MG_NetworkWeapons")
else
	net.Receive("MG_NetworkWeapons", function()
		chat.AddText(unpack(net.ReadTable()))
	end)
end

function SWEP:SetupDataTables()
	self:NetworkVar("Bool", 0, "IsWeaponChecking")
	self:NetworkVar("Bool", 1, "IsWeaponConfiscating")
	self:NetworkVar("Float", 0, "StartCheckTime")
	self:NetworkVar("Float", 1, "EndCheckTime")
	self:NetworkVar("Float", 2, "NextSoundTime")
end

function SWEP:Initialize()
	self:SetHoldType("normal")
end

function SWEP:Deploy()
	return true
end

function SWEP:DrawWorldModel()
end

function SWEP:PreDrawViewModel(vm)
	return true
end

function SWEP:ChatText(ply, ...)
	net.Start("MG_NetworkWeapons")
		net.WriteTable({...})
	net.Send(ply)
end

function SWEP:GetStrippableWeapons(ent, callback)
	for _, v in ipairs(ent:GetWeapons()) do
		local class = v:GetClass()
		if GAMEMODE.Config.weaponCheckerHideDefault and (table.HasValue(GAMEMODE.Config.DefaultWeapons, class) or ent:getJobTable() and ent:getJobTable().weapons and table.HasValue(ent:getJobTable().weapons, class)) then
			continue
		end
		if (GAMEMODE.Config.weaponCheckerHideNoLicense and GAMEMODE.NoLicense[class]) or GAMEMODE.Config.noStripWeapons[class] then continue end
		if v:GetNW2Bool("Armory") then continue end
		callback(v)
	end
end

function SWEP:GetPocketWeapons(ent, callback)
	for k, v in pairs(ent.darkRPPocket or {}) do
		local typ = v.Class
		if typ == "spawned_weapon" or typ == "spawned_shipment" or typ == "zmlab_meth" then
			callback(v, k)
		end
	end
end

function SWEP:ValidPlayer(ply)
	local rag_ply = ply:GetDTEntity(0)
	return ply:IsPlayer() and ply or IsValid(rag_ply) and rag_ply:IsRagdoll() and rag_ply:IsPlayer() and rag_ply:Alive() and rag_ply
end

function SWEP:PrimaryAttack()
	if self:GetIsWeaponChecking() or self:GetIsWeaponConfiscating() then return end
	local owner = self:GetOwner()
	self:SetNextPrimaryFire(CurTime() + 0.3)
	owner:LagCompensation(true)
	local trace = owner:GetEyeTrace()
	owner:LagCompensation(false)
	local ent = trace.Entity
	if !IsValid(ent) or !self:ValidPlayer(ent) or ent:GetPos():DistToSqr(owner:GetPos()) > 10000 then return end
	self:SetIsWeaponChecking(true)
	self:SetStartCheckTime(CurTime())
	local time = self.CheckTime
	time = hook.Run("checkerCheckTime", self, owner, time) or time
	self:SetEndCheckTime(CurTime() + time)
	self:SetNextSoundTime(CurTime() + 0.3)
	if CLIENT then
		self.Dots = ""
		self.NextDotsTime = CurTime() + 0.5
	end
end

function SWEP:SecondaryAttack()
	if self:GetIsWeaponChecking() or self:GetIsWeaponConfiscating() then return end
	local owner = self:GetOwner()
	self:SetNextSecondaryFire(CurTime() + 0.3)
	owner:LagCompensation(true)
	local trace = owner:GetEyeTrace()
	owner:LagCompensation(false)
	local ent = trace.Entity
	if !IsValid(ent) or !self:ValidPlayer(ent) or ent:GetPos():DistToSqr(owner:GetPos()) > 10000 then return end
	self:SetIsWeaponConfiscating(true)
	self:SetStartCheckTime(CurTime())
	local time = self.ConfiscateTime
	time = hook.Run("checkerConfiscateTime", self, owner, time) or time
	self:SetEndCheckTime(CurTime() + time)
	self:SetNextSoundTime(CurTime() + 0.3)
	if CLIENT then
		self.Dots = ""
		self.NextDotsTime = CurTime() + 0.5
	end
end

function SWEP:Reload()
	if CLIENT or CurTime() < (self.NextReloadTime or 0) then return end
	local owner = self:GetOwner()
	self.NextReloadTime = CurTime() + 1
	local trace = owner:GetEyeTrace()
	local ent = trace.Entity
	if !IsValid(ent) or !self:ValidPlayer(ent) or ent:GetPos():DistToSqr(owner:GetPos()) > 10000 then return end
	ent = self:ValidPlayer(ent)
	if !ent.ConfiscatedWeapons then
		DarkRP.notify(owner, 1, 4, DarkRP.getPhrase("no_weapons_confiscated", ent:Name()))
		return
	else
		for k, v in pairs(ent.ConfiscatedWeapons) do
			if isnumber(k) then
				local replaced = ents.Create(v.Class)
				if replaced:IsValid() then
					duplicator.DoGeneric(replaced, v)
					replaced:Spawn()
					replaced:Activate()
					duplicator.DoGenericPhysics(replaced, ent, v)
					table.Merge(replaced:GetTable(), v)
					replaced.USED = nil
					ent:addPocketItem(replaced)
				end
				continue
			end
			local wep = ent:Give(v.class, true)
			if wep:IsValid() then
				wep:SetClip1(v.clip1)
				wep:SetClip2(v.clip2)
			end
			ent:GiveAmmo(v.primaryAmmoCount, v.primaryAmmoType, true)
			ent:GiveAmmo(v.secondaryAmmoCount, v.secondaryAmmoType, true)
		end
		DarkRP.notify(owner, 2, 4, DarkRP.getPhrase("returned_persons_weapons", ent:Name()))
		hook.Call("playerWeaponsReturned", nil, owner, ent, ent.ConfiscatedWeapons)
		ent.ConfiscatedWeapons = nil
	end
end

function SWEP:Holster()
	self:SetIsWeaponChecking(false)
	self:SetIsWeaponConfiscating(false)
	self:SetNextSoundTime(0)
	return true
end

function SWEP:Succeed()
	local owner = self:GetOwner()
	local confiscating = self:GetIsWeaponConfiscating()
	self:Fail()
	if confiscating then
		local trace = owner:GetEyeTrace()
		local ent = trace.Entity
		if !SERVER or !IsValid(ent) or !self:ValidPlayer(ent) then return end
		ent = self:ValidPlayer(ent)
		self:PrintWeapons(ent, DarkRP.getPhrase("confiscated_these_weapons"))
		local stripped = {}
		self:GetStrippableWeapons(ent, function(wep)
			local class = wep:GetClass()
			stripped[class] = {
				class = class,
				primaryAmmoCount = ent:GetAmmoCount(wep:GetPrimaryAmmoType()),
				primaryAmmoType = wep:GetPrimaryAmmoType(),
				secondaryAmmoCount = ent:GetAmmoCount(wep:GetSecondaryAmmoType()),
				secondaryAmmoType = wep:GetSecondaryAmmoType(),
				clip1 = wep:Clip1(),
				clip2 = wep:Clip2()
			}
			ent:StripWeapon(class)
			ent:RemoveAmmo(ent:GetAmmoCount(wep:GetPrimaryAmmoType()), wep:GetPrimaryAmmoType())
			ent:RemoveAmmo(ent:GetAmmoCount(wep:GetSecondaryAmmoType()), wep:GetSecondaryAmmoType())
		end)
		self:GetPocketWeapons(ent, function(wep, id)
			if wep.ClassName == "zmlab_meth" then
				local amt = wep.DT and wep.DT.MethAmount
				if amt and amt > 0 then
					local worth = math.floor((zmlab.currentPrice * amt) / 2)
					if worth > 0 then
						owner:addMoney(worth)
						DarkRP.notify(owner, 0, 4, "Du hast "..math.Round(amt, 1).."g hergestelltes Meth konfisziert und $"..string.Comma(worth).." erhalten.")
					end
				end
			else
				stripped[id] = table.Copy(wep)
			end
			ent:removePocketItem(id)
		end)
		if !ent.ConfiscatedWeapons then
			if !table.IsEmpty(stripped) then
				ent.ConfiscatedWeapons = stripped
			end
		else
			for k, v in pairs(stripped) do
				ent.ConfiscatedWeapons[k] = v
			end
		end
		hook.Call("playerWeaponsConfiscated", nil, owner, ent, ent.ConfiscatedWeapons)
		if !table.IsEmpty(stripped) then
			self:EmitSound("npc/combine_soldier/gear5.wav", 65)
			self:SetNextSoundTime(CurTime() + 0.3)
		else
			self:EmitSound("ambient/energy/zap1.wav", 65)
			self:SetNextSoundTime(0)
		end
	else
		local trace = owner:GetEyeTrace()
		local ent = trace.Entity
		if !SERVER or !IsValid(ent) or !self:ValidPlayer(ent) then return end
		ent = self:ValidPlayer(ent)
		local weps = {}
		self:GetStrippableWeapons(ent, function(wep)
			table.insert(weps, wep)
		end)
		hook.Call("playerWeaponsChecked", nil, owner, ent, weps)
		weps = {}
		self:GetPocketWeapons(ent, function(wep, id)
			table.insert(weps, {wep, id})
		end)
		hook.Call("playerPocketItemsChecked", nil, owner, ent, weps)
		self:PrintWeapons(ent, DarkRP.getPhrase("persons_weapons", ent:Name()))
	end
end

function SWEP:FindWeaponName(info)
	if info.Class == "spawned_weapon" then
		local class = info.DT and info.DT.WeaponClass
		if !class then return end
		local wep = ents.Create(class)
		if !IsValid(wep) then return end
		local name = wep:GetPrintName() or wep:GetClass()
		local is_illegal = MG_IsExplosive(wep) or MG_IsHeavy(wep)
		wep:Remove()
		return name, is_illegal
	elseif info.Class == "spawned_shipment" then
		local content = info.DT and info.DT.contents or ""
		local contents = CustomShipments[content]
		if !contents then return end
		local wep = ents.Create(contents.entity)
		if !IsValid(wep) or !wep:IsWeapon() then SafeRemoveEntity(wep) return end
		local name = wep.GetPrintName and wep:GetPrintName() or wep.PrintName or wep:GetClass()
		local is_illegal = MG_IsExplosive(wep) or MG_IsHeavy(wep)
		wep:Remove()
		return name, is_illegal
	elseif info.Class == "zmlab_meth" then
		return "Hergestelltes Meth", true
	end
end

function SWEP:PrintWeapons(ent, weaponsFoundPhrase)
	local owner = self:GetOwner()
	local text = {}
	local weps = {}
	self:GetStrippableWeapons(ent, function(wep)
		table.insert(weps, wep)
	end)
	local first = true
	local cont = false
	for _, wep in ipairs(weps) do
		local name = wep:GetPrintName() or wep:GetClass()
		local is_illegal = MG_IsExplosive(wep) or MG_IsHeavy(wep)
		if hook.Run("canPrintWeaponName", self, ent, name, is_illegal, false) != false then
			if !first then
				table.insert(text, color_white)
				table.insert(text, ", ")
			else
				first = false
			end
			table.insert(text, is_illegal and Color(200, 0, 0) or Color(0, 200, 0))
			table.insert(text, name)
			cont = true
		end
	end
	self:GetPocketWeapons(ent, function(wep)
		local name, is_illegal = self:FindWeaponName(wep)
		if hook.Run("canPrintWeaponName", self, ent, name, is_illegal, true) != false then
			if name then
				if !first then
					table.insert(text, color_white)
					table.insert(text, ", ")
				else
					first = false
				end
				table.insert(text, is_illegal and Color(200, 0, 0) or Color(0, 200, 0))
				table.insert(text, name)
				cont = true
			end
		end
	end)
	if !cont then
		self:ChatText(owner, color_white, DarkRP.getPhrase("no_illegal_weapons", ent:Name()))
		return
	end
	self:ChatText(owner, color_white, weaponsFoundPhrase)
	self:ChatText(owner, unpack(text))
end

function SWEP:Fail()
	self:SetIsWeaponChecking(false)
	self:SetIsWeaponConfiscating(false)
	self:SetHoldType("normal")
	self:SetNextSoundTime(0)
end

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

	if (dt.IsWeaponChecking or dt.IsWeaponConfiscating) and dt.EndCheckTime != 0 then
		local trace = self:GetOwner():GetEyeTraceNoCursor()
		if !IsValid(trace.Entity) or !self:ValidPlayer(trace.Entity, true) or trace.HitPos:DistToSqr(self:GetOwner():GetShootPos()) > 10000 then
			self:Fail()
		end
		if self:GetEndCheckTime() <= CurTime() then
			self:Succeed()
		end
	end
	if dt.NextSoundTime != 0 and CurTime() >= dt.NextSoundTime then
		if (dt.IsWeaponChecking or dt.IsWeaponConfiscating) then
			dt.NextSoundTime = CurTime() + 0.5
			self:EmitSound("npc/combine_soldier/gear5.wav", 65)
		else
			dt.NextSoundTime = 0
			self:EmitSound("npc/combine_soldier/gear5.wav", 60)
		end
	end
	if CLIENT and tb.NextDotsTime and CurTime() >= tb.NextDotsTime then
		tb.NextDotsTime = CurTime() + 0.5
		tb.Dots = tb.Dots or ""
		local len = string.len(tb.Dots)
		local dots = {
			[0] = ".",
			[1] = "..",
			[2] = "...",
			[3] = ""
		}
		tb.Dots = dots[len]
	end
end

function SWEP:DrawHUD()
	if (self:GetIsWeaponChecking() or self:GetIsWeaponConfiscating()) and self:GetEndCheckTime() != 0 then
		self.Dots = self.Dots or ""
		local w = ScrW()
		local h = ScrH()
		local x, y, width, height = w / 2 - w / 10, h / 2, w / 5, h / 15
		local time = self:GetEndCheckTime() - self:GetStartCheckTime()
		local curtime = CurTime() - self:GetStartCheckTime()
		local status = math.Clamp(curtime / time, 0, 1)
		local BarWidth = status * (width - 16)
		local cornerRadius = math.Min(8, BarWidth / 3 * 2 - BarWidth / 3 * 2 % 2)
		draw.RoundedBox(8, x, y, width, height, Color(10, 10, 10, 120))
		draw.RoundedBox(cornerRadius, x + 8, y + 8, BarWidth, height - 16, Color(0, 0 + (status * 255), 255 - (status * 255), 255))
		if self:GetIsWeaponChecking() then
			draw.DrawNonParsedSimpleText(DarkRP.getPhrase("checking_weapons")..self.Dots, "Trebuchet24", w / 2, y + height / 2, color_white, 1, 1)
		else
			draw.DrawNonParsedSimpleText("Waffen konfiszieren"..self.Dots, "Trebuchet24", w / 2, y + height / 2, color_white, 1, 1)
		end
	end
end

if SERVER then
	hook.Add("OnPlayerChangedTeam", "MG_WeaponChecker_Reset", function(ply)
		ply.ConfiscatedWeapons = nil
	end)

	hook.Add("PlayerDeath", "MG_WeaponChecker_Reset", function(ply)
		ply.ConfiscatedWeapons = nil
	end)
end