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

function group:ctor(ui, config)
  group.super.ctor(self, ui, config)
  self._orderIndexes = {}
  self._curPage = nil
  self._orderPages = {}
  self._freePages = {}
  self._freeIndex = 0
  self._orderOutCount = config.orderOutCount or 1
end

function group:_enterFreePage(pageName, page)
  self._freeIndex = self._freeIndex + 1
  page.index = self._freeIndex
  self._freePages[pageName] = page
  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._orderOutCount, #indexes do
    local d = indexesPages[indexes[i]]
    d.cls:unBind()
    d.cls:destroy()
    d.cls:disposeUIBinding()
    self._ui:execMiddleware("onDestroy", d.assetName, d.gameObject)
    if d.cls.loadHandle:IsValid() then
      self._ui:execMiddleware("releaseHandler", d.cls)
    end
    self._freePages[d.pageName] = nil
  end
end

function group:_doOpen(page, options)
  self._ui:profilerMarkTimeForRecord(page.pageName, "t_doOpenStart")
  page.cls:open(options)
  page.cls:show(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:_toOpen(page, options, callback)
  self._ui:profilerMarkTimeForRecord(page.pageName, "t_preOpenStart")
  self:disableRaycaster()
  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")
  if page.cls.blendDefinition then
    page.animating = true
    page.cls.blendDefinition:OpenReset()
    self._ui:profilerMarkTimeForRecord(page.pageName, "t_openAnimStart")
    page.cls.blendDefinition:PlayOpen(function()
      self._ui:profilerMarkTimeForRecord(page.pageName, "t_openAnimEnd")
      self:enableRaycaster()
      page.animating = false
      if page.status == "shown" then
        self:_doOpen(page, options)
      end
      if callback ~= nil then
        callback(true)
      end
    end)
  else
    self:enableRaycaster()
    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:_onCheck(page, result, callback)
  if result then
    self._curPage = page
  elseif #self._orderIndexes > 0 then
    local newPage = self._orderIndexes[1]
    table.remove(self._orderIndexes, 1)
    self:open(newPage.pageName, newPage.options)
  else
    self._curPage = nil
  end
  if callback ~= nil then
    callback(result)
  end
end

function group:open(pageName, options, callback)
  if self._curPage then
    table.insert(self._orderIndexes, #self._orderIndexes + 1, {pageName = pageName, options = options})
    if callback ~= nil then
      callback(true)
    end
  else
    local page = self._orderPages[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._orderPages[pageName] = p
          if p then
            self:_toOpen(p, options, function(result)
              self._curPage = p
              if callback ~= nil then
                callback(result)
              end
            end)
          else
            if #self._orderIndexes > 0 then
              local newPage = self._orderIndexes[1]
              table.remove(self._orderIndexes, 1)
              self:open(newPage.pageName, newPage.options)
            else
              self._curPage = nil
            end
            if callback ~= nil then
              callback(false)
            end
          end
        end)
        if not newPageFailed then
          self._orderPages[pageName] = page
        end
      else
        self._freePages[pageName] = nil
        self._orderPages[pageName] = page
        self:_toCheck(page, options, function(result)
          self:_onCheck(page, result, callback)
        end)
      end
    else
      self:_toCheck(page, options, function(result)
        self:_onCheck(page, result, callback)
      end)
    end
  end
end

function group:currentPage()
  return self._orderIndexes[1]
end

function group:_doClose(page, options)
  if page.cls.status == "shown" then
    page.cls:hide()
    self._ui:dispatch(self._ui.pageEvent.hided, page.pageName)
    if page.config.blockInput then
      self._ui:execMiddleware("blockInput", false)
    end
  end
  self._orderPages[page.pageName] = nil
  self:_enterFreePage(page.pageName, page)
  self._curPage = nil
  page.cls:close(options)
  page.cls:hideModules()
  page.cls:closeModules(options)
  page.status = "closed"
  page.cls.status = "closed"
  page.gameObject:SetActive(false)
  self._ui:dispatch(self._ui.pageEvent.closed, page.pageName)
  if #self._orderIndexes > 0 then
    local newPage = self._orderIndexes[1]
    table.remove(self._orderIndexes, 1)
    self:open(newPage.pageName, newPage.options)
  end
end

function group:close(pageName, options, callback)
  local page = self._orderPages[pageName]
  if not page then
    error("modal dont opened pageName:" .. pageName)
    if callback ~= nil then
      callback(pageName)
    end
    return
  end
  if page.cls.blendDefinition then
    page.animating = true
    page.cls.blendDefinition:CloseReset()
    page.cls.blendDefinition:PlayClose(function()
      page.animating = false
      self:_doClose(page, options)
      if callback ~= nil then
        callback(pageName)
      end
    end)
  else
    self:_doClose(page, options)
    if callback ~= nil then
      callback(pageName)
    end
  end
end

function group:update()
  for i, p in pairs(self._orderPages) 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._orderPages) 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._orderPages[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()
  if self._curPage and self._curPage.status == "shown" then
    self._ui:setPageRaycasterEnabled(self._curPage, true)
  end
end

function group:internalDisableRaycaster()
  if self._curPage and self._curPage.status == "shown" then
    self._ui:setPageRaycasterEnabled(self._curPage, false)
  end
end

function group:destroy(pageName, options)
  local d = self._freePages[pageName]
  if d then
    self._freePages[pageName] = nil
  else
    d = self._orderPages[pageName]
    self._orderPages[pageName] = nil
  end
  if d then
    d.cls:unBind()
    d.cls:destroy(options)
    self._ui:execMiddleware("onDestroy", d.gameObject)
    if d.cls.loadHandle:IsValid() then
      self._ui:execMiddleware("releaseHandler", d.cls)
    end
  end
end

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

function group:getAllPages()
  return self._orderPages
end

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

function group:clearAllPages()
  for pageName, _ in pairs(self._orderPages) do
    self:destroy(pageName)
  end
  for pageName, _ in pairs(self._freePages) do
    self:destroy(pageName)
  end
end

return group
