-- GLON: Garry's Mod Lua Object Notation
-- A extension of LON: Lua Object Notation
-- Made entirely by Deco Da Man
-- Types:
	-- 2: table
	-- 3: array
	-- 4: fasle boolean
	-- 5: true boolean
	-- 6: number (NOT COMPRESSED, it isn't worth it)
	-- 7: string
	---- non-LON types start here!
	-- 8: Vector (NOT COMPRESSED, it isn't worth it)
	-- 9: Angle (NOT COMPRESSED, it isn't worth it)
	-- 10: Entity (Can do players, vehicles, npcs, weapons and any other type of entity (-1 for null entity))
	-- 11: Player (By UserID)
	-- 12: CEffectData
	-- 13: ConVar (Not ClientConVar)
	-- 15: Color
	-- 254: The number equal to -math.huge (tostring(math.huge) == "-1.#INF")
	-- 254: The number equal to math.huge (tostring(math.huge) == "1.#INF")
	-- 255: reference (Sends the ID of the table to use (for "local t = {} t.a=t"))
local pairs = pairs
local type = type
local string = string
local math = math
local tostring = tostring
local ValidEntity = ValidEntity
local error = error
local print = print
local setmetatable = setmetatable
local Vector = Vector
local Angle = Angle
local Entity = Entity
local EffectData = EffectData
local GetConVar = GetConVar
local tonumber = tonumber
local player = player
module("glon")
local encode_types
local decode_types
local function InDataEscape(s)
	s = string.gsub(s, "([\1\2])", "\2%1")
	s = string.gsub(s, "%z", "\2\3")
	s = string.gsub(s, "\"", "\4") // escape the " character to simplify client commands
	return s
end
encode_types = {
	["nil"] = {nil, function()
		return "", nil
	end},
	table = {2, function(o, rtabs)
		for k,v in pairs(rtabs) do
			if v == o then
				return tostring(k).."\1", 255
			end
		end
		rtabs[#rtabs+1] = o
		local is_array = true
		local i = 0
		for k,v in pairs(o) do
			i = i + 1
			if k ~= i or type(k) ~= "number" or math.floor(k) ~= k then
				is_array = false
			break end
		end
		local s = ""
		for k,v in pairs(o) do
			if not is_array then
				s = s..Write(k, rtabs)
			end
			s = s..Write(v, rtabs)
		end
		return s.."\1", is_array and 3
	end},
	boolean = {4, function(o)
		return "", o and 5
	end},
	number = {6, function(o)
		o = o == 0 and "" or o
		return o == ((o == math.huge or o == -math.huge) and "") or tostring(o).."\1", (o == math.huge and 254) or (o == -math.huge and 253)
	end},
	string = {7, function(o)
		return InDataEscape(o).."\1"
	end},
	-- non-LON types start here!
	Vector = {8, function(o)
		return o.x.."\1"..o.y.."\1"..o.z.."\1"
	end},
	Angle = {9, function(o)
		return o.p.."\1"..o.y.."\1"..o.r.."\1"
	end},
	Entity = {10, function(o)
		return (ValidEntity(o) and o:EntIndex() or -1).."\1"
	end},
	Player = {11, function(o)
		return o:EntIndex().."\1"
	end},
	CEffectData = {12, function(o, rtabs)
		local t = {}
		if o:GetAngle() ~= Angle(0,0,0) then
			t.a = o:GetAngle()
		end
		if o:GetAttachment() ~= 0 then
			t.h = o:GetAttachment()
		end
		if o:GetEntity():IsValid() then
			t.e = o:GetEntity()
		end
		if o:GetMagnitude() ~= 0 then
			t.m = o:GetMagnitude()
		end
		if o:GetNormal() ~= Vector(0,0,0) then
			t.n = o:GetNormal()
		end
		if o:GetOrigin() ~= Vector(0,0,0) then
			t.o = o:GetOrigin()
		end
		if o:GetRadius() ~= 0 then
			t.r = o:GetRadius()
		end
		if o:GetScale() ~= 0 then
			t.c = o:GetScale()
		end
		if o:GetStart() ~= 0 then
			t.s = o:GetStart()
		end
		if o:GetSurfaceProp() ~= 0 then
			t.p = o:GetSurfaceProp()
		end
		return encode_types.table[2](t, rtabs)
	end},
	ConVar = {13, function(o)
		return InDataEscape(o:GetName()).."\1"
	end},
	PhysObj = {14, function(o)
		local parent, obj, id = o:GetEntity()
		for i = 1, parent:GetPhysicsObjectCount() do
			obj = parent:GetPhysicsObjectNum()
			if obj == o then
				id = i
			break end
		end
		return parent:EntIndex().."\1"..id.."\1"
	end},
	Color = {15, function(o)
		return o.r.."\1"..o.g.."\1"..o.b.."\1"..o.a.."\1"
	end},
}
function Write(data, rtabs)
	local t = encode_types[type(data)]
	if t then
		local data, id_override = t[2](data, rtabs)
		local char = id_override or t[1] or ""
		if char ~= "" then char = string.char(char) end
		return char..(data or "")
	else
		error(string.format("Tried to write unwriteable type: %s",
			type(data)))
	end
end
local CEffectDataTranslation = {
	a = "Angle",
	h = "Attachment",
	e = "Entity",
	m = "Magnitude",
	n = "Normal",
	o = "Origin",
	r = "Radius",
	c = "Scale",
	s = "Start",
	p = "SurfaceProp",
}
decode_types = {
	-- \2\6omg\1\6omgavalue\1\1
	[2	] = function(reader, rtabs) -- table
		local t, c, pos = {}, reader:Next()
		rtabs[#rtabs+1] = t
		local stage = false
		local key
		while true do
			c, pos = reader:Peek()
			if c == "\1" then
				if stage then
					error(string.format("Expected value to match key at %s! (Got EO Table)",
						pos))
				else
					reader:Next()
					return t
				end
			else
				if stage then
					t[key] = Read(reader, rtabs)
				else
					key = Read(reader, rtabs)
				end
				stage = not stage
			end
		end
	end,
	[3	] = function(reader, rtabs) -- array
		local t, i, c, pos = {}, 1, reader:Next()
		rtabs[#rtabs+1] = t
		while true do
			c, pos = reader:Peek()
			if c == "\1" then
				reader:Next()
				return t
			else
				t[i] = Read(reader, rtabs)
				i = i+1
			end
		end
	end,
	[4	] = function(reader) -- false boolean
		reader:Next()
		return false
	end,
	[5	] = function(reader) -- true boolean
		reader:Next()
		return true
	end,
	[6	] = function(reader) -- number
		local s, c, pos, e = "", reader:Next()
		while true do
			c = reader:Next()
			if not c then
				error(string.format("Expected \1 to end number at %s! (Got EOF!)",
					pos))
			elseif c == "\1" then
				break
			else
				s = s..c
			end
		end
		if s == "" then s = "0" end
		local n = tonumber(s)
		if not n then
			error(string.format("Invalid number at %s! (%q)",
				pos, s))
		end
		return n
	end,
	[7	] = function(reader) -- string
		local s, c, pos, e = "", reader:Next()
		while true do
			c = reader:Next()
			if not c then
				error(string.format("Expected unescaped \1 to end string at position %s! (Got EOF)",
					pos))
			elseif e then
				if c == "\3" then
					s = s.."\0"
				else
					s = s..c
				end
				e = false
			elseif c == "\2" then
				e = true
			elseif c == "\1" then
				s = string.gsub(s, "\4", "\"") // unescape quotes
				return s
			else
				s = s..c
			end
		end
	end,
	[8	] = function(reader) -- Vector
		local x = decode_types[6](reader)
		reader:StepBack()
		local y = decode_types[6](reader)
		reader:StepBack()
		local z = decode_types[6](reader)
		return Vector(x, y, z)
	end,
	[9	] = function(reader) -- Angle
		local p = decode_types[6](reader)
		reader:StepBack()
		local y = decode_types[6](reader)
		reader:StepBack()
		local r = decode_types[6](reader)
		return Angle(p, y, r)
	end,
	[10	] = function(reader) -- Entity
		return Entity(decode_types[6](reader))
	end,
	[11	] = function(reader) -- Player
		local num = decode_types[6](reader)
		return player.GetByID(num)
	end,
	[12 ] = function(reader, rtabs) -- CEffectData
		local t = decode_types[2](reader, rtabs)
		local d = EffectData()
		for k,v in pairs(t) do
			d["Set"..CEffectDataTranslation[k]](d, v)
		end
		return d
	end,
	[13	] = function(reader) -- ConVar
		return GetConVar(decode_types[7](reader))
	end,
	[15 ] = function(reader) -- Color
		local r = decode_types[6](reader)
		reader:StepBack()
		local g = decode_types[6](reader)
		reader:StepBack()
		local b = decode_types[6](reader)
		reader:StepBack()
		local a = decode_types[6](reader)
		return Color(r, g, b, a)
	end,
	[253] = function(reader) -- -math.huge
		reader:Next()
		return -math.huge
	end,
	[254] = function(reader) -- math.huge
		reader:Next()
		return math.huge
	end,
	[255] = function(reader, rtabs) -- Reference
		return rtabs[decode_types[6](reader) - 1]
	end,
}
function Read(reader, rtabs)
	local t, pos = reader:Peek()
	if not t then
		error(string.format("Expected type ID at %s! (Got EOF)",
			pos))
	else
		local dt = decode_types[string.byte(t)]
		if not dt then
			error(string.format("Unknown type ID, %s!",
				string.byte(t)))
		else
			return dt(reader, rtabs or {0})
		end
	end
end
local reader_meta = {}
reader_meta.__index = reader_meta
function reader_meta:Next()
	self.i = self.i+1
	self.c = string.sub(self.s, self.i, self.i)
	if self.c == "" then self.c = nil end
	self.p = string.sub(self.s, self.i+1, self.i+1)
	if self.p == "" then self.p = nil end
	return self.c, self.i
end
function reader_meta:StepBack()
	self.i = self.i-1
	self.c = string.sub(self.s, self.i, self.i)
	if self.c == "" then self.c = nil end
	self.p = string.sub(self.s, self.i+1, self.i+1)
	if self.p == "" then self.p = nil end
	return self.c, self.i
end
function reader_meta:Peek()
	return self.p, self.i+1
end
function decode(data)
	if type(data) == "nil" then
		return nil
	elseif type(data) ~= "string" then
		error(string.format("Expected string to decode! (Got type %s)",
			type(data)
		))
	elseif data:len() == 0 then
		return nil
	end
	return Read(setmetatable({
		s = data,
		i = 0,
		c = string.sub(data, 0, 0),
		p = string.sub(data, 1, 1),
	}, reader_meta), {})
end
function encode(data)
	return Write(data, {0}) -- to use the first key, to prevent it from interfereing with \1s. You can have up to 254 unique tables (including arrays)
end