local u=require(script.Parent.Stream)local c=require(script.Parent.stableKeys)local N=require(script.Parent.sizeOf)local z=(require(script.Parent.var128)).decode local r local function l(u)return z(u)end local function s(u)local c=z(u)if c%2==0 then return c/2 else return(1-c)/2 end end local function L(u)local c=string.char(u.byte(4))return string.unpack("\102",c)end local function v(u)local c=string.char(u.byte(8))return string.unpack("\100",c)end local function x(u)local c=z(u)local N={}for c=1,c,1 do N[c]=string.char(u.byte())end return table.concat(N)end local function e(u)return u.byte()==1 end local function w(u,c)local N=z(c)local l={}for N=1,N,1 do l[N]=r(u.valueType,c)end return l end local function P(u,c)local N=0 for c=1,c,1 do N*=256 N+=u.byte()end return N end local function A(z,l)local s=c(z.data)local L=l if typeof(l)=="\115\116\114\105\110\103"then L=u(l)end local v=0 for u,c in ipairs(s)do local N=z.data[c]if N.default~=nil then v+=1 end end local x={}if v>0 then local u=P(L,N(2^v-1))for c=1,v,1 do local N=v-c local z=math.floor(u/2^N)%2==1 x[s[c]]=z end end local e={}for u,c in ipairs(s)do local N=z.data[c]if x[c]then e[c]=N.default else e[c]=r(N,L)end end return e end function r(u,c)if u.type=="\105\110\116"then return s(c)elseif u.type=="\117\105\110\116"then return l(c)elseif u.type=="\102\108\111\097\116"then return L(c)elseif u.type=="\100\111\117\098\108\101"then return v(c)elseif u.type=="\115\116\114\105\110\103"then return x(c)elseif u.type=="\098\111\111\108"then return e(c)elseif u.type=="\097\114\114\097\121"then return w(u,c)elseif u.type=="\109\101\115\115\097\103\101"then return A(u,c)end end return A
local Stream = require(script.Parent.Stream)
local stableKeys = require(script.Parent.stableKeys)
local sizeOf = require(script.Parent.sizeOf)
local decode128 = require(script.Parent.var128).decode

local decodeAny

local function decodeUInt(stream)
	return decode128(stream)
end

local function decodeInt(stream)
	local n = decode128(stream)
	if n % 2 == 0 then -- zigzag
		return n / 2
	else
		return (1 - n) / 2
	end
end

local function decodeFloat(stream)
	local str = string.char(stream.byte(4))
	return string.unpack("f", str)
end

local function decodeDouble(stream)
	local str = string.char(stream.byte(8))
	return string.unpack("d", str)
end

local function decodeString(stream)
	local len = decode128(stream)
	local out = {}
	for i = 1, len do
		out[i] = string.char(stream.byte())
	end
	return table.concat(out)
end

local function decodeBool(stream)
	return stream.byte() == 1
end

local function decodeArray(entry, stream)
	local len = decode128(stream)
	local out = {}
	for i = 1, len do
		out[i] = decodeAny(entry.valueType, stream)
	end
	return out
end

local function decodeFlags(stream, size)
	local n = 0
	for _ = 1, size do
		n *= 0x100
		n += stream.byte()
	end
	return n
end

local function decodeMessage(message, input)
	local keys = stableKeys(message.data)
	local stream = input
	if typeof(input) == "string" then
		stream = Stream(input)
	end

	local numOptionalKeys = 0
	for _, key in ipairs(keys) do
		local entry = message.data[key]
		if entry.default ~= nil then
			numOptionalKeys += 1
		end
	end

	local isDefault = {}
	if numOptionalKeys > 0 then
		local defaultFlags = decodeFlags(stream, sizeOf(2 ^ numOptionalKeys - 1))
		for i = 1, numOptionalKeys do
			local sh = numOptionalKeys - i
			local flag = math.floor(defaultFlags / 2 ^ sh) % 2 == 1
			isDefault[keys[i]] = flag
		end
	end

	local out = {}
	for _, key in ipairs(keys) do
		local entry = message.data[key]
		if isDefault[key] then
			out[key] = entry.default
		else
			out[key] = decodeAny(entry, stream)
		end
	end

	return out
end

function decodeAny(entry, stream)
	if entry.type == "int" then
		return decodeInt(stream)
	elseif entry.type == "uint" then
		return decodeUInt(stream)
	elseif entry.type == "float" then
		return decodeFloat(stream)
	elseif entry.type == "double" then
		return decodeDouble(stream)
	elseif entry.type == "string" then
		return decodeString(stream)
	elseif entry.type == "bool" then
		return decodeBool(stream)
	elseif entry.type == "array" then
		return decodeArray(entry, stream)
	elseif entry.type == "message" then
		return decodeMessage(entry, stream)
	end
end

return decodeMessage
