local uiBase = class("uiBase")
local tag = "uiBase"
uiBase._bindDataMetaClass = require("core.ui.base.bindDataMeta")

function uiBase:ctor()
  self.gameObject = nil
  self.modules = {}
end

function uiBase:_wrapData()
  local mt = self._bindDataMetaClass.new(self)
  mt:setupBindings()
  self.bind.mt = mt
  self.bind = setmetatable(self.bind, mt)
  return self.bind
end

function uiBase._doOpenModule(md, options)
  if md.isBind and not md.isOpen then
    md.isOpen = true
    if md.openModules then
      md:openModules(options)
    end
    if md.open then
      md:open(options)
    end
  end
end

function uiBase._doCloseModule(md, options)
  if md.isBind and md.isOpen then
    if md.closeModules then
      md:closeModules(options)
    end
    if md.close then
      md:close(options)
    end
    md.isOpen = false
  end
end

function uiBase._doShowModule(md, options)
  if md.isBind and md.isOpen and not md.isShow then
    local go = md.uiBinding and md.uiBinding.gameObject
    if go and go.activeSelf then
      md.isShow = true
      if md.showModules then
        md:showModules(options)
      end
      if md.show then
        md:show(options)
      end
    end
  end
end

function uiBase._doHideModule(md, options)
  if md.isBind and md.isShow then
    if md.hideModules then
      md:hideModules(options)
    end
    if md.hide then
      md:hide(options)
    end
    md.isShow = false
  end
end

function uiBase._doRefreshModule(md)
  if md.isBind and md.isOpen then
    if md.refreshModules then
      md:refreshModules()
    end
    if md.refresh then
      md:refresh()
    end
  end
end

function uiBase._doUnbindModule(md)
  if md.isBind then
    if md.unbindModules then
      md:unbindModules()
    end
    if md.unBind then
      md:unBind()
    end
  end
end

function uiBase:_processModules(func, ...)
  if func == nil then
    return
  end
  if not self.modules then
    return
  end
  for _, mds in pairs(self.modules) do
    if type(mds) == "table" then
      if mds.class then
        func(mds, ...)
      else
        for _, md in pairs(mds) do
          if type(md) == "table" then
            func(md, ...)
          end
        end
      end
    end
  end
end

function uiBase._doRefreshTopBar(md)
  if md.isBind and md.isOpen and md.onTopBarRefresh then
    md:onTopBarRefresh()
  end
end

function uiBase:toBind(uiBinding)
  if self.isBind then
    return
  end
  if uiBinding then
    self.uiBinding = uiBinding
  end
  if self.uiBinding == nil then
    errorf("uiBinding is nil: " .. self.pageName)
  end
  self.bindConfigs = self.uiBinding:GetBindConfigs()
  self.uiBinding:RemoveAllAction()
  self.linkerDataDict = {}
  if self.bindConfigs ~= nil then
    for i = 0, self.bindConfigs.Length - 1 do
      local linker = self.bindConfigs[i]
      self.linkerDataDict[linker.label] = linker.attributeType
    end
  end
  self.bind = self.bind or {}
  self.methods = self.methods or {}
  self:_wrapData()
  self.bindComponents = {}
  if self.uiBinding.BindComponentMap ~= nil then
    for k, v in pairs(self.uiBinding.BindComponentMap) do
      self.bindComponents[k] = v
    end
  end
  self.isBind = true
end

function uiBase:unBind()
  if not self.isBind then
    return
  end
  self:unbindModules()
  local meta = getmetatable(self.bind)
  setmetatable(self.bind, nil)
  if meta and meta._values then
    for k, v in pairs(meta._values) do
      self.bind[k] = v
    end
  end
  self.modules = {}
  table.clear(self.bindComponents)
  self.isBind = false
end

function uiBase:disposeUIBinding()
  self.uiBinding = nil
end

function uiBase:reBind(uiBinding)
  self:unBind()
  self:toBind(uiBinding)
end

function uiBase:setUIBinding(uiBinding)
  self.uiBinding = uiBinding
end

function uiBase:created()
  if self.uiBinding == nil then
    return
  end
  self:toBind(self.uiBinding)
end

function uiBase:mergeData(newData, defaultData)
  if newData == nil then
    return defaultData
  end
  if type(newData) == "table" then
    if defaultData ~= nil then
      for i, v in pairs(defaultData) do
        if newData[i] == nil then
          newData[i] = v
        end
      end
    end
  elseif type(newData) == "function" and defaultData ~= nil then
    local nd = newData()
    if type(defaultData) == "function" then
      function newData()
        return self:mergeData(nd, defaultData())
      end
    else
      function newData()
        return self:mergeData(nd, defaultData)
      end
    end
  end
  return newData
end

function uiBase:set(module, key, value, methods)
  local mt = getmetatable(module)
  if mt == nil then
    error("uiBase:set() dont has this module " .. module)
    return
  end
  if key == nil then
    error("uiBase:set() key == nil ")
    return
  end
  if methods ~= nil and type(methods) ~= "function" then
    error("set method must use function")
    return
  end
  if self.bindConfigs ~= nil then
    for i = 0, self.bindConfigs.Length - 1 do
      local linker = self.bindConfigs[i]
      if linker.label == key then
        local t = linker.attributeType
        if t == 11 then
          mt._setObjectValue(key, value, methods)
          mt._arrHandlers[key] = function(val)
            mt._values.clear(key, false)
            local oldVal = mt._values[key]
            for k, v in ipairs(val) do
              table.insert(oldVal, v)
            end
            mt.uiBinding:LinkerSetSystemObject(key, oldVal)
          end
        elseif t < 100 then
          local dataType = type(value)
          if dataType == "string" and value == "nil" then
          elseif dataType == "function" then
            mt._setValue(key, t, value())
          else
            mt._setValue(key, t, value)
          end
          mt._handlers[key] = function(val)
            mt._setValue(key, t, val)
          end
        else
          mt._setMethods(key, t, methods)
        end
      end
    end
  end
end

function uiBase:setData(module, key, value)
  local mt = getmetatable(module)
  if mt == nil then
    error("uiBase:set() dont has this module " .. module)
    return
  end
  if key == nil then
    error("uiBase:set() key == nil ")
    return
  end
  if self.bindConfigs ~= nil then
    for i = 0, self.bindConfigs.Length - 1 do
      local linker = self.bindConfigs[i]
      if linker.label == key then
        local t = linker.attributeType
        if t == 11 then
          mt._setObjectValue(key, value, nil)
          mt._arrHandlers[key] = function(val)
            mt._values.clear(key, false)
            local oldVal = mt._values[key]
            for k, v in ipairs(val) do
              table.insert(oldVal, v)
            end
            mt.uiBinding:LinkerSetSystemObject(key, oldVal)
          end
        elseif t < 100 then
          do
            local dataType = type(value)
            if dataType == "string" and value == "nil" then
            elseif dataType == "function" then
              mt._setValue(key, t, value())
            else
              mt._setValue(key, t, value)
            end
            mt._handlers[key] = function(val)
              mt._setValue(key, t, val)
            end
          end
        end
      end
    end
  end
end

function uiBase:open(options)
end

function uiBase:close(options)
end

function uiBase:show(options)
end

function uiBase:hide(options)
end

function uiBase:refresh()
end

function uiBase:onTopBarRefresh()
end

function uiBase:openModules(options)
  self:_processModules(self._doOpenModule, options)
end

function uiBase:closeModules(options)
  self:_processModules(self._doCloseModule, options)
end

function uiBase:showModules(options)
  self:_processModules(self._doShowModule, options)
end

function uiBase:hideModules(options)
  self:_processModules(self._doHideModule, options)
end

function uiBase:refreshModules()
  self:_processModules(self._doRefreshModule)
end

function uiBase:unbindModules()
  self:_processModules(self._doUnbindModule)
end

function uiBase:playAnimation(animName)
  self.uiBinding:PlayAnimation(animName)
end

function uiBase:stopAnimation(animName)
  self.uiBinding:StopAnimation(animName)
end

function uiBase:refreshTopBar()
  self:onTopBarRefresh()
  self:_processModules(self._doRefreshTopBar)
end

function uiBase:safeWrap(clauser)
  return function(...)
    if self.isBind then
      clauser(...)
    end
  end
end

return uiBase
