local group = class("core.ui.freedomType", require("core.ui.uitype.groupBase"))

function group:ctor(ui, config)
  group.super.ctor(self, ui, config)
  self._freedomPages = {}
  self._freePages = {}
  self._destroyingPages = {}
  self._freeIndex = 0
  self._freedomOutCount = config.freedomOutCount or 3
end

function group:_processDestroyingPages()
  for _, p in ipairs(self._destroyingPages) do
    self:_doDestroyPage(p)
  end
  table.clear(self._destroyingPages)
end

function group:_doDestroyPage(page)
  if page == nil then
    return
  end
  page.cls:unBind()
  page.cls:destroy()
  page.cls:disposeUIBinding()
  self._ui:execMiddleware("onDestroy", page.assetName, page.gameObject)
  if page.cls.loadHandle:IsValid() then
    self._ui:execMiddleware("releaseHandler", page.cls)
  end
end

function group:_enterFreePage(pageName, page)
  self._freeIndex = self._freeIndex + 1
  page.index = self._freeIndex
  self._freePages[pageName] = page
end

function group:_processFreePages()
  local indexes = {}
  local indexesPages = {}
  for i, v in pairs(self._freePages) do
    table.insert(indexes, v.index)
    indexesPages[v.index] = v
  end
  table.sort(indexes, function(a, b)
    return b < a
  end)
  for i = self._freedomOutCount, #indexes do
    local p = indexesPages[indexes[i]]
    if not p.gameObject.activeSelf then
      table.insert(self._destroyingPages, p)
      self._freePages[p.pageName] = nil
    end
  end
  self:_processDestroyingPages()
end

function group:_doOpen(page, options)
  self._ui:profilerMarkTimeForRecord(page.pageName, "t_doOpenStart")
  page.cls:open(options)
  self:_show(page, options)
  self._ui:playAudio(self._ui.audioEvent.pageBgm, page.config.windowBGM, true)
  self._ui:dispatch(self._ui.pageEvent.showed, page.pageName)
  if page.config.blockInput then
    self._ui:execMiddleware("blockInput", true)
  end
  self._ui:profilerMarkTimeForRecord(page.pageName, "t_doOpenEnd")
end

function group:_show(page, options)
  self._ui:playAudio(self._ui.audioEvent.pageState, page.config.windowState)
  page.cls:show(options)
end

function group:_toOpen(page, options, callback)
  self._ui:profilerMarkTimeForRecord(page.pageName, "t_preOpenStart")
  page.gameObject:SetActive(true)
  page.cls:openModules(options)
  page.cls:showModules(options)
  page.cls:preOpen(options)
  self._ui:playAudio(self._ui.audioEvent.pageOpen, page.config.windowOpenAudio)
  self._ui:dispatch(self._ui.pageEvent.preOpen, page.pageName)
  page.cls.status = "shown"
  page.status = "shown"
  self._ui:profilerMarkTimeForRecord(page.pageName, "t_preOpenEnd")
  local blendDefinition = page.cls.blendDefinition
  if blendDefinition then
    page.animating = true
    blendDefinition:OpenReset()
    self._ui:profilerMarkTimeForRecord(page.pageName, "t_openAnimStart")
    blendDefinition:PlayOpen(function()
      self._ui:profilerMarkTimeForRecord(page.pageName, "t_openAnimEnd")
      page.animating = false
      if page.status == "shown" then
        self:_doOpen(page, options)
      end
      if callback ~= nil then
        callback(true)
      end
    end)
  else
    self:_doOpen(page, options)
    if callback ~= nil then
      callback(true)
    end
  end
end

function group:_toCheck(page, options, callback)
  self._ui:checkPage(page, options, function(result)
    if result then
      self:_toOpen(page, options, callback)
    elseif callback ~= nil then
      callback(false)
    end
  end)
end

function group:open(pageName, options, callback)
  local page = self._freedomPages[pageName]
  if page == nil then
    page = self._freePages[pageName]
    if page == nil then
      local newPageFailed = false
      page = self._ui:newPage(pageName, options, function(p)
        newPageFailed = p == nil
        self._freedomPages[pageName] = p
        if p then
          self:_toOpen(p, options, callback)
        elseif callback ~= nil then
          callback(false)
        end
      end)
      if not newPageFailed then
        self._freedomPages[pageName] = page
      end
    else
      self._freePages[pageName] = nil
      self._freedomPages[pageName] = page
      self._ui:checkPage(page, options, function(result)
        if result then
          self:_toOpen(page, options, callback)
        else
          self._freePages[pageName] = page
          self._freedomPages[pageName] = nil
          if callback ~= nil then
            callback(false)
          end
        end
      end)
    end
  else
    if not page.gameObject then
      errorf("[freedomType] open page twice during very short time: " .. pageName)
      if callback ~= nil then
        callback(false)
      end
      return
    end
    self._ui:checkPage(page, options, function(result)
      if result then
        page.cls:showModules(options)
        self:_show(page, options)
        self._ui:playAudio(self._ui.audioEvent.pageBgm, page.config.windowBGM, true)
        self._ui:dispatch(self._ui.pageEvent.showed, page.pageName)
        if page.config.blockInput then
          self._ui:execMiddleware("blockInput", true)
        end
        page.gameObject:SetActive(true)
      end
      if callback ~= nil then
        callback(result)
      end
    end)
  end
end

function group:_postClose(page, callback)
  if page.status == "closed" then
    page.gameObject:SetActive(false)
  end
  if page.config.disableCache then
  else
    self:_enterFreePage(page.pageName, page)
    self:_processFreePages()
  end
  if callback ~= nil then
    callback(page.pageName)
  end
end

function group:close(pageName, options, callback)
  local page = self._freedomPages[pageName]
  if not page then
    print("modal dont opened pageName:" .. pageName)
    if callback ~= nil then
      callback(pageName)
    end
    return
  end
  if page.status == "closed" then
    print("ui is not opened:", pageName)
    if callback ~= nil then
      callback(pageName)
    end
    return
  end
  self._freedomPages[pageName] = nil
  if page.cls.status == "shown" then
    page.cls:hide()
    self._ui:dispatch(self._ui.pageEvent.hided, pageName)
    if page.config.blockInput then
      self._ui:execMiddleware("blockInput", false)
    end
  end
  page.cls:close(options)
  page.cls:hideModules(options)
  page.cls:closeModules(options)
  page.status = "closed"
  page.cls.status = "closed"
  self._ui:dispatch(self._ui.pageEvent.closed, pageName)
  self:internalOnPageClose(page)
  local blendDefinition = page.cls.blendDefinition
  if blendDefinition then
    page.animating = true
    blendDefinition:CloseReset()
    blendDefinition:PlayClose(function()
      page.animating = false
      self:_postClose(page, callback)
    end)
  else
    self:_postClose(page, callback)
  end
end

function group:show(pageName, options)
  local page = self._freedomPages[pageName]
  if page then
    page.cls:showModules(options)
    self:_show(page, options)
    self._ui:playAudio(self._ui.audioEvent.pageBgm, page.config.windowBGM, true)
    self._ui:dispatch(self._ui.pageEvent.showed, page.pageName)
    if page.config.blockInput then
      self._ui:execMiddleware("blockInput", true)
    end
    page.gameObject:SetActive(true)
    local blendDefinition = page.cls.blendDefinition
    if blendDefinition then
      blendDefinition:ShowReset()
      blendDefinition:PlayShow(nil)
    end
  else
    errorf("freedom dont opened pageName:" .. pageName)
  end
end

function group:hide(pageName, options)
  local page = self._freedomPages[pageName]
  if page then
    page.cls:hide(options)
    page.cls:hideModules(options)
    self._ui:dispatch(self._ui.pageEvent.hided, page.pageName)
    if page.config.blockInput then
      self._ui:execMiddleware("blockInput", false)
    end
    local blendDefinition = page.cls.blendDefinition
    if blendDefinition then
      blendDefinition:HideReset()
      blendDefinition:PlayHide(function()
        page.gameObject:SetActive(false)
      end)
    else
      page.gameObject:SetActive(false)
    end
  else
    errorf("freedom dont opened pageName:" .. pageName)
  end
end

function group:update()
  for i, p in pairs(self._freedomPages or table.empty) do
    if p ~= nil and p.status == "shown" then
      local pcls = p.cls
      if pcls and pcls.update then
        pcls:update()
      end
    end
  end
end

function group:info()
  local result = "orderPages:"
  for k, v in pairs(self._freedomPages) do
    result = result .. k .. ","
  end
  result = result .. "\n"
  result = result .. "freePages:"
  for k, v in pairs(self._freePages) do
    result = result .. k .. ","
  end
  return result
end

function group:pageInfo(pageName)
  local page = self._freedomPages[pageName]
  if page == nil then
    page = self._freePages[pageName]
    if page == nil then
      return nil
    else
      return table.tostring(page, 20)
    end
  else
    return table.tostring(page, 20)
  end
end

function group:internalEnableRaycaster()
end

function group:internalDisableRaycaster()
end

function group:destroy(pageName, options)
  local page = self._freePages[pageName]
  if not page then
    page = self._freedomPages[pageName]
    self._freedomPages[pageName] = nil
  else
    self._freePages[pageName] = nil
  end
  if page then
    self:_doDestroyPage(page)
  end
end

function group:getPage(pageName)
  local page = self._freedomPages[pageName]
  if page ~= nil then
    return page
  end
  return nil
end

function group:getAllPages()
  return self._freedomPages
end

function group:refreshAllPages()
  for _, page in pairs(self._freedomPages) do
    if page.cls then
      page.cls:refresh()
      page.cls:refreshModules()
    end
  end
end

function group:clearAllPages()
  for pageName, _ in pairs(self._freePages) do
    self:destroy(pageName)
  end
  for pageName, _ in pairs(self._freedomPages) do
    if pageName ~= "confirm" then
      self:destroy(pageName)
    end
  end
end

return group
