local reddotManager = class("reddotManager")
reddotManager:importPartialClass(require("ui.manager.reddot.reddotManagerGetFuncs"))
reddotManager:importPartialClass(require("ui.manager.reddot.reddotManagerDotConfs"))
local C_ReddotCtrl = CS.Lens.Gameplay.UI.LReddotCtrl
local tag = reddotManager.getName()
local reddotNode = {value = 0, parent = ""}

function reddotManager:checkStartReddotListener()
  if self._started then
    return
  end
  self._started = true
  reddotManager:initReddotConfs()
  self._reddotMap = {}
  self._dirtyList = {}
  self._go2Key = {}
  self._key2Gos = {}
  self:_initDotMap()
  self:_listenEvents()
  C_UpdateSource.AddUpdateEventHandler(handler(self, self.update), "reddotManager.update")
end

function reddotManager:_initDotMap()
  self._reddotMap = {}
  for key, conf in pairs(reddotManager.DotConf) do
    self._reddotMap[key] = self._reddotMap[key] or {value = 0}
    if conf.subNodes then
      for _, subKey in ipairs(conf.subNodes) do
        self._reddotMap[subKey] = self._reddotMap[subKey] or {value = 0}
        self._reddotMap[subKey].parent = key
      end
    end
    if conf.bindNodeId ~= nil then
      AzurWorld.RedDotMgr:BindLuaNode(conf.bindNodeId, key)
    end
  end
  AzurWorld.RedDotMgr:RegistOnStateChanged(handler(self, self.onCsharpNotify))
end

function reddotManager:_listenEvents()
  for _, conf in ipairs(reddotManager.EventConf) do
    local store = conf.store
    store.listenCallFunc(store, conf.event, function()
      self._dirtyList[conf.key] = true
    end)
  end
  for key, conf in pairs(reddotManager.DotConf) do
    if conf.unlock then
      L_SystemUnlockStore:listenCallFunc(L_SystemUnlockStore.event.onDataChange, function()
        self._dirtyList[key] = true
      end)
    end
  end
end

function reddotManager:setAllLeavesDirty()
  for key, conf in pairs(self.DotConf) do
    if conf.listFunc or conf.getFunc then
      self._dirtyList[key] = true
    end
  end
end

function reddotManager:update()
  for key, _ in pairs(self._dirtyList) do
    self:updateNode(key)
  end
  if table.count(self._dirtyList) > 0 then
    printf("\230\155\180\230\150\176\231\186\162\231\130\185", self._dirtyList)
  end
  table.clear(self._dirtyList)
end

local ListNodeDatas = {}

function reddotManager:updateNode(key)
  if not self._reddotMap[key] then
    errorf("no Reddot  " .. key)
    return
  end
  local preValue = self._reddotMap[key].value
  local conf = reddotManager.DotConf[key]
  local addedState = 0
  if conf.bindNodeId ~= nil then
    local has = AzurWorld.RedDotMgr:GetRedDotState(conf.bindNodeId)
    addedState = has and 1 or 0
  end
  if conf.subNodes or conf.bindNodeId then
    local value = addedState
    for _, subKey in ipairs(conf.subNodes or {}) do
      value = value + (self._reddotMap[subKey] and self._reddotMap[subKey].value or 0)
    end
    self._reddotMap[key].value = math.min(value, 1)
  elseif conf.getFunc then
    if not self[conf.getFunc] or conf.unlock and not L_SystemUnlockStore:getSystemIsOpen(conf.unlock) then
      self._reddotMap[key].value = 0
    else
      local getValue = function()
        return self[conf.getFunc](self)
      end
      local res, v = xpcall(getValue, self.errorFunction)
      if res then
        self._reddotMap[key].value = v
      end
    end
  elseif conf.listFunc then
    if not self[conf.listFunc] then
      self._reddotMap[key].value = 0
    else
      ListNodeDatas[key] = ListNodeDatas[key] or {}
      local lastValueMap = ListNodeDatas[key]
      local getValueMap = function()
        return not (not conf.unlock or L_SystemUnlockStore:getSystemIsOpen(conf.unlock)) and {} or self[conf.listFunc](self)
      end
      local res, valueMap = xpcall(getValueMap, self.errorFunction)
      if res then
        for id, value in pairs(lastValueMap) do
          if not valueMap[id] then
            local subKey = string.format(key, id)
            self._reddotMap[subKey] = nil
            self:invokeNodeUpdate(subKey)
          end
        end
        local totalValue = 0
        for id, value in pairs(valueMap) do
          if lastValueMap[id] ~= value then
            local key = string.format(key, id)
            self._reddotMap[key] = self._reddotMap[key] or {}
            self._reddotMap[key].value = value
            self:invokeNodeUpdate(key)
          end
          totalValue = totalValue + value
        end
        ListNodeDatas[key] = valueMap
        self._reddotMap[key].value = math.min(totalValue, 1)
      end
    end
  else
    errorf("error dotConf  " .. key)
  end
  if preValue == self._reddotMap[key].value then
    return
  end
  self:invokeNodeUpdate(key)
  if not string.isEmpty(self._reddotMap[key].parent) then
    self:updateNode(self._reddotMap[key].parent)
  end
end

function reddotManager:getRedDotState(key, id)
  if self._reddotMap == nil then
    return
  end
  local subKey = string.format(key, id)
  local reddot = self._reddotMap[subKey]
  if reddot == nil then
    return
  end
  return reddot.value >= 1
end

function reddotManager:invokeNodeUpdate(key)
  local clearList = {}
  for go, _ in pairs(self._key2Gos[key] or {}) do
    if L_CommonUtil.isValid(go) then
      self:checkChangeState(go, self._reddotMap[key] and self._reddotMap[key].value > 0 or false)
    else
      table.insert(clearList, go)
    end
  end
  for _, go in ipairs(clearList) do
    self._key2Gos[key][go] = nil
    self._go2Key[go] = nil
  end
end

function reddotManager:registerReddot(obj, key)
  if not L_CommonUtil.isValid(obj) then
    return
  end
  local go = obj.gameObject
  go:SetActive(true)
  if not go:GetComponent(typeof(C_ReddotCtrl)) then
    go:AddComponent(typeof(C_ReddotCtrl))
  end
  local oldKey = self._go2Key[go]
  if oldKey then
    self._key2Gos[oldKey][go] = nil
  end
  self._go2Key[go] = key
  self._key2Gos[key] = self._key2Gos[key] or {}
  self._key2Gos[key][go] = true
  self:checkChangeState(go, self._reddotMap[key] and self._reddotMap[key].value > 0 or false)
end

function reddotManager:checkChangeState(go, state)
  local ctrl = go:GetComponent(typeof(C_ReddotCtrl))
  ctrl:SetState(state)
end

local TempNewDatas = {}
local TempNews = setmetatable({}, {
  __index = function(t, k)
    local id = L_PlayerStore:getPlayerId()
    return TempNewDatas[id] and TempNewDatas[id][k]
  end,
  __newindex = function(t, k, v)
    local id = L_PlayerStore:getPlayerId()
    TempNewDatas[id] = TempNewDatas[id] or {}
    TempNewDatas[id][k] = v
  end
})

function reddotManager:markNew(key, id, ignore)
  local totalKey = string.format(key, id)
  local dotKey = string.format("RedDot_New_%s", totalKey)
  if not TempNews[dotKey] then
    TempNews[dotKey] = true
    L_CommonUtil.setLocalValue(dotKey, 1)
    if not ignore then
      self._dirtyList[key] = true
    end
  end
end

function reddotManager:clearNew(key, id, ignore)
  local totalKey = string.format(key, id)
  local dotKey = string.format("RedDot_New_%s", totalKey)
  if TempNews[dotKey] then
    TempNews[dotKey] = false
    L_CommonUtil.clearLocalValue(dotKey)
    if not ignore then
      self._dirtyList[key] = true
    end
  end
end

function reddotManager:haveNew(key, id)
  if not L_PlayerStore:getPlayerId() then
    return false
  end
  local totalKey = string.format(key, id)
  local dotKey = string.format("RedDot_New_%s", totalKey)
  if TempNews[dotKey] == nil then
    TempNews[dotKey] = tonumber(L_CommonUtil.getLocalValue(dotKey)) == 1
  end
  return TempNews[dotKey]
end

function reddotManager:dispose()
end

function reddotManager:onCsharpNotify(key)
  self._dirtyList[key] = true
end

function reddotManager:errorFunction(err)
  print(err)
end

return reddotManager
