local PLUGIN = PLUGIN

PLUGIN.name = "Spawns"
PLUGIN.description = "Spawn points for factions and classes."
PLUGIN.author = "Chessnut"
PLUGIN.spawns = PLUGIN.spawns or {}

local function IsSpawnPointClear(pos)
    local mins = Vector(-16, -16, 0)
    local maxs = Vector(16, 16, 72)

    for _, ply in ipairs(player.GetAll()) do
        if ply:Alive() and ply:GetPos():Distance(pos) < 50 then
            return false
        end
    end

    local ents = ents.FindInBox(pos + mins, pos + maxs)
    for _, ent in ipairs(ents) do
        if IsValid(ent) and not ent:IsPlayer() and ent:GetSolid() ~= SOLID_NONE then
            return false
        end
    end

    return true
end

function PLUGIN:PlayerLoadout(client)
	local character = client:GetCharacter()

	if (self.spawns and !table.IsEmpty(self.spawns) and character) then
		local class = character:GetClass()
		local points
		local className = "default"

		for k, v in ipairs(ix.faction.indices) do
			if (k == client:Team()) then
				points = self.spawns[v.uniqueID] or {}

				break
			end
		end

		if (points) then
			local factionTable = ix.faction.indices[client:Team()]
			local podr = character:GetPodr()

			if (podr and factionTable and factionTable.Podr and factionTable.Podr[podr]) then
				className = factionTable.Podr[podr].name:lower()
			else
				for _, v in ipairs(ix.class.list) do
					if (class == v.index) then
						className = v.uniqueID

						break
					end
				end
			end

			points = points[className] or points["default"]

			if (points and !table.IsEmpty(points)) then
				local tries = 0
				local position

				repeat
					position = table.Random(points)
					tries = tries + 1
				until IsSpawnPointClear(position) or tries > 10

				client:SetPos(position)
			end
		end
	end
end

function PLUGIN:LoadData()
	self.spawns = self:GetData() or {}
end

function PLUGIN:SaveSpawns()
	self:SetData(self.spawns)
end

ix.command.Add("SpawnAdd", {
	description = "@cmdSpawnAdd",
	privilege = "Manage Spawn Points",
	adminOnly = true,
	arguments = {
		ix.type.string,
		bit.bor(ix.type.text, ix.type.optional)
	},
	OnRun = function(self, client, name, class)
		local full = (name .. " " .. (class or "")):Trim()
		local words = string.Explode(" ", full)
		
		local info, info2, faction, className
		
		-- Перебираем возможные разбиения строки на фракцию и класс
		for i = 1, #words do
			local fName = table.concat(words, " ", 1, i)
			local cName = table.concat(words, " ", i + 1)
			
			for _, v in ipairs(ix.faction.indices) do
				-- Проверяем совпадение фракции
				if (ix.util.StringMatches(v.uniqueID, fName) or ix.util.StringMatches(L(v.name, client), fName)) then
					local potentialInfo = v
					local potentialFaction = v.uniqueID
					local potentialClassName, potentialInfo2
					
					if (cName == "") then
						potentialClassName = "default"
					else
						-- Ищем класс
						for _, c in ipairs(ix.class.list) do
							if (c.faction == v.index and (c.uniqueID:lower() == cName:lower() or ix.util.StringMatches(L(c.name, client), cName))) then
								potentialInfo2 = c
								potentialClassName = c.uniqueID
								break
							end
						end
						
						-- Ищем подразделение (Podr)
						if (!potentialClassName and v.Podr) then
							for _, p in pairs(v.Podr) do
								local pName = p.name:lower()
								local qName = cName:lower()
								
								-- Сравнение с запасом на окончания (по первым 4-5 символам) или по вхождению
								if (ix.util.StringMatches(pName, qName) or ix.util.StringMatches(qName, pName) or 
									pName:find(qName, 1, true) or qName:find(pName, 1, true) or
									(pName:sub(1, 8) == qName:sub(1, 8))) then -- 8 байт = ~4 русских символа
									potentialInfo2 = p
									potentialClassName = p.name:lower()
									break
								end
							end
						end
					end
					
					if (potentialClassName) then
						info = potentialInfo
						faction = potentialFaction
						className = potentialClassName
						info2 = potentialInfo2
						break
					end
				end
			end
			
			if (className) then break end
		end

		if (info) then
			if (!className) then
				return "@invalidClass"
			end

			PLUGIN.spawns[faction] = PLUGIN.spawns[faction] or {}
			PLUGIN.spawns[faction][className] = PLUGIN.spawns[faction][className] or {}

			table.insert(PLUGIN.spawns[faction][className], client:GetPos())
			PLUGIN:SaveSpawns()

			local logName = L(info.name, client)
			if (info2) then
				logName = logName .. " (" .. L(info2.name, client) .. ")"
			end

			return "@spawnAdded", logName
		else
			return "@invalidFaction"
		end
	end
})

ix.command.Add("SpawnRemove", {
	description = "@cmdSpawnRemove",
	privilege = "Manage Spawn Points",
	adminOnly = true,
	arguments = bit.bor(ix.type.number, ix.type.optional),
	OnRun = function(self, client, radius)
		radius = radius or 120

		local position = client:GetPos()
		local i = 0

		for _, v in pairs(PLUGIN.spawns) do
			for _, v2 in pairs(v) do
				for k3, v3 in pairs(v2) do
					if (v3:Distance(position) <= radius) then
						v2[k3] = nil
						i = i + 1
					end
				end
			end
		end

		if (i > 0) then
			PLUGIN:SaveSpawns()
		end

		return "@spawnDeleted", i
	end
})
