--[[
| 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/
--]]

local bit = bit
local IsValid = IsValid
local pairs = pairs
local table = table
local L = L
local netstream = netstream
local Entity = Entity
local print = print
local math = math
local tostring = tostring
local CAMI = CAMI
local Vector = Vector
local game = game
local RunConsoleCommand = RunConsoleCommand
local net = net
local player = player
local ipairs = ipairs
local ents = ents
local string = string
local timer = timer
local ix = ix

local PLUGIN = PLUGIN

--Links
ix.command.Add("Discord", {
	description = "Obtenir un lien vers le serveur Discord",
	privilege = "Basic Commands",
	OnRun = function(self, client)
		net.Start("ixOpenURL")
			net.WriteString(ix.config.Get("DiscordLink"))
		net.Send(client)
	end,
	bNoIndicator = true
})
ix.command.Add("Content", {
	description = "Obtenir un lien vers le Workshop",
	privilege = "Basic Commands",
	OnRun = function(self, client)
		net.Start("ixOpenURL")
			net.WriteString(ix.config.Get("ContentLink"))
		net.Send(client)
	end
})
ix.command.Add("Forum", {
	description = "Obtenir un lien vers les forums",
	privilege = "Basic Commands",
	OnRun = function(self, client)
		net.Start("ixOpenURL")
			net.WriteString(ix.config.Get("ForumLink"))
		net.Send(client)
	end,
	bNoIndicator = true
})

--Basic Admin
ix.command.Add("PGI", {
	description = "Obtenez les informations de base de quelqu'un et copiez son SteamID.",
	arguments = {
		bit.bor(ix.type.player, ix.type.optional)
	},
	--alias = "PlyGetInfo",
	privilege = "Commandes d'administration de base",
	OnRun = function(self, client, target)
		if (!target) then
			target = client:GetEyeTraceNoCursor().Entity
		end

		if (!IsValid(target) or !target:IsPlayer()) then
			client:NotifyLocalized("bastionPGIInvalidTarget")
			return
		end

		net.Start("ixPlayerInfo")
			net.WriteEntity(target)
		net.Send(client)
	end,
	bNoIndicator = true
})

ix.command.Add("PrintStaffList", {
	alias = "PSL",
	description = "Imprimez une liste des membres du personnel en ligne.",
	OnRun = function(self, client)
		local staff = {}
		local bFound = false
		for _, v in ipairs(player.GetAll()) do
			local incognitoSetting = ix.option.Get(v, "iconIncognitoMode", false)
			local iconIncognitoMode = !client:IsAdmin() and !client:IsSuperAdmin() and incognitoSetting
			if (v:IsAdmin() or v:IsSuperAdmin()) and !iconIncognitoMode then
				local userGroup = v:GetUserGroup()
				staff[userGroup] = staff[userGroup] or {}
				staff[userGroup][#staff[userGroup] + 1] = v
				bFound = true
			end
		end

		local toSend = {}
		for k, v in pairs(staff) do
			toSend[#toSend + 1] = {name = k, players = v}
		end
		table.SortByMember(toSend, "name", true)

		if (bFound) then
			net.Start("ixStaffList")
				net.WriteUInt(#toSend, 8)
				for _, v in ipairs(toSend) do
					net.WriteString(v.name)
					net.WriteUInt(#v.players, 8)
					for i = 1, #v.players do
						net.WriteEntity(v.players[i])
					end
				end
			net.Send(client)
		else
			client:Notify("Il n'y a pas de STAFF en ligne actuellement.")
		end
	end,
	bNoIndicator = true
})

ix.command.Add("PrintFactionList", {
	alias = "PFL",
	description = "Imprimer une liste des membres d'une faction actuellement en ligne (y compris sur un autre personnage).",
	arguments = {
		ix.type.string
	},
	privilege = "Basic Admin Commands",
	OnRun = function(self, client, name)
		if (name == "") then
			return "@invalidArg", 2
		end

		local faction = ix.faction.teams[name]

		if (!faction) then
			for _, v in ipairs(ix.faction.indices) do
				if (ix.util.StringMatches(L(v.name, client), name) or ix.util.StringMatches(v.uniqueID, name)) then
					faction = v

					break
				end
			end
		end

		if (faction) then
			local players = {}
			for _, v in ipairs(player.GetAll()) do
				if (v:HasWhitelist(faction.index)) then
					players[#players + 1] = v
				end
			end
			net.Start("ixStaffList")
				net.WriteUInt(1, 8)
				net.WriteString(faction.name)
				net.WriteUInt(#players, 8)
				for i = 1, #players do
					net.WriteEntity(players[i])
				end
			net.Send(client)
		else
			return "@invalidFaction"
		end
	end,
	bNoIndicator = true
})

ix.command.Add("PlaySound", {
	description = "Jouez un son pour tous les joueurs (lorsqu'aucune portée n'est donnée) ou ceux qui sont proches de vous.",
	arguments = {
		ix.type.string,
		bit.bor(ix.type.number, ix.type.optional)
	},
	privilege = "Basic Admin Commands",
	OnRun = function(self, client, sound, range)
		local targets = range and {} or player.GetAll()
		if (range) then
			range = range * range
			local clientPos = client:EyePos()
			for _, target in ipairs(player.GetAll()) do
				if (target:EyePos():DistToSqr(clientPos) < range) then
					targets[#targets + 1] = target
				end
			end
		end

		net.Start("ixPlaySound")
			net.WriteString(PLUGIN.soundAlias[sound] or sound)
		net.Send(targets)
	end,
	indicator = "chatPerforming"
})

ix.command.Add("ScreenShake", {
	description = "Shakes the screen of everyone in the specified range. Specify the amplitude, the frequency and the radius for it to work.",
	arguments = {
		ix.type.number,
		ix.type.number,
		ix.type.number,
		ix.type.number
	},
	argumentNames = {"time(seconds)", "amplitude", "frequency", "radius"},
	privilege = "Basic Admin Commands",
	OnRun = function(self, client, seconds, strength, frequency, radius)
		local vector = client:GetPos() 
		util.ScreenShake(vector, strength or 5, frequency or 5, seconds, radius)
	end,
	indicator = "chatPerforming"
})

ix.command.Add("PlaySoundGlobal", {
	description = "Jouez un son pour tous les joueurs.",
	arguments = {
		ix.type.string,
	},
	privilege = "Basic Admin Commands",
	OnRun = function(self, client, sound)
		net.Start("ixPlaySound")
			net.WriteString(PLUGIN.soundAlias[sound] or sound)
			net.WriteBool(true)
		net.Send(player.GetAll())
	end,
	indicator = "chatPerforming"
})

ix.command.Add("ShowEdicts", {
	description = "Renvoie le nombre d'entités en réseau actuellement sur le serveur.",
	privilege = "Basic Admin Commands",
	OnRun = function(self, client)
		local edictsCount = ents.GetEdictCount()
		local edictsLeft = 8192 - edictsCount

		return string.format("Il y a actuellement %s édits sur le serveur. Vous pouvez avoir jusqu'à %s de plus.", edictsCount, edictsLeft)
	end,
	bNoIndicator = true
})

ix.command.Add("ShowEntsInRadius", {
	description = "Affiche une liste d'entités dans un rayon donné.",
	privilege = "Basic Admin Commands",
	arguments = {ix.type.number},
	OnRun = function(self, client, radius)
		local entities = {}
		local pos = client:GetPos()
		for _, v in pairs(ents.FindInSphere(pos, radius)) do
			if (!IsValid(v)) then continue end
			entities[#entities + 1] = table.concat({v:EntIndex(), v:GetClass(), v:GetModel() or "no model", v:GetPos():Distance(pos), v:MapCreationID()}, ", ")
		end
		netstream.Start(client, "ixShowEntsInRadius", table.concat(entities,"\n"))
		client:NotifyLocalized("entsPrintedInConsole")
	end,
	bNoIndicator = true,
})

ix.command.Add("RemoveEntityByID", {
	description = "Affiche une liste d'entités dans un rayon donné.",
	superAdminOnly = true,
	arguments = {ix.type.number},
	OnRun = function(self, client, number)
		local entity = Entity(number)
		if (IsValid(entity)) then
			client:NotifyLocalized("entityRemoved", number, entity:GetClass())
			entity:Remove()
		else
			client:NotifyLocalized("entityNotFound", number)
		end
	end,
	bNoIndicator = true,
})

if (CLIENT) then
	netstream.Hook("ixShowEntsInRadius", function(text)
		print(text)
	end)
end

ix.command.Add("LocalEvent", {
	description = "@cmdLocalEvent",
	privilege = "Event",
	arguments = {
		ix.type.string,
		bit.bor(ix.type.number, ix.type.optional)
	},
	OnRun = function(self, client, event, range)
		local _range = range or (ix.config.Get("chatRange", 280) * 2)

		ix.chat.classes.localevent.range = _range * _range

		local doubleCommand = false

		-- This could probably be done a bit smarter but I'm sure it'll do.
		if (string.Left(string.lower(event), 11) == "/localevent") then
			doubleCommand = true

			event = string.Right(event, #event - 12)
		elseif (string.Left(string.lower(event), 10) == "localevent") then
			doubleCommand = true

			event = string.Right(event, #event - 11)
		end

		if (doubleCommand) then
			client:NotifyLocalized("textDoubleCommand", "/LocalEvent")
		end

		ix.chat.Send(client, "localevent", event)
	end,
	indicator = "chatPerforming"
})

ix.command.Add("RemovePersistedProps", {
	description = "@cmdRemovePersistedProps",
	superAdminOnly = true,
	arguments = {
		ix.type.number
	},
	OnRun = function(self, client, radius)
		if (radius < 0) then
			client:Notify("Radius must be a positive number!")

			return
		end

		for _, entity in ipairs(ents.FindInSphere(client:GetPos(), radius)) do
			if (!entity:GetNetVar("Persistent")) then continue end
			
			entity:Remove()
		end

		client:Notify("Removed all persisted props in a radius of " .. radius .. " units.")
	end
})

ix.command.Add("Announce", {
	description = "@cmdAnnounce",
	arguments = {
		ix.type.text
	},
	OnRun = function(self, client, event)
		ix.chat.Send(client, "announcement", event)
	end,
	OnCheckAccess = function(self, client)
		return !client or CAMI.PlayerHasAccess(client, "Helix - Basic Admin Commands")
	end,
	indicator = "chatTyping"
})


-- Help
ix.command.Add("Help", {
	description = "@cmdStaffHelp",
	arguments = ix.type.text,
	alias = {"GMHelp", "ModHelp"},
	OnRun = function(self, client, text)
		client:Say("@ " .. text)
	end,
	OnCheckAccess = function(self, client)
		return !CAMI.PlayerHasAccess(client, "Helix - Hear Staff Chat")
	end,
	indicator = "chatTyping"
})

-- Admin Chat
ix.command.Add("Staff", {
	description = "Discutez avec d'autres membres du STAFF.",
	arguments = ix.type.text,
	privilege = "Hear Staff Chat",
	alias = {"Op", "Mod", "ModChat"},
	OnRun = function(self, client, text)
		ix.chat.Send(client, "staff_chat", text)
	end,
	indicator = "chatTyping"
})

-- Gamemaster Chat
ix.command.Add("GameMaster", {
	description = "Discutez avec d'autres Gamemasters.",
	arguments = ix.type.text,
	privilege = "Hear GM Chat",
	alias = {"GMChat", "GM"},
	OnRun = function(self, client, text)
		ix.chat.Send(client, "gm_chat", text)
	end,
	indicator = "chatTyping"
})

-- Mentor Chat
ix.command.Add("Mentor", {
	description = "Discutez avec d'autres mentors.",
	arguments = ix.type.text,
	privilege = "Hear Mentor Chat",
	alias = {"MChat", "M"},
	OnRun = function(self, client, text)
		ix.chat.Send(client, "mentor_chat", text)
	end,
	indicator = "chatTyping"
})

-- Fun Stuff
ix.command.Add("Achievement", {
	description = "Quelqu'un a gagné une réalisation spéciale !",
	arguments = {
		ix.type.player,
		ix.type.text
	},
	privilege = "Fun Stuff",
	OnRun = function(self, client, target, text)
		ix.chat.Send(client, "achievement_get", text, false, nil,
		{target, "ambient/water/drip" .. math.random( 1, 4 ) .. ".wav"})
	end,
	indicator = "chatTyping"
})
ix.command.Add("DarwinAward", {
	description = "Quelqu'un a mérité un exploit : il a fait le sacrifice ultime pour augmenter le QI moyen de l'humanité.",
	arguments = {
		ix.type.player
	},
	privilege = "Fun Stuff",
	OnRun = function(self, client, target)
		if (!target:Alive()) then
			local pos = target:GetPos()
			target:Spawn()

			target:SetPos(pos)
		end
		target:SetMoveType(MOVETYPE_WALK)
		target:SetVelocity(Vector(0, 0, 4000))

		timer.Simple(1, function() PLUGIN:Explode(target) end)
		ix.chat.Send(client, "achievement_get", "DARWIN AWARD", false, nil,
		{target, "ambient/alarms/razortrain_horn1.wav"})
	end,
	indicator = "chatTyping"
})
ix.command.Add("PlyRocket", {
	description = "To infinity, and beyond!.",
	arguments = {
		ix.type.player
	},
	privilege = "Fun Stuff",
	OnRun = function(self, client, target)
		if (!target:Alive()) then
			local pos = target:GetPos()
			target:Spawn()

			target:SetPos(pos)
		end
		target:SetMoveType(MOVETYPE_WALK)
		target:SetVelocity(Vector(0, 0, 4000))

		timer.Simple(1, function() PLUGIN:Explode(target) end)
	end,
	bNoIndicator = true
})

ix.command.Add("SetTimeScale", {
	description = "@cmdTimeScale",
	arguments = {
		bit.bor(ix.type.number, ix.type.optional)
	},
	privilege = "Fun Stuff",
	OnRun = function(self, client, number)
		local scale = math.Clamp(number or 1, 0.001, 5)
		game.SetTimeScale(scale)

		for _, v in ipairs(player.GetAll()) do
			if (self:OnCheckAccess(v)) then
				v:NotifyLocalized("bastionTimeScale", client:Name(), scale)
			end
		end
	end,
	bNoIndicator = true
})

ix.command.Add("SetGravity", {
	description = "@cmdGravity",
	arguments = {
		bit.bor(ix.type.number, ix.type.optional)
	},
	privilege = "Fun Stuff",
	OnRun = function(self, client, number)
		RunConsoleCommand("sv_gravity", number)

		for _, v in ipairs(player.GetAll()) do
			if (self:OnCheckAccess(v)) then
				v:NotifyLocalized("bastionGravity", client:Name(), number)
			end
		end
	end,
	bNoIndicator = true
})

-- lookup commands
ix.command.Add("LookupSteamID", {
	description = "Lookup a SteamID in the Bastion user database",
	arguments = {
		ix.type.text
	},
	privilege = "Bastion Lookup",
	OnRun = function(self, client, target)
		if (string.find(target, "^STEAM_%d+:%d+:%d+$")) then
			PLUGIN:LookupSteamID(client, target)
			return
		elseif (string.len(target) == 17 and string.find(target, "^%d+$")) then
			PLUGIN:LookupSteamID(client, target)
			return
		end

		target = ix.util.FindPlayer(target, false)
		client:NotifyLocalized("bastionTargetSelected", target:Name())

		PLUGIN:LookupSteamID(client, target:SteamID64())
	end,
	bNoIndicator = true
})

ix.command.Add("LookupIPUsers", {
	description = "Lookup a SteamID in the Bastion IP database",
	arguments = {
		ix.type.text
	},
	privilege = "Bastion Lookup",
	OnRun = function(self, client, target)
		if (string.find(target, "^STEAM_%d+:%d+:%d+$")) then
			PLUGIN:LookupIPUsers(client, target)
			return
		elseif (string.len(target) == 17 and string.find(target, "^%d+$")) then
			PLUGIN:LookupIPUsers(client, target)
			return
		end

		target = ix.util.FindPlayer(target, false)
		client:NotifyLocalized("bastionTargetSelected", target:Name())

		PLUGIN:LookupIPUsers(client, target:SteamID64())
	end,
	bNoIndicator = true
})

ix.command.Add("ProxyWhitelist", {
	description = "Whitelist a player as trusted to disable future proxy checks.",
	arguments = {
		ix.type.player
	},
	privilege = "Bastion Whitelist",
	OnRun = function(self, client, target)
		if (PLUGIN:WhitelistPlayer(target)) then
			client:NotifyLocalized("whitelistDone")
		else
			client:NotifyLocalized("whitelistError")
		end
	end,
	indicator = "chatTyping"
})

ix.command.Add("PlyGetCharacters", {
	description = "Get a list of a player's characters.",
	arguments = {
		ix.type.player
	},
	adminOnly = true,
	OnRun = function(self, client, target)
		client:ChatNotify(target:SteamName() .. "'s characters:")
		client:ChatNotify("====================")

		for _, character in pairs(target.ixCharList) do
			client:ChatNotify(ix.char.loaded[character].vars.name)
		end
	end
})


ix.command.Add("LTARP", {
	description = "Automatically ban a player for leaving to avoid RP after a grace period.",
	arguments = {
		ix.type.string,
		ix.type.number,
		bit.bor(ix.type.text, ix.type.optional),
	},
	adminOnly = true,
	OnRun = function(self, client, steamID, days, reason)
		local target = player.GetBySteamID64(steamID) or player.GetBySteamID(steamID)
		if (target) then
			client:Notify(target:SteamName().." is still on the server as '"..target:Name().."!")
			return
		end

		if (days > 100) then
			client:Notify("Invalid duration entered. Max ban length is 100 days.")
			return
		end

		if (string.find(string.upper(steamID), "^STEAM")) then
			steamID = util.SteamIDTo64(string.upper(steamID))
		end

		if (!PLUGIN.disconnects[steamID]) then
			client:Notify("Could not find a disconnect for "..steamID..", manually ban them if needed.")
			return
		end

		local info = PLUGIN.disconnects[steamID]
		if (info.timer) then
			client:Notify(steamID.." is already tagged for LTARP by "..info.bannedByName..".")
			return
		end

		local reconnectTime, maxBanDelay = 30, 40
		if (info.time + reconnectTime * 60 < os.time()) then
			if (info.time + maxBanDelay * 60 < os.time()) then
				client:Notify(steamID.." disconnected more than "..maxBanDelay.." minutes ago already. Manually ban them if needed.")
				return
			end

			RunConsoleCommand("sam", "banid", steamID, tostring(days * 60 * 24), reason != "" and reason or "Leaving to avoid RP. Appeal on willard.network ~"..client:SteamName())
			return
		end

		local uniqueID = "ixLTARP"..steamID
		local bannedBy = client and client:SteamName() or "console"
		timer.Create(uniqueID, info.time - os.time() + reconnectTime * 60, 1, function()
			if (IsValid(client)) then
				client:Notify(steamID.." did not reconnect and has been banned.")
			end
			RunConsoleCommand("sam", "banid", steamID, tostring(days * 60 * 24), reason != "" and reason or "Leaving to avoid RP. Appeal on willard.network ~"..bannedBy)
		end)

		info.timer = uniqueID
		info.bannedBy = client
		info.bannedByName = client and client:SteamName() or "console"
		client:Notify("Timer set! "..steamID.." will be banned in "..math.ceil((info.time - os.time() + reconnectTime * 60) / 60).." minutes.")
	end
})
