--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| 	Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]

/*--------------------------------------------------
	=============== VJ Spawn Menu ===============
	*** Copyright (c) 2012-2023 by DrVrej, All rights reserved. ***
	No parts of this code or any of its contents may be reproduced, copied, modified or adapted,
	without the prior written consent of the author, unless otherwise indicated for stand-alone materials.
--------------------------------------------------*/
if (!file.Exists("autorun/vj_base_autorun.lua","LUA")) then return end

AddCSLuaFile()
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------ Hooks ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
if CLIENT then
	hook.Add("PopulateVJBaseHome","AddVJBaseSpawnMenu_Home",function(pnlContent,tree,node)
		local hometree = tree:AddNode("Home", "icon16/monkey.png")
		hometree.PropPanel = vgui.Create("ContentContainer", pnlContent)
		hometree.PropPanel:SetVisible(false)
		hometree.PropPanel:SetTriggerSpawnlistChange(false)
		//hometree.PropPanel:MoveToFront()
		
		function hometree:DoClick()
			pnlContent:SwitchPanel(self.PropPanel)
		end
		
		local Header1 = vgui.Create("DLabel")
		Header1:SetPos(40, 40)
		Header1:SetSize(200, 70)
		Header1:SetTextColor(Color(255, 102, 0, 255))
		Header1:SetText("Welcome to VJ Base!")
		hometree.PropPanel:Add(Header1)
		
		local Text1 = vgui.Create("DLabel")
		Header1:SetPos(80, 80)
		Text1:SetSize(100, 35)
		Text1:SetTextColor(Color(102, 204, 255, 255))
		Text1:SetText("By: DrVrej")
		hometree.PropPanel:Add(Text1)
		
		hometree:InternalDoClick()
	end)
	--[-------------------------------------------------------]--
	local function VJ_PopulateTrees(pnlContent, tree, node, vjTreeName, vjIcon, vjList)
		local roottree = tree:AddNode(vjTreeName, vjIcon)
		if vjTreeName == "SNPCs" then
			roottree:MoveToFront() -- Make this the main tree
		end
		roottree.PropPanel = vgui.Create("ContentContainer", pnlContent)
		roottree.PropPanel:SetVisible(false)
		roottree.PropPanel:SetTriggerSpawnlistChange(false)
		
		function roottree:DoClick()
			pnlContent:SwitchPanel(self.PropPanel)
		end
		
		local EntList = list.Get(vjList)
		local CatInfoList = list.Get("VJBASE_CATEGORY_INFO")
		
		-- Categorize them
		local Categories = {}
		for k, v in pairs(EntList) do
			local Category = v.Category or "Uncategorized"
			if Category == "VJ Base" then Category = "Default" end
			local Tab = Categories[Category] or {}
			Tab[k] = v
			Categories[Category] = Tab
		end
		
		-- Create an icon for each one and put them on the panel
		for CategoryName, v in SortedPairs(Categories) do
			
			-- Category icon
			local icon = vjIcon -- Make the default icon the category icon
			if list.HasEntry("VJBASE_CATEGORY_INFO", CategoryName) then
				icon = CatInfoList[CategoryName].icon
			elseif CategoryName == "Default" then
				icon = "vj_base/icons/vrejgaming.png"
			end
			
			local node = roottree:AddNode(CategoryName, icon)
			local CatPropPanel = vgui.Create("ContentContainer", pnlContent)
			CatPropPanel:SetVisible(false)
			
			-- Write the name of the categories in both the general menu and in its own menu
			local generalHeader = vgui.Create("ContentHeader", roottree.PropPanel)
			generalHeader:SetText(CategoryName)
			roottree.PropPanel:Add(generalHeader)
			local catHeader = vgui.Create("ContentHeader", CatPropPanel)
			catHeader:SetText(CategoryName)
			CatPropPanel:Add(catHeader)
			
			if vjTreeName == "SNPCs" then
				for name, ent in SortedPairsByMemberValue(v, "Name") do
					local t = {
						nicename	= ent.Name or name,
						spawnname	= name,
						material	= "entities/" .. name .. ".png",
						weapon		= ent.Weapons,
						admin		= ent.AdminOnly
					}
					spawnmenu.CreateContentIcon("npc", CatPropPanel, t)
					spawnmenu.CreateContentIcon("npc", roottree.PropPanel, t)
				end
			elseif vjTreeName == "Weapons" then
				for _, ent in SortedPairsByMemberValue(v, "PrintName") do
					local t = { 
						nicename	= ent.PrintName or ent.ClassName,
						spawnname	= ent.ClassName,
						material	= "entities/" .. ent.ClassName .. ".png",
						admin		= ent.AdminOnly
					}
					spawnmenu.CreateContentIcon(ent.ScriptedEntityType or "weapon", CatPropPanel, t)
					spawnmenu.CreateContentIcon(ent.ScriptedEntityType or "weapon", roottree.PropPanel, t)
				end
			elseif vjTreeName == "Entities" then
				for _, ent in SortedPairsByMemberValue(v, "PrintName") do
					local t = { 
						nicename	= ent.PrintName or ent.ClassName,
						spawnname	= ent.ClassName,
						material	= "entities/" .. ent.ClassName .. ".png",
						admin		= ent.AdminOnly
					}
					spawnmenu.CreateContentIcon(ent.ScriptedEntityType or "entity", CatPropPanel, t)
					spawnmenu.CreateContentIcon(ent.ScriptedEntityType or "entity", roottree.PropPanel, t)
				end
			end
			function node:DoClick()
				pnlContent:SwitchPanel(CatPropPanel)
			end
		end
		roottree:SetExpanded(true)
		if vjTreeName == "SNPCs" then
			roottree:InternalDoClick() -- Automatically select this folder when the menu first opens
		end
	end
	--[-------------------------------------------------------]--
	hook.Add("PopulateVJBaseNPC", "AddVJBaseSpawnMenu_NPC", function(pnlContent, tree, node)
		VJ_PopulateTrees(pnlContent, tree, node, "SNPCs", "icon16/monkey.png", "VJBASE_SPAWNABLE_NPC")
	end)
	--[-------------------------------------------------------]--
	hook.Add("PopulateVJBaseWeapons","AddVJBaseSpawnMenu_Weapon", function(pnlContent, tree, node)
		VJ_PopulateTrees(pnlContent, tree, node, "Weapons", "icon16/gun.png", "VJBASE_SPAWNABLE_WEAPON")
	end)
	--[-------------------------------------------------------]--
	hook.Add("PopulateVJBaseEntities","AddVJBaseSpawnMenu_Entity",function(pnlContent,tree,node)
		VJ_PopulateTrees(pnlContent, tree, node, "Entities", "icon16/bricks.png", "VJBASE_SPAWNABLE_ENTITIES")
	end)
	--[-------------------------------------------------------]--
	hook.Add("PopulateVJBaseTools","AddVJBaseSpawnMenu_Tool",function(pnlContent,tree,node)
		local tooltree = tree:AddNode("Tools", "icon16/bullet_wrench.png")
		tooltree.PropPanel = vgui.Create("ContentContainer", pnlContent)
		tooltree.PropPanel:SetVisible(false)
		tooltree.PropPanel:SetTriggerSpawnlistChange(false)

		function tooltree:DoClick()
			pnlContent:SwitchPanel(self.PropPanel)
		end
		
		local ToolList = spawnmenu.GetTools()
		if (ToolList) then
			for _, nv in pairs(ToolList) do
				if nv.Name == "DrVrej" then
					for _, nv2 in pairs(nv.Items) do
						if nv2.ItemName == "Tools" then
							//local node = tooltree:AddNode("Default", "icon16/bullet_wrench.png")
							local CatPropPanel = vgui.Create("ContentContainer", pnlContent)
							CatPropPanel:SetVisible(false)
							local Header = vgui.Create("ContentHeader", tooltree.PropPanel)
							Header:SetText("Tools")
							tooltree.PropPanel:Add(Header)
							for _, nv3 in pairs(nv2) do
								if !istable(nv3) then continue end
									local t = { 
										nicename	= nv3.Text,
										spawnname	= nv3.ItemName,
										//material	= "entities/" .. ent.ClassName .. ".png",
										//admin		= ent.AdminOnly
									}
									spawnmenu.CreateContentIcon("tool", CatPropPanel, t)
									spawnmenu.CreateContentIcon("tool", tooltree.PropPanel, t)
								end
							function tooltree:DoClick()	
								pnlContent:SwitchPanel(CatPropPanel)
							end
						end
					end
				end
			end
		end
		tooltree:SetExpanded(true)
	end)
	
	--[-------------------------------------------------------]--
	-- Adds the searching functionality for the VJ Base spawn menu. Note: This algorithm is based on the base GMod algorithm.
	search.AddProvider(function(str)
		local results = {}
		local entities = {}
		
		local function searchList(lname, lctype)
			for k, v in pairs(list.Get(lname)) do
				v.ClassName = k
				v.PrintName = v.PrintName or v.Name
				v.ScriptedEntityType = lctype
				table.insert(entities, v)
			end
		end
		searchList("VJBASE_SPAWNABLE_NPC", "npc")
		searchList("VJBASE_SPAWNABLE_WEAPON", "weapon")
		searchList("VJBASE_SPAWNABLE_ENTITIES", "entity")
		// searchList("VJBASE_SPAWNABLE_VEHICLES", "vehicle") -- vehicle (Not yet lol)

		for _, v in pairs(entities) do
			local name = v.PrintName
			local name_c = v.ClassName
			if (!name && !name_c) then continue end
			
			if ((name && name:lower():find(str, nil, true)) or (name_c && name_c:lower():find(str, nil, true))) then
				local entry = {
					text = v.PrintName or v.ClassName,
					icon = spawnmenu.CreateContentIcon(v.ScriptedEntityType or "entity", nil, {
						nicename = v.PrintName or v.ClassName,
						spawnname = v.ClassName,
						material = "entities/" .. v.ClassName .. ".png",
						admin = v.AdminOnly
					}),
					words = {v}
				}

				table.insert(results, entry)
			end
		end
		table.SortByMember(results, "text", true)
		return results
	end, "vjbase_npcs")

	--[-------------------------------------------------------]--
	spawnmenu.AddCreationTab("VJ Base", function()
		local ctrl = vgui.Create("SpawnmenuContentPanel")
		ctrl:EnableSearch("vjbase_npcs", "PopulateVJBaseNPC")
		//ctrl:CallPopulateHook("PopulateVJBaseHome")
		ctrl:CallPopulateHook("PopulateVJBaseNPC")
		ctrl:CallPopulateHook("PopulateVJBaseWeapons")
		ctrl:CallPopulateHook("PopulateVJBaseEntities")
		ctrl:CallPopulateHook("PopulateVJBaseTools")
		
		local sidebar = ctrl.ContentNavBar
		sidebar.Options = vgui.Create( "VJ_SpawnmenuNPCSidebarToolbox", sidebar )
	
		return ctrl
	end, "vj_base/icons/vrejgaming.png", 60, "All VJ Base entities are located here!") // icon16/plugin.png
	
	--[-------------------------------------------------------]--
	-- Based on GMod's SpawnmenuNPCSidebarToolbox but with some changes
	local PANEL = {}
	Derma_Hook(PANEL, "Paint", "Paint", "Tree")
	PANEL.m_bBackground = true -- Hack for above

	function PANEL:AddCheckbox(text, cvar)
		local DermaCheckbox = self:Add("DCheckBoxLabel", self)
		DermaCheckbox:Dock(TOP)
		DermaCheckbox:SetText(text)
		DermaCheckbox:SetDark(true)
		DermaCheckbox:SetConVar(cvar)
		DermaCheckbox:SizeToContents()
		DermaCheckbox:DockMargin(0, 5, 0, 0)
	end

	function PANEL:Init()
		self:SetOpenSize(150)
		self:DockPadding(15, 10, 15, 10)

		self:AddCheckbox("#vjbase.spawn.menu.npc.disablethinking", "ai_disabled")
		self:AddCheckbox("#vjbase.spawn.menu.npc.ignoreplayers", "ai_ignoreplayers")
		self:AddCheckbox("#vjbase.spawn.menu.npc.keepcorpses", "ai_serverragdolls")
		self:AddCheckbox("#vjbase.spawn.menu.npc.guard", "vj_npc_spawn_guard")
		
		local label = vgui.Create("DLabel", self)
		label:Dock(TOP)
		label:DockMargin(0, 5, 0, 0)
		label:SetDark(true)
		label:SetText("#menubar.npcs.weapon")

		local DComboBox = vgui.Create( "DComboBox", self )
		DComboBox:Dock( TOP )
		DComboBox:DockMargin(0, 0, 0, 0)
		DComboBox:SetConVar("gmod_npcweapon")
		DComboBox:SetSortItems(false)

		DComboBox:AddChoice("#menubar.npcs.defaultweapon", "")
		DComboBox:AddChoice("#menubar.npcs.noweapon", "none")
		DComboBox:AddSpacer()

			-- Sort the items by name, and group by category
		local groupedWeps = {}
		for _, v in pairs( list.Get( "VJBASE_SPAWNABLE_NPC_WEAPON" ) ) do
			local cat = (v.category or ""):lower()
			groupedWeps[ cat ] = groupedWeps[ cat ] or {}
			groupedWeps[ cat ][ v.class ] = language.GetPhrase( v.title )
		end

		for _, items in SortedPairs( groupedWeps ) do
			DComboBox:AddSpacer()
			for class, title in SortedPairsByValue( items ) do
				DComboBox:AddChoice( title, class )
			end
		end

		function DComboBox:OnSelect( index, value )
			self:ConVarChanged( self.Data[ index ] )
		end

		self:Open()
	end
	vgui.Register("VJ_SpawnmenuNPCSidebarToolbox", PANEL, "DDrawer")
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------ Spawn Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
local function CreateInternal_NPC(Player, Position, Normal, Class, Equipment, SpawnFlagsSaved)
	if CLIENT then return end
	print("Running VJ Base NPC duplicator internal...")
	local NPCList = list.Get("NPC") //VJBASE_SPAWNABLE_NPC
	local NPCData = NPCList[Class]
	if NPCData == nil then print("ERROR! VJ Base NPC duplicator internal failed, NPC not listed in the NPC menu!") return end
	//PrintTable(NPCData)
	//if !IsValid(NPCData) then print("VJ Base NPC Internal was unable to spawn the NPC, it didn't find any NPC Data to use") return end
	print("VJ Base NPC duplicator internal creating: " .. NPCData.Name .. " ( ".. NPCData.Class .. " ) --> ".. NPCData.Category)

	-- Don't let them spawn this entity if it isn't in our NPC Spawn list.
	/*if (!NPCData) then 
		if (!IsValid(Player)) then
			Player:SendLua("Derma_Message(\"Hey, stop trying to spawn it, your not allowed to!\")")
		end
	return end*/

	local isValidPly = IsValid(Player)

	if ( NPCData.AdminOnly && isValidPly && !Player:IsAdmin() ) then return end

	local bDropToFloor = false
	if ( NPCData.OnCeiling && Vector( 0, 0, -1 ):Dot( Normal ) < 0.95 ) then -- This NPC has to be spawned on a ceiling (Barnacle)
		return nil
	end
	if ( NPCData.OnFloor && Vector( 0, 0, 1 ):Dot( Normal ) < 0.95 ) then -- This NPC has to be spawned on a floor (Turrets)
		return nil
	else
		bDropToFloor = true
	end
	if ( NPCData.NoDrop ) then bDropToFloor = false end

	-- Offset the position
	local Offset = NPCData.Offset or 32
	Position = Position + Normal * Offset

	-- Create NPC
	local NPC = ents.Create(NPCData.Class)
	if (!IsValid(NPC)) then print("ERROR! VJ Base NPC duplicator internal failed, NPC class does not exist!") return end
	NPC:SetPos(Position)
	
	-- Rotate to face player (expected behavior)
	local Angles = Angle( 0, 0, 0 )
		if ( isValidPly ) then
			Angles = Player:GetAngles()
		end
		Angles.pitch = 0
		Angles.roll = 0
		Angles.yaw = Angles.yaw + 180
	if ( NPCData.Rotate ) then Angles = Angles + NPCData.Rotate end
	NPC:SetAngles( Angles )

	-- This NPC has a special model we want to define
	if ( NPCData.Model ) then NPC:SetModel( NPCData.Model ) end

	-- This NPC has a special texture we want to define
	if ( NPCData.Material ) then NPC:SetMaterial( NPCData.Material ) end

	-- Spawn Flags
	local SpawnFlags = bit.bor( SF_NPC_FADE_CORPSE, SF_NPC_ALWAYSTHINK )
	if ( NPCData.SpawnFlags ) then SpawnFlags = bit.bor( SpawnFlags, NPCData.SpawnFlags ) end
	if ( NPCData.TotalSpawnFlags ) then SpawnFlags = NPCData.TotalSpawnFlags end
	if ( SpawnFlagsSaved ) then SpawnFlags = SpawnFlagsSaved end
	NPC:SetKeyValue( "spawnflags", SpawnFlags )

	-- Optional Key Values
	if ( NPCData.KeyValues ) then
		for k, v in pairs( NPCData.KeyValues ) do
			NPC:SetKeyValue( k, v )
		end		
	end

	-- This NPC has a special skin we want to define
	if ( NPCData.Skin ) then NPC:SetSkin( NPCData.Skin ) end
	
	-- Body groups
	if (NPCData.BodyGroups) then
		for k, v in pairs(NPCData.BodyGroups) do
			NPC:SetBodygroup(k, v)
		end
	end

	-- Check if this is a valid entity from the list, or the user is trying to fool us.
	local valid = false
	for _, v in pairs( list.Get( "NPCUsableWeapons" ) ) do
		if v.class == Equipment then valid = true break end
	end

	if ( Equipment && Equipment != "none" && valid ) then
		NPC:SetKeyValue( "additionalequipment", Equipment )
		NPC.Equipment = Equipment 
	end

	DoPropSpawnedEffect(NPC)
	NPC:Spawn()
	NPC:Activate()
	
	if ( bDropToFloor && !NPCData.OnCeiling ) then NPC:DropToFloor() end
	print("VJ Base NPC duplicator internal successfully created the NPC!")
	return NPC
end
-------------------------------------------------------------------------------------------------------------------------
if !VJ then VJ = {} end -- If VJ isn't initialized, initialize it!
--
local vecZ1 = Vector(0, 0, 1)
--
VJ.CreateDupe_NPC = function(ply, class, equipment, spawnflags, data) -- Based on the GMod NPCs, had to recreate it here because it's not a global function
	//PrintTable(data)
	if IsValid(ply) && !gamemode.Call("PlayerSpawnNPC", ply, class, equipment) then return end -- Don't create if this player isn't allowed to spawn NPCs!

	local normal = vecZ1
	local NPCList = list.Get("NPC")
	local NPCData = NPCList[class]
	if (NPCData && NPCData.OnCeiling) then normal = Vector(0, 0, -1) end

	local ent = CreateInternal_NPC(ply, data.Pos, normal, class, equipment, spawnflags)
	if (IsValid(ent)) then
		local pos = ent:GetPos() -- Prevents the NPCs from falling through the floor
		duplicator.DoGeneric(ent, data) -- Applies generic every-day entity stuff for ent from table data (wiki)
		if (!NPCData.OnCeiling && !NPCData.NoDrop) then
			ent:SetPos(pos)
			ent:DropToFloor()
		end
		if (IsValid(ply)) then
			gamemode.Call("PlayerSpawnedNPC", ply, ent)
			ply:AddCleanup("npcs", ent)
		end
		table.Add(ent:GetTable(), data)
	end
	return ent
end