-- 嵌套表
local skynet = require "skynet"
local class = class
local select = select
local next = next
local setmetatable = setmetatable
local assert = assert
local type = type
local getmetatable = getmetatable
local pairs = pairs
local xpcall = xpcall
local tostring = tostring
local table_insert = table.insert
--------------------------------------------------
local NestingTable = class("NestingTable")

-- 断言类型
local function isKind(obj, strKind)
    local mt
    if type(obj) == "table" then
        mt = getmetatable(obj)
    end

    while mt do
        if mt.__cname == strKind then
            return true
        end
        mt = mt.super
    end

    return false
end

local function exception(e)
    skynet.error(e)
    return e
end

local function xxpcall(f, ...)
    return xpcall(f, exception, ...)
end

--- 构造函数
-- @param deep 深度
-- @param bWeak 弱引用
function NestingTable:__init(deep, bWeak)
    self.m_deep = deep or 1
    self.m_bWeak = bWeak == true
    self.m_count = 0
    self.m_items = {}

    self:clear()
end

--- 新增对象
-- @param item 对象实例
-- @param k 键值
-- @param ... 多个键值
function NestingTable:add(item, k, ...)
    if not item or not k or select("#", k, ...) ~= self.m_deep then
        return
    end

    if ... then
        local _item = self:get(k)
        if not _item then
            local deep = self.m_deep - 1
            _item = NestingTable.new(deep, self.m_bWeak)
            self.m_items[k] = _item
        end
        _item:add(item, ...)
    else
        self.m_items[k] = item
    end
end

--- 删除对象
-- @param k 键值
-- @param ... 多个键值
function NestingTable:remove(k, ...)
    if not k or select("#", k, ...) ~= self.m_deep then
        return
    end

    local _item = self:get(k)
    if not ... then
        self.m_items[k] = nil
    elseif _item then
        _item:remove(...)
        if not next(_item:getItems()) then
            self.m_items[k] = nil
        end
    end
end

--- 取对象
-- @param k 键值
-- @param ... 多个键值
-- @return 被管理对象实例
function NestingTable:get(k, ...)
    if not k then
        -- Log.w("NestingTable get k nil!")
        return
    end

    local item = self.m_items[k]
    if not ... then
        return item
    end

    if item then
        return item:get(...)
    end
end

--- 取所有对象
function NestingTable:getItems()
    return self.m_items
end

function NestingTable:clear()
    if self.m_deep <= 1 then
        self.m_items = self.m_bWeak and setmetatable({}, {
            __mode = "v",
        }) or {}
    else
        self.m_items = {}
    end
end

function NestingTable:traverse(closure)
    assert(closure)

    for _, item in pairs(self.m_items) do
        if isKind(item, "NestingTable") then
            item:traverse(closure)
        else
            xxpcall(closure, item)
        end
    end
end

function NestingTable:pack(packName, ...)
    local ret = {}

    for key, item in pairs(self.m_items) do
        if isKind(item, "NestingTable") then
            ret[tostring(key)] = item:pack(packName, ...)
        elseif packName then
            ret[tostring(key)] = item[packName](item, ...)
        else
            ret[tostring(key)] = item
        end
    end

    return ret
end

function NestingTable:packKey(deep)
    assert(deep, "packKey")

    local ret = {}

    for key, item in pairs(self.m_items) do
        if deep <= self.m_deep then
            ret[tostring(key)] = item:packKey(deep)
        else
            table_insert(ret, key)
        end
    end

    return ret
end

return NestingTable
