--[[
协议模板规则如下
	1.在module文件夹下面建立的所有文件夹都是一个一个的模块，里面至少要有一个proto.lua和handler.lua文件
	2.handler.lua里面填写该模块的cg_xxx函数
	3.proto.lua里面填写该模块的cg和gc协议
	4.对于proto.lua里面的不是以cg和gc开头的table，是作为子结构，其中，如果没有local开头的，那么子结构的名字将作为客户端对应的类的名字，如果有local开头或者是直接在table里面展开的子结构，客户端对应的类的名字将根据一层一层的字段自动生成
	5.协议的每个字段{名字，容量，类型}
		名字：只能用string，不能用number，并且不能以下划线开头（避免生成的跟客户端的私有变量冲突）
		容量：为预分配的最大空间，当容量大于1的时候，该字段就必需包含长度，例如字段名为aa，aa就是一个table，那么长度就是aa[0]，aa[0]必需小于等于容量
		类型：
			{"aa", 1, "int"}	--32位整型
			{"aa", 1, "double"}	--64位浮点数
			{"aa", 1, "string", 128} --一个字符串，里面最多有128个字节，字符串长度上限不填的话，默认是64
			{"aa", 1, sub}		--aa是一个table，里面的字段由子结构的模板sub决定

			{"aa", 2, "int"}	--数组，里面最多有2个整型
			{"aa", 2, "double"}	--数组，里面最多有2个64位浮点数
			{"aa", 2, "string", 128}	--字符串数组，每个字符串里面最多有128个字节，字符串长度上限不填的话，默认是64
			{"aa", 2, sub}		--aa是一个table，aa[1]也是一个table，aa[1]里面的字段由子结构的模板sub决定
--]]

local msg_ex = _G.msg_ex

cg = cg or {}
gc = gc or {}
proto_handler = proto_handler or {}
proto_id = proto_id or {}
proto_name = proto_name or {}
proto_template = proto_template or {}
list = list or {} -- 消息发送对象列表 第0为长度 后面1-x为obj

local Config = require("Config")

local mt = {
	__index =
	function(t, k)
		local id = t[1] or ""
		assert(nil, id .. " " .. k .. " not exist")
	end
}

local function proto_container_metatable(msg)
	if not Config.IS_WINDOWS then
		return
	end
	setmetatable(msg, mt)
	for _, v in pairs(msg) do
		if type(v) == "table" then
			proto_container_metatable(v)
		end
	end
end

local function proto_container_resize(msg, template)
	for _, v in ipairs(template) do
		if v[2] < 2 then
			if type(v[3]) == "table" then
				cnt = cnt + 1
				msg[v[1]] = {}
				proto_container_resize(msg[v[1]], v[3])
			end
		else
			if type(v[3]) == "table" then
				local v2 = v[2]
				if v2 == 2 and (v[1] == "items" or v[1] == "equips" or v[1] == "gems") then
					v2 = 1
				end
				cnt = cnt + 1
				msg[v[1]] = {}
				for i = 1, v2 do
					msg[v[1]][i] = {}
					proto_container_resize(msg[v[1]][i], v[3])
				end
			else
				cnt = cnt + 1
				msg[v[1]] = {}
			end
		end
	end
end

function register(name, id, template, handler) 
	if not proto_name[id] then
		msg_ex.register_template(id, template)
		if string.sub(name, 1, 2) == "CG" then
			cg[id] = {[1] = id}
			proto_container_resize(cg[id], template)
			proto_container_metatable(cg[id])
		elseif string.sub(name, 1, 2) == "GC" then
			gc[name] = {[1] = id}
			cnt = 0
			proto_container_resize(gc[name], template)
			if 8000 < cnt then
			end
		end
	end
	proto_handler[id] = handler
	proto_name[id] = name
	proto_template[id] = template
end

if msg_ex then

send = msg_ex.unicast 

sendMulti = msg_ex.multicast

sendWorld = msg_ex.broadcast

end

local out = {}
local out_len = 0
local function add_space(step)
	for i = 1, step do
		out_len = out_len + 1
		out[out_len] = "	"
	end
end

local set = {}
function trace(m, template, step, print_on_err)
	if not step then
		template = proto_template[m[1]]
		step = 0
		out_len = 0 
	end
	for k in pairs(set) do
		set[k] = nil
	end
	for _, v in ipairs(template) do
		set[v[1]] = true
	end
	for k in pairs(m) do
		if k ~= 1 and not set[k] then
			print(table.concat(out, nil, 1, out_len))
			assert(nil, k .. " not need")
		end
	end
	for _, v in ipairs(template) do
		add_space(step)
		out_len = out_len + 1
		out[out_len] = v[1]
		out_len = out_len + 1
		out[out_len] = "="
		if v[2] < 2 then
			if type(v[3]) == "table" then
				if type(m[v[1]]) ~= "table" then
					print(table.concat(out, nil, 1, out_len))
					assert(nil, v[1] .. " is not table")
				end
				out_len = out_len + 1
				out[out_len] = "\n"
				trace(m[v[1]], v[3], step + 1, print_on_err)
			else
				if (v[3] == "byte" or v[3] == "short" or v[3] == "int" or v[3] == "double" or v[3] == "uint") and type(m[v[1]]) ~= "number" then
					print(table.concat(out, nil, 1, out_len))
					assert(nil, v[1] .. " is not number")
				end
				if v[3] == "string" and type(m[v[1]]) ~= "string" then
					print(table.concat(out, nil, 1, out_len))
					assert(nil, v[1] .. " is not string")
				end
				out_len = out_len + 1
				out[out_len] = m[v[1]]
				out_len = out_len + 1
				out[out_len] = "\n"
			end
		else
			if type(m[v[1]]) ~= "table" then
				print(table.concat(out, nil, 1, out_len))
				assert(nil, v[1] .. " is not table")
			end
			out_len = out_len + 1
			out[out_len] = "\n"
			if type(m[v[1]][0]) ~= "number" then
				print(table.concat(out, nil, 1, out_len))
				assert(nil, v[1] .. " len is not number")
			end
			add_space(step + 1)
			out_len = out_len + 1
			out[out_len] = "0="
			out_len = out_len + 1
			out[out_len] = m[v[1]][0]
			out_len = out_len + 1
			out[out_len] = "\n"
			for i = 1, m[v[1]][0] do
				if type(v[3]) == "table" then
					if type(m[v[1]][i]) ~= "table" then
						print(table.concat(out, nil, 1, out_len))
						assert(nil, v[1] .. " " .. i .. " is not table")
					end
					add_space(step + 1)
					out_len = out_len + 1
					out[out_len] = i
					out_len = out_len + 1
					out[out_len] = "="
					out_len = out_len + 1
					out[out_len] = "\n"
					trace(m[v[1]][i], v[3], step + 2, print_on_err)
				else
					if (v[3] == "byte" or v[3] == "short" or v[3] == "int" or v[3] == "double" or v[3] == "uint") and type(m[v[1]][i]) ~= "number" then
						print(table.concat(out, nil, 1, out_len))
						assert(nil, v[1] .. " " .. i .. " is not number")
					end
					if v[3] == "string" and type(m[v[1]][i]) ~= "string" then
						print(table.concat(out, nil, 1, out_len))
						assert(nil, v[1] .. " " .. i .. " is not string")
					end
					add_space(step + 1)
					out_len = out_len + 1
					out[out_len] = i
					out_len = out_len + 1
					out[out_len] = "="
					out_len = out_len + 1
					out[out_len] = m[v[1]][i]
					out_len = out_len + 1
					out[out_len] = "\n"
				end
			end
		end
	end
	if step == 0 and not print_on_err then
		print(table.concat(out, nil, 1, out_len))
	end
end
