local this = class("resPool")
local _instantiate = Unity.GameObject.Instantiate

function this:ctor(count)
  self.cache = L_Struct.getLruCache()
  self.cache:setMaxCount(count)
  self.cache:setRemoveCall(L_CommonUtil.handle(self.onTrigger_garbage, self))
  self.gcCount = 1
  self.gcBase = count or 100
  self.gcMax = self.gcBase
  self.pathToInsGroup = {}
end

function this:syncLoadRes(path)
  local res = self.cache:get(path)
  local isNew = res == nil
  if isNew then
    res = {
      handle = C_LuaUtility.LoadGameObjectSync(path)
    }
    res.sourceRes = res.handle.gameObject
    self.cache:add(path, res)
  elseif res.sourceRes == nil then
    res.handle.handler:waitForCompletion()
  end
  return res, isNew
end

function this:asyncGameObject(path, callBack)
  local res = self.cache:get(path)
  if res and res.handle then
    if res.sourceRes then
      callBack(self:instantiateGameObject(path, res.sourceRes))
    else
      table.insert(res.cb, callBack)
    end
  else
    res = {
      cb = {callBack}
    }
    res.handle = C_LuaUtility.LoadGameObjectAsync(path, function(go)
      res.sourceRes = go
      for _, v in ipairs(res.cb) do
        v(self:instantiateGameObject(path, go))
      end
      res.cb = nil
    end)
    self.cache:add(path, res)
  end
  return res
end

function this:syncGameObject(path, parent)
  local res = self:syncLoadRes(path)
  return self:instantiateGameObject(path, res.sourceRes, parent)
end

function this:instantiateGameObject(path, originalRes, parent)
  local group = self:getInsGroup(path, true)
  local go = parent and _instantiate(originalRes, parent) or _instantiate(originalRes)
  table.insert(group, 1, go)
  if #group > self.gcMax then
    local _, isTrigger = self:garbageGroup(path)
    if not isTrigger then
      self.gcCount = self.gcCount + 1
      self.gcMax = self.gcCount * self.gcBase
    end
  end
  return go
end

function this:getInsGroup(path, isCreate)
  local group = self.pathToInsGroup[path]
  if group == nil and isCreate then
    group = {}
    self.pathToInsGroup[path] = group
  end
  return group
end

function this:onTrigger_garbage(path, res)
  local needGarbage = self:garbageGroup(path)
  if needGarbage then
    C_LuaUtility.DestroyObject(res.handle)
  end
  return needGarbage
end

function this:garbageGroup(path)
  local group = self:getInsGroup(path)
  local needGarbage = true
  local isTrigger = false
  if group then
    local count = #group
    for i = count, 1, -1 do
      local ins = group[i]
      if ins:IsNull() then
        table.remove(group, i)
        isTrigger = true
      end
    end
    needGarbage = #group <= 0
    if needGarbage then
      self.pathToInsGroup[path] = nil
    end
  end
  return needGarbage, isTrigger
end

function this:forceGarbage()
  for path, v in pairs(self.pathToInsGroup) do
    if self:garbageGroup(path) then
      local node = self.cache:remove(path)
      if node then
        local res = node.value
        C_LuaUtility.DestroyObject(res.handle)
      end
    end
  end
end

function this:destroyAllGo()
  for _, v in pairs(self.pathToInsGroup) do
    for _, go in pairs(v) do
      if not go:IsNull() then
        C_GameObject.Destroy(go)
      end
    end
  end
  self:dispose()
end

function this:dispose()
  self.pathToInsGroup = {}
  self.cache:dispose()
end

return this
