local this = class("moduleMainMiniMap", G_UIModuleBase)
local _typeImg = typeof(C_LImage)
local _areaTpl = L_GameTpl:getWorldAreaTpl()
local _mapNodeTpl = L_GameTpl:getMapNodeTpl()
local _taskTpl = L_GameTpl:getTaskTpl()
local HEIGHT_RANGE = L_GameConstTpl:getValue("MAP_MARK_HEIGHT_DIFFERENCE")
local TIME_LOOP_BREAKPOINT = string.split(L_GameConstTpl:getValue("TIME_LOOP_BREAKPOINT"), ",")
local halfDayHour_1 = tonumber(TIME_LOOP_BREAKPOINT[1])
local halfDayHour_2 = tonumber(TIME_LOOP_BREAKPOINT[2])
local MINIMAP_AREAMASK_MATERIAL = "UI/MapMask/Sdf/mat_minimap_mask"
local TIME_OF_DAY = 2400
local filterNode = {
  [L_MapConst.mapSievingType.task] = 1,
  [L_MapConst.mapSievingType.specialPet] = 1,
  [L_MapConst.mapSievingType.homeTrace] = 1,
  [L_MapConst.mapSievingType.mythicalPet] = 1
}

function this.bind()
  return {
    imgMap = "",
    mapPos = Unity.Vector2(0, 0),
    txt_pos = "",
    img_up = false,
    img_down = false,
    go_normalMap = false,
    time_root = true,
    module_meeting = {
      moduleName = "pages/main/moduleMeeting"
    }
  }
end

function this.methods()
  return {
    onClick_BigMap = function()
      L_UI:open("pageBigMap")
    end,
    onClick_timeLoop = function()
      if not L_SystemBreakManager:checkBanSystemAndTip(L_SystemConst.enum.timeChange) and L_SystemUnlockManager:checkAndTip(L_SystemConst.enum.timeChange) then
        L_UI:open("pageTimeLoop")
      end
    end,
    onClick_home = function(self)
      L_HomeUtil.switchInOutHome()
    end,
    onClick_homeScience = function(self)
      L_SystemBreakManager:jumpTo(L_SystemConst.enum.homeScience)
    end
  }
end

function this:open()
  self.mapNodeList = {}
  self.mapSpecialPetList = {}
  self.mapHomeTraceList = {}
  self.mapCollectionTraceList = {}
  self.areaBlockDic = {}
  self.collectionIdNodeMap = {}
  self.updateHandle = handler(self, self.update)
  C_UpdateSource.AddUpdateEventHandler(self.updateHandle, "moduleMainMiniMap.update")
  L_SceneStore:listenCallFunc(L_SceneStore.event.refreshPoint, self.refreshNodeListPos, self)
  L_MapStore:listenCallFunc(L_MapStore.event.setMapMark, self.initSelfMarkNode, self)
  L_MapStore:listenCallFunc(L_MapStore.event.refreshCollectionTrace, self.refreshCollectionTrace, self)
  L_MapStore:listenCallFunc(L_MapStore.event.refreshCollectionTraceItem, self.refreshCollectionTraceItem, self)
  L_AreaStore:listenCallFunc(L_AreaStore.event.mapAreaChanged, self.onEvent_mapAreaChanged, self)
  L_SystemUnlockStore:listenCallFunc(L_SystemUnlockStore.event.updateSystem, self.refreshSystem, self)
  L_TaskStore:listenCallFunc(L_TaskStore.event.refreshTask, self.refreshTask, self)
  L_TaskStore:listenCallFunc(L_TaskStore.event.setCurrentTracedTask, self.onEvent_setCurrentTracedTask, self)
  L_HomeStore:listenCallFunc(L_HomeStore.event.syncHomeContentUnlock, self.onEvent_refreshHomeAreaNode, self)
  L_SceneStore:listenCallFunc(L_SceneStore.event.refreshPoint, self.onEvent_refreshTransferNode, self)
  L_MapStore:listenCallFunc(L_MapStore.event.refreshSpecialPetNode, self.onEvent_refreshSpecialPetNode, self)
  L_MapStore:listenCallFunc(L_MapStore.event.refreshNodeTrace, self.onEvent_refreshHomeTraceNode, self)
  L_MapStore:listenCallFunc(L_MapStore.event.refreshMythicalPet, self.onEvent_refreshMythicalPetNode, self)
  self.onEvent_TaskRefreshFunc = handler(self, self.onEvent_TaskRefresh)
  AzurWorld.TaskMgr:RegisterEvent(C_ETaskEvent.TaskStartDataRefresh, self.onEvent_TaskRefreshFunc)
  self.onEvent_DayTimeRefreshFunc = handler(self, self.refreshDayTimeArrow)
  C_WorldEvent.instance:Listen(C_EWorldEvent.OnTimeHourChange, self.onEvent_DayTimeRefreshFunc)
  self.pool = L_ResPool.new()
  self.taskId = nil
  self.nodeRotVector = {
    x = 0,
    y = 0,
    z = 0
  }
  self.taskNodeOutMap = false
  self.timerResume = false
  local mapMaskSize, _ = self.bindComponents.mapMask:GetUISize()
  self.halfMapMaskSize = mapMaskSize / 2
  self.mainCamerTrans = C_CameraManager.mainCamera.transform
  self:initSystem()
  self:bindPcKey()
  self:onEvent_sceneEndHandle()
  self.curTime = 0
end

function this:initSystem()
  self.systemToTrans = {
    [L_SystemConst.enum.timeChange] = {
      self.bindComponents.transTime
    },
    [L_SystemConst.enum.adventure] = {
      self.bindComponents.minimapGo
    },
    [L_SystemConst.enum.home] = {
      self.bindComponents.transBtnHome
    },
    [L_SystemConst.enum.homeScience] = {
      self.bindComponents.rTrans_homeScience
    }
  }
  for systemId, v in pairs(self.systemToTrans) do
    v.locker = L_SystemUnlockManager:tryGenerateLocker(systemId)
    if v.locker then
      self:refreshSystem(systemId, false)
      v.locker:setCallback(self.refreshSystem, self, self.gameObject)
    end
  end
end

function this:bindPcKey()
  L_ShortCutManager:addShortKeyFlag(self, "H", L_Const.AnchorType.Bottom, self.bindComponents.transBtnHome, L_Vector3.new(0, 9, 0), 0.8)
end

function this:refreshSystem(systemId, isUnlock)
  self:refreshNodeState()
  local node = self.systemToTrans[systemId]
  if not node then
    return
  end
  local locker = node.locker
  for _, v in ipairs(node) do
    local isNeedShow = locker:tryShowOrHide(v, isUnlock)
    if isNeedShow then
      local lock = v:Find("lock")
      local unlock = v:Find("unlock")
      if lock then
        lock.gameObject:ActiveTrans(not isUnlock)
      end
      if unlock then
        unlock.gameObject:ActiveTrans(isUnlock)
      end
    end
  end
  if isUnlock ~= false and systemId == L_SystemConst.enum.adventure then
    self:onEvent_sceneEndHandle()
  end
end

function this:destroyAllLocker()
  for _, v in pairs(self.systemToTrans) do
    local locker = v.locker
    if locker then
      L_SystemUnlockManager:removeLocker(locker)
    end
  end
end

function this:close()
  if self.arrowTimer then
    Timer.remove(self.arrowTimer)
    self.arrowTimer = nil
  end
  C_UpdateSource.RemoveUpdateEventHandler(self.updateHandle)
  L_SceneStore:unListenCallFunc(L_SceneStore.event.refreshPoint, self.refreshNodeListPos)
  L_MapStore:unListenCallFunc(L_MapStore.event.setMapMark, self.initSelfMarkNode)
  L_MapStore:unListenCallFunc(L_MapStore.event.refreshCollectionTrace, self.refreshCollectionTrace)
  L_MapStore:unListenCallFunc(L_MapStore.event.refreshCollectionTraceItem, self.refreshCollectionTraceItem)
  L_AreaStore:unListenCallFunc(L_AreaStore.event.mapAreaChanged, self.onEvent_mapAreaChanged)
  L_SystemUnlockStore:unListenCallFunc(L_SystemUnlockStore.event.updateSystem, self.refreshSystem)
  L_TaskStore:unListenCallFunc(L_TaskStore.event.setCurrentTracedTask, self.onEvent_setCurrentTracedTask)
  L_TaskStore:unListenCallFunc(L_TaskStore.event.refreshTask, self.refreshTask)
  L_HomeStore:unListenCallFunc(L_HomeStore.event.syncHomeContentUnlock, self.onEvent_refreshHomeAreaNode)
  L_SceneStore:unListenCallFunc(L_SceneStore.event.refreshPoint, self.onEvent_refreshTransferNode)
  L_MapStore:unListenCallFunc(L_MapStore.event.refreshSpecialPetNode, self.onEvent_refreshSpecialPetNode, self)
  L_MapStore:unListenCallFunc(L_MapStore.event.refreshNodeTrace, self.onEvent_refreshHomeTraceNode, self)
  L_MapStore:unListenCallFunc(L_MapStore.event.refreshMythicalPet, self.onEvent_refreshMythicalPetNode, self)
  if self.onEvent_TaskRefreshFunc then
    AzurWorld.TaskMgr:UnregisterEvent(C_ETaskEvent.TaskStartDataRefresh, self.onEvent_TaskRefreshFunc)
  end
  if self.onEvent_DayTimeRefreshFunc then
    C_WorldEvent.instance:Cancel(C_EWorldEvent.OnTimeHourChange, self.onEvent_DayTimeRefreshFunc)
  end
  self.pool:dispose()
  self:destroyList()
  self:destroySpecialPetMapNodeList()
  self:destroyMythicalPetNodeList()
  self:destroyHomeTraceMapNodeList()
  self:disposeItem()
  self:destroyAllLocker()
end

function this:show()
  self:refreshDayTimeArrow()
  if self.arrowTimer then
    Timer.remove(self.arrowTimer)
    self.arrowTimer = nil
  end
  self.arrowTimer = Timer.repeated(2, function()
    self:refreshDayTimeArrow()
  end, self, self.gameObject)
  if not table.isEmpty(self.mapNodeList) and not table.isEmpty(self.mapData) then
    for _, v in ipairs(self.mapNodeList) do
      v:refreshState(self.mapData)
    end
  end
  self:refreshTask()
  self:initSelfMarkNode()
  self.bindComponents.rect_imgMap.gameObject:ActiveTrans(true)
  L_SceneManager:addListener(L_SceneManager.event.loadSceneEnd, self.checkTimeChangeLock, self, nil, 0)
end

function this:hide()
  self:disposeItem()
  self.bindComponents.rect_imgMap.gameObject:ActiveTrans(false)
  if self.arrowTimer then
    Timer.remove(self.arrowTimer)
    self.arrowTimer = nil
  end
end

function this:onEvent_sceneEndHandle()
  self.lastPos = nil
  self.bind.go_normalMap = true
  self:initMap()
  if L_AreaStore:getMapAreaId() then
    self:initMapNode()
    self:refreshNodeListPos()
  end
  self:update()
end

function this:onEvent_refreshHomeAreaNode()
  if table.isEmpty(self.mapNodeList) then
    return
  end
  for _, v in pairs(self.mapNodeList) do
    if v.type == L_MapConst.mapSievingType.homeArea then
      v:refreshLockState()
    end
  end
end

function this:onEvent_refreshTransferNode()
  if table.isEmpty(self.mapNodeList) or table.isEmpty(self.areaBlockDic) then
    return
  end
  for _, v in pairs(self.mapNodeList) do
    if v.type == L_MapConst.mapSievingType.transfer then
      v:refreshLockState()
    elseif v.isUnLock ~= nil then
      v:refreshLockState()
    end
  end
  for i, node in ipairs(self.areaBlockDic.areaMask) do
    node:refreshLockState()
  end
end

function this:onEvent_mapAreaChanged(curAreaId)
  if L_DungeonManager:getIsRandomDungeon() then
  else
    self:initMap(curAreaId)
  end
  if curAreaId then
    self:initMapNode()
    self:refreshNodeListPos()
  end
  self.curAreaId = curAreaId
end

function this:onEvent_setCurrentTracedTask(taskId)
  self.taskId = taskId
  if taskId ~= nil and taskId ~= 0 and L_TaskStore:getIsTaskUnderway(taskId) == true then
    self:disposeItem()
  end
end

function this:onEvent_TaskRefresh()
  if not self.mapData then
    return
  end
  self:initMapNode()
  self:refreshNodeListPos()
end

function this:refreshNodeState()
  if table.isEmpty(self.mapNodeList) then
    return
  end
  for _, v in pairs(self.mapNodeList) do
    v:refreshState(self.mapData)
  end
end

function this:disposeItem()
  if self.taskLimitNode ~= nil then
    for i, node in pairs(self.taskLimitNode) do
      if node.effectTimer ~= nil then
        node.effectTimer:stop()
        Timer.remove(node.effectTimer)
        node.effectTimer = nil
      end
      C_GameObject.Destroy(node.obj)
    end
    table.clear(self.taskLimitNode)
  end
end

function this:destroyList()
  if table.isEmpty(self.mapNodeList) or table.isEmpty(self.areaBlockDic) then
    return
  end
  for k, v in pairs(self.mapNodeList) do
    v:onDestroy()
    C_GameObject.Destroy(v.trans.gameObject)
    self.mapNodeList[k] = {}
  end
  self.mapNodeList = {}
  for _, list in pairs(self.areaBlockDic) do
    for _, v in pairs(list) do
      v:onDestroy()
      C_GameObject.Destroy(v.trans.gameObject)
    end
  end
  self.areaBlockDic = nil
end

function this:destroySpecialPetMapNodeList()
  if table.isEmpty(self.mapSpecialPetList) then
    return
  end
  for k, v in pairs(self.mapSpecialPetList) do
    v:onDestroy()
    C_GameObject.Destroy(v.trans.gameObject)
  end
  self.mapSpecialPetList = {}
end

function this:destroyMythicalPetNodeList()
  if table.isEmpty(self.mapMythicalPetList) then
    return
  end
  for k, v in pairs(self.mapMythicalPetList) do
    v:onDestroy()
    C_GameObject.Destroy(v.trans.gameObject)
  end
  self.mapMythicalPetList = {}
end

function this:destroyMapCollectionItemNode()
  if table.isEmpty(self.mapCollectionItem) then
    return
  end
  for k, v in pairs(self.mapCollectionItem) do
    v:onDestroy()
    C_GameObject.Destroy(v.trans.gameObject)
  end
  self.mapCollectionItem = {}
end

function this:destroyHomeTraceMapNodeList()
  if table.isEmpty(self.mapHomeTraceList) then
    return
  end
  for k, v in pairs(self.mapHomeTraceList) do
    v:onDestroy()
    C_GameObject.Destroy(v.trans.gameObject)
  end
  self.mapHomeTraceList = {}
end

function this:destroyCollectionMapNodeList()
  if table.isEmpty(self.mapCollectionTraceList) then
    return
  end
  for k, v in pairs(self.mapCollectionTraceList) do
    v:onDestroy()
    C_GameObject.Destroy(v.trans.gameObject)
  end
  table.clear(self.mapCollectionTraceList)
end

function this:update()
  local player = L_PlayerManager:getMainControlEntity()
  if self.mapData == nil or player == nil then
    return
  end
  if not player:isValid() then
    error("\230\143\144\231\164\186 \233\151\174\228\184\139\232\138\146\230\158\156 \230\128\142\228\185\136player\229\143\136\232\162\171\233\148\128\230\175\129\228\186\134 \229\129\154\228\184\170\232\174\176\229\189\149 \229\183\178\229\129\154\228\191\157\230\138\164")
    return
  end
  local pos = player:getPosition()
  local playerRot = player:getRotation()
  self.offsetRot = self.mapData.zRot
  local _, cameraRotY, _ = self.mainCamerTrans:GetEulerAngles()
  local lock = L_Const.settingText[L_Const.settingType.smallMap][tonumber(L_SettingStore:getSetData(L_Const.settingType.smallMap))]
  if self.mapLock ~= lock then
    self.mapLock = lock
    if self.mapLock then
      self.bindComponents.mapBack:SetEulerAngles(0, 0, self.mapData.zRot)
    else
      self.bindComponents.cameraDir:SetEulerAngles(0, 0, 0)
    end
  end
  if self.mapLock then
    self.bindComponents.cameraDir:SetEulerAngles(0, 0, -cameraRotY + self.mapData.zRot)
    self.bindComponents.playerDir:SetEulerAngles(0, 0, -playerRot.y + self.mapData.zRot)
  else
    self.bindComponents.mapBack:SetEulerAngles(0, 0, cameraRotY)
    self.bindComponents.playerDir:SetEulerAngles(0, 0, cameraRotY - playerRot.y)
    self.offsetRot = -cameraRotY
  end
  if self.lastPos ~= pos then
    self.lastPos = pos
    local uiPosX, uiPosY
    uiPosX = pos.x * self.mapData.scale
    uiPosY = pos.z * self.mapData.scale
    self.playerUIPos = self.mapData:worldToLocal(pos)
    L_Vector3.setAnchored(self.bindComponents.player, self.playerUIPos)
    uiPosX = math.clamp(uiPosX, -self.mapData.halfX, self.mapData.halfX)
    uiPosY = math.clamp(uiPosY, -self.mapData.halfY, self.mapData.halfY)
    uiPosX = (pos.x - self.mapData.offset.x) * self.mapData.scale * self.mapData.imgScale
    uiPosY = (pos.z - self.mapData.offset.y) * self.mapData.scale * self.mapData.imgScale
    self.bindComponents.rect_imgMap:SetAnchoredPosition(-uiPosX, -uiPosY)
    self.bindComponents.rect_imgMap:SetLocalEulerAngles(0, 0, -self.mapData.zRot)
  elseif not self.playerUIPos then
    self.playerUIPos = L_Vector3.getLocalPos(self.bindComponents.player.transform)
  end
  self:updateNodeListRot(pos)
  self:refreshMapMarkNode(self.playerUIPos, pos)
  self:refreshTaskNode(self.playerUIPos, pos)
end

function this:GetTempMapVect(x, y, z)
  if self.tempMapVec == nil then
    self.tempMapVec = {
      x = 0,
      y = 0,
      z = 0
    }
  end
  self.tempMapVec.x = x
  self.tempMapVec.y = y
  self.tempMapVec.z = z
  return self.tempMapVec
end

function this:initMap(areaId)
  areaId = areaId or L_AreaStore:getMapAreaId()
  local isOpen = L_SystemUnlockStore:getSystemIsOpen(L_SystemConst.enum.adventure)
  if not isOpen then
    return
  end
  if areaId then
    self.mapData = L_MapManager:createMapData(areaId)
    self.mapData:miniMapCheckMinScale()
    self.mapData.imgScale = 0.5
    local rect = self.bindComponents.imgMap.rect
    self.mapData:setShowSize(rect.width / 2, rect.height / 2)
    L_Vector3.setUISize(self.bindComponents.imgMap, self.mapData.imgSize)
    L_Vector3.setSize(self.bindComponents.imgMap, self.mapData.imgScale)
    L_Vector3.setAnchored(self.bindComponents.imgMap, L_Vector3.zero)
    if self.mapBuilder ~= nil and self.mapBuilder.data.areaId ~= areaId then
      self.mapBuilder:removePaint()
      self.mapBuilder = nil
    end
    local tpl = _areaTpl:getTplById(self.mapData.areaId)
    local isCut = _areaTpl:getIsCut(tpl)
    if self.mapBuilder == nil then
      self.mapBuilder = L_MapManager:createMapBuilder(self.mapData)
      self.mapBuilder:paint(self.bindComponents.entityLayer)
      if self.mapData.isStream and isCut then
        self.mapBuilder:setTargetTrans(self.bindComponents.player)
      end
    end
    local scale = self.mapData.imgScale
    self.bindComponents.rect_imgMap.transform:SetScale(scale, scale, scale)
    scale = 1 / scale
    self.bindComponents.cameraDir.transform:SetScale(scale, scale, scale)
    self.bindComponents.playerDir.transform:SetScale(scale, scale, scale)
    self.mapLock = nil
    self.bindComponents.minimapGo.gameObject:ActiveTrans(true)
  else
    self.bindComponents.minimapGo.gameObject:ActiveTrans(false)
  end
end

function this:initMapNode()
  self:destroyList()
  if table.isEmpty(self.mapData) then
    return
  end
  self.areaBlockDic = {
    sceneBorderTransfer = {},
    areaMask = {},
    areaBoundary = {}
  }
  self.mapNodeList = L_MapManager:getMapNodeList(AzurWorldInstance.CurWorldId, filterNode)
  for _, v in ipairs(self.mapNodeList) do
    if L_MapManager:checkIsNoMaskNode(v.type) then
      v:generate(self.bindComponents.noMaskNode.transform, self.pool, true)
    else
      v:generate(self.bindComponents.entityNode.transform, self.pool, true)
    end
    v:refreshLockState()
    v:refreshState(self.mapData)
    v:setSize(L_Vector3.new(50, 50))
  end
  self.allImage = self.bindComponents.entityLayer:GetComponentsInChildren(typeof(C_LImage), true)
  for i = 0, self.allImage.Length - 1 do
    local image = self.allImage[i]
    image:LoadMaterial(MINIMAP_AREAMASK_MATERIAL)
  end
  self.areaBlockDic.areaMask = L_MapManager:getMapAreaMask(self.mapData.sceneId, self.areaBlockDic.areaMask)
  if table.isEmpty(self.areaBlockDic.areaMask) then
    self.bindComponents.mapMaskNode.gameObject:ActiveTrans(false)
  else
    self.bindComponents.mapMaskNode.gameObject:ActiveTrans(true)
  end
  for i, list in pairs(self.areaBlockDic) do
    for _, v in ipairs(list) do
      if v.type == L_MapConst.mapSievingType.areaMask then
        v:generate(self.bindComponents.mapMaskNode.transform, self.pool, true)
      else
        v:generate(self.bindComponents.entityNode.transform, self.pool, true)
      end
    end
  end
  for i, node in ipairs(self.areaBlockDic.areaMask) do
    node:refreshLockState()
  end
  self:initSpecialPetMarkNode()
  self:initMythicalPetNode()
  self:initHomeTraceMarkNode()
  self:refreshTask()
  self:initSelfMarkNode()
end

function this:refreshNodeListPos()
  if table.isEmpty(self.mapNodeList) then
    return
  end
  for _, node in ipairs(self.mapNodeList) do
    node:refreshPosByMapData(self.mapData)
  end
  for _, list in pairs(self.areaBlockDic) do
    for _, node in pairs(list) do
      node:refreshPosByMapData(self.mapData)
      node:refreshState(self.mapData)
    end
  end
end

function this:updateNodeListRot(playerPos)
  if table.isEmpty(self.mapNodeList) and table.isEmpty(self.mapSpecialPetList) and table.isEmpty(self.mapHomeTraceList) and table.isEmpty(self.mapCollectionTraceList) then
    return
  end
  local z
  if self.mapLock then
    z = 0
  else
    z = self.offsetRot + self.mapData.zRot
  end
  local setRot = math.abs(self.nodeRotVector.z - z) > 1.0E-4
  if setRot then
    self.nodeRotVector.z = z
  end
  if self.mapNodeList then
    for i, v in ipairs(self.mapNodeList) do
      if setRot then
        L_Vector3.setLocalRot(v.trans, self.nodeRotVector)
      end
      local canShowLocation, index = self:judgeHeightOffset(v.configPos.y - playerPos.y)
      v:refreshRelativeHeightPos(canShowLocation, index)
    end
  end
  if self.mapSpecialPetList then
    for i, v in ipairs(self.mapSpecialPetList) do
      if setRot then
        L_Vector3.setLocalRot(v.trans, self.nodeRotVector)
      end
      local canShowLocation, index = self:judgeHeightOffset(v.configPos.y - playerPos.y)
      v:refreshRelativeHeightPos(canShowLocation, index)
    end
  end
  if self.mapHomeTraceList then
    for i, v in ipairs(self.mapHomeTraceList) do
      if setRot then
        L_Vector3.setLocalRot(v.trans, self.nodeRotVector)
      end
      local canShowLocation, index = self:judgeHeightOffset(v.configPos.y - playerPos.y)
      v:refreshRelativeHeightPos(canShowLocation, index)
    end
  end
  if self.mapCollectionTraceList then
    for i, v in ipairs(self.mapCollectionTraceList) do
      if setRot then
        L_Vector3.setLocalRot(v.trans, self.nodeRotVector)
      end
      local canShowLocation, index = self:judgeHeightOffset(v.configPos.y - playerPos.y)
      v:refreshRelativeHeightPos(canShowLocation, index)
    end
  end
end

function this:checkIsInPlayerRadius(playerPos, targetPos)
  local distance = C_Vector3.Distance(playerPos, targetPos)
  local mapDistance = distance * self.mapData.scale
  return mapDistance < L_Const.miniMapSeeRadius
end

function this:refreshDayTimeArrow()
  local curTime = C_RealWeather.GetTimeOfDay()
  if math.abs(self.curTime - curTime) < 1 then
    return
  end
  self.curTime = curTime
  local hour = curTime / 100
  local timeOfDayRot = 90 - curTime / TIME_OF_DAY * 360
  if self.bindComponents.timeArrow then
    L_Vector3.setLocalRot(self.bindComponents.timeArrow, L_Vector3.new(0, 0, timeOfDayRot))
  end
  local isNotNight = hour >= halfDayHour_1 and hour <= halfDayHour_2
  if self.isNotNight ~= isNotNight then
    self.isNotNight = isNotNight
    self.bind.img_up = isNotNight == true and true or false
    self.bind.img_down = isNotNight == false and true or false
  end
end

function this:doAnimDay(curDayType)
end

function this:refreshTask()
  if self.mapData == nil then
    return
  end
  self:initTaskNode()
end

function this:initTaskNode()
  if self.taskLimitNode == nil then
    self.taskLimitNode = {}
  end
  local index = 0
  local infos = C_TaskManager.GetTaskTraceInfosBySceneId(AzurWorldInstance.CurWorldId)
  for i = 0, infos.Count - 1 do
    index = index + 1
    local func = function()
      return infos[i]:GetPosition(false)
    end
    local nodeIcon = self.taskLimitNode[index]
    if not nodeIcon then
      nodeIcon = self:createTaskNode()
      self.taskLimitNode[index] = nodeIcon
    end
    self:refreshTaskNodeData(nodeIcon, infos[i].TriggerData, infos[i].TaskId, func)
  end
  if index < 1 then
    self:activeLimitNode(false)
  else
    for i = index + 1, #self.taskLimitNode do
      self:activeLimitNode(false, nil, i)
    end
  end
end

function this:refreshTaskNode(playerV2Pos, playerPos)
  if not self.taskLimitNode then
    return
  end
  local count = #self.taskLimitNode
  for i = 1, count do
    local node = self.taskLimitNode[i]
    if node.activeNode then
      local taskEntityPos, res = node.getPosFunc()
      if res then
        local l2w = self.mapData:worldToLocal(taskEntityPos)
        if node.isNeedShowRange then
          local distanceSqr = L_Vector3.sqrDistance2(taskEntityPos.x, 0, taskEntityPos.z, playerPos.x, 0, playerPos.z)
          local showRange = distanceSqr <= node.triggerRangeSqr
          if node.curShowRange ~= showRange then
            node.curShowRange = showRange
            if showRange then
              node.imgNode.enabled = node.inRangeShowGuide == true
              node.areaGuideGo:ActiveTrans(node.isShow)
              node.outMap = false
              node.showLocation = false
              node.location:ActiveTrans(false)
            else
              node.imgNode.enabled = true
              node.areaGuideGo:ActiveTrans(false)
            end
          end
        end
        if not node.curShowRange then
          local isOutMask, result = self:judgeNodeShowStateOutMask(node, taskEntityPos, l2w, playerPos, playerV2Pos)
          node.outMap = isOutMask
          if isOutMask then
            self:activeLimitNode(true, result, i)
          else
            self:activeLimitNode(true, l2w, i)
          end
        else
          self:activeLimitNode(true, l2w, i)
        end
      else
        self:activeLimitNode(false, nil, i)
      end
    end
  end
end

function this:createTaskNode()
  local go = self.pool:syncGameObject("UI/Pages/Map/BigMap/cellMapTaskNode.prefab")
  go.transform:Reset(self.bindComponents.noMaskNode.transform)
  go.transform.sizeDelta = Unity.Vector2(60, 60)
  go.transform.localPosition = C_Vector3.zero
  local iconSize, _ = go.transform:GetUISize()
  iconSize = iconSize / 2
  local outDistance = (self.halfMapMaskSize - iconSize) / self.mapData.imgScale / self.mapData.scale
  local locationTrans = go.transform:Find("location")
  local areaGuideTrans = go.transform:Find("areaGuide")
  local nodeIcon = {
    obj = go,
    trans = go.transform,
    imgNode = go.transform:Find("taskIcon").gameObject:GetComponent(typeof(C_LImage)),
    imgBgNode = go:GetComponent(typeof(C_LImage)),
    effectControlNode = go:GetComponent(typeof(C_UIEffectControl)),
    locationTrans = locationTrans,
    location = locationTrans.gameObject,
    areaGuideTrans = areaGuideTrans,
    areaGuideGo = areaGuideTrans.gameObject,
    halfIconSize = iconSize,
    outDistanceSqr = outDistance * outDistance
  }
  return nodeIcon
end

function this:refreshTaskNodeData(nodeIcon, data, taskId, func)
  L_Vector3.setSize(nodeIcon.trans, 1 / self.mapData.imgScale)
  nodeIcon.isShow = true
  nodeIcon.isNeedShowRange = data.isNeedShowRange
  nodeIcon.showRange = data.showRange
  nodeIcon.inShow = data.inShow
  local triggerRange = data.triggerRange > 0 and data.triggerRange or data.showRange
  nodeIcon.triggerRangeSqr = triggerRange * triggerRange
  nodeIcon.inRangeShowGuide = data.inRangeShowGuide
  nodeIcon.getPosFunc = func
  nodeIcon.activeNode = true
  nodeIcon.obj:ActiveTrans(true)
  nodeIcon.curShowRange = nil
  nodeIcon.trans.sizeDelta = Unity.Vector2(60, 60)
  nodeIcon.imgNode.enabled = true
  nodeIcon.areaGuideGo:ActiveTrans(nodeIcon.curShowRange)
  nodeIcon.imgBgNode.enabled = nodeIcon.curShowRange
  if data.isNeedShowRange then
    local size = data.showRange * self.mapData.scale * self.mapData.imgScale * 2
    nodeIcon.areaGuideTrans:SetUISize(size, size)
  end
  local tplTask = _taskTpl:getTplById(taskId)
  if tplTask then
    nodeIcon.imgNode:LoadSprite(_taskTpl:getMapTaskIcon(tplTask))
    nodeIcon.imgBgNode:LoadSprite(_taskTpl:getMapTaskIconBg(tplTask))
  end
  nodeIcon.trans:SetAsLastSibling()
  if nodeIcon.effectTimer == nil then
    nodeIcon.effectTimer = Timer.repeated(1.1, function()
      nodeIcon.effectControlNode:Play()
    end, self)
    nodeIcon.effectTimer:pause()
    nodeIcon.resume = false
  end
end

function this:initSpecialPetMarkNode()
  self:destroySpecialPetMapNodeList()
  self.mapSpecialPetList = L_MapManager:getMapNodeListByType(AzurWorldInstance.CurWorldId, L_MapConst.mapSievingType.specialPet)
  for _, v in ipairs(self.mapSpecialPetList) do
    v:generate(self.bindComponents.entityNode.transform, self.pool, true)
    v:refreshLockState()
    v:refreshState(self.mapData)
    v:setSize(L_Vector3.new(50, 50))
  end
  self:refreshSpecialPetNodeListPos()
  self.allImage = self.bindComponents.entityLayer:GetComponentsInChildren(typeof(C_LImage), true)
  for i = 0, self.allImage.Length - 1 do
    local image = self.allImage[i]
    image:LoadMaterial(MINIMAP_AREAMASK_MATERIAL)
  end
end

function this:refreshSpecialPetNodeListPos()
  if table.isEmpty(self.mapSpecialPetList) then
    return
  end
  for _, node in ipairs(self.mapSpecialPetList) do
    node:refreshPosByMapData(self.mapData)
  end
end

function this:onEvent_refreshSpecialPetNode()
  self:initSpecialPetMarkNode()
end

function this:initMythicalPetNode()
  self:destroyMythicalPetNodeList()
  self.mapMythicalPetList = L_MapManager:getMapNodeListByType(AzurWorldInstance.CurWorldId, L_MapConst.mapSievingType.mythicalPet)
  for _, v in ipairs(self.mapMythicalPetList) do
    v:generate(self.bindComponents.noMaskNode.transform, self.pool, true)
    v:refreshLockState()
    v:refreshState(self.mapData)
    v:setSize(L_Vector3.new(50, 50))
  end
  self:refreshMythicalPetNodeListPos()
  self.allImage = self.bindComponents.entityLayer:GetComponentsInChildren(typeof(C_LImage), true)
  for i = 0, self.allImage.Length - 1 do
    local image = self.allImage[i]
    image:LoadMaterial(MINIMAP_AREAMASK_MATERIAL)
  end
end

function this:refreshMythicalPetNodeListPos()
  if table.isEmpty(self.mapMythicalPetList) then
    return
  end
  for _, node in ipairs(self.mapMythicalPetList) do
    node:refreshPosByMapData(self.mapData)
  end
end

function this:onEvent_refreshMythicalPetNode()
  self:initMythicalPetNode()
end

function this:initHomeTraceMarkNode()
  self:destroyHomeTraceMapNodeList()
  self.mapHomeTraceList = L_MapManager:getMapNodeListByType(AzurWorldInstance.CurWorldId, L_MapConst.mapSievingType.homeTrace)
  for _, v in ipairs(self.mapHomeTraceList) do
    v:generate(self.bindComponents.entityNode.transform, self.pool, true)
    v:refreshLockState()
    v:refreshState(self.mapData)
    v:setSize(L_Vector3.new(50, 50))
  end
  self:refreshHomeTraceNodeListPos()
  self.allImage = self.bindComponents.entityLayer:GetComponentsInChildren(typeof(C_LImage), true)
  for i = 0, self.allImage.Length - 1 do
    local image = self.allImage[i]
    image:LoadMaterial(MINIMAP_AREAMASK_MATERIAL)
  end
end

function this:refreshHomeTraceNodeListPos()
  if table.isEmpty(self.mapHomeTraceList) then
    return
  end
  for _, node in ipairs(self.mapHomeTraceList) do
    node:refreshPosByMapData(self.mapData)
  end
end

function this:onEvent_refreshHomeTraceNode()
  self:initHomeTraceMarkNode()
end

function this:refreshCollectionTrace()
  self:destroyCollectionMapNodeList()
  self:refreshCollectionItemNode()
  self:refreshCollectionNodeListPos()
  self.allImage = self.bindComponents.entityLayer:GetComponentsInChildren(typeof(C_LImage), true)
  for i = 0, self.allImage.Length - 1 do
    local image = self.allImage[i]
    image:LoadMaterial(MINIMAP_AREAMASK_MATERIAL)
  end
end

function this:refreshCollectionNodeListPos()
  if table.isEmpty(self.mapCollectionTraceList) then
    return
  end
  for _, node in ipairs(self.mapCollectionTraceList) do
    node:refreshPosByMapData(self.mapData)
  end
end

function this:refreshCollectionItemNode()
  local mapCollectionTraceId = L_MapStore:getCurTraceCollectionId()
  self.mapCollectionTraceList = L_MapManager:getCurMapCollectionItemNode(AzurWorldInstance.CurWorldId, mapCollectionTraceId)
  table.clear(self.collectionIdNodeMap)
  for _, v in ipairs(self.mapCollectionTraceList) do
    self.collectionIdNodeMap[v.id] = v
    v:generate(self.bindComponents.entityNode.transform, self.pool, true)
    v:refreshLockState()
    local serverData = AzurWorld.service.map:GetServerData(v.id, self.mapData.sceneId)
    if not serverData or not serverData.complete then
      v.hideNode = false
    else
      v.hideNode = true
    end
    v:refreshState(self.mapData)
    v:setSize(L_Vector3.new(50, 50))
  end
end

function this:refreshCollectionTraceItem(worldMapId)
  local node = self.collectionIdNodeMap[worldMapId]
  if node then
    local serverData = AzurWorld.service.map:GetServerData(node.id, self.mapData.sceneId)
    if not serverData or not serverData.complete then
      node.hideNode = false
    else
      node.hideNode = true
    end
    node:refreshState(self.mapData)
  end
end

function this:judgeNodeShowStateOutMask(nodeIcon, entityPos, entityV2Pos, playerPos, playerV2Pos)
  local heightOffest = entityPos.y - playerPos.y
  local canShowLocation, index = self:judgeHeightOffset(heightOffest)
  if nodeIcon.showLocation ~= canShowLocation then
    nodeIcon.showLocation = canShowLocation
    nodeIcon.location:ActiveTrans(canShowLocation)
  end
  if nodeIcon.showLocation and nodeIcon.localtionIndex ~= index then
    nodeIcon.localtionIndex = index
    local angle = index == 1 and 0 or 180
    nodeIcon.locationTrans:SetLocalEulerAngles(0, 0, angle)
  end
  local distanceSqr = L_Vector3.sqrDistance2(entityPos.x, 0, entityPos.z, playerPos.x, 0, playerPos.z)
  if distanceSqr >= nodeIcon.outDistanceSqr then
    local result = L_Vector3.new(entityV2Pos.x - playerV2Pos.x, entityV2Pos.y - playerV2Pos.y, 0).normalized / self.mapData.imgScale
    result.x = playerV2Pos.x + (self.halfMapMaskSize - nodeIcon.halfIconSize) * result.x
    result.y = playerV2Pos.y + (self.halfMapMaskSize - nodeIcon.halfIconSize) * result.y
    return true, result
  else
    return false, nil
  end
end

function this:activeLimitNode(bool, uiPos, index)
  if index == nil then
    for _, node in ipairs(self.taskLimitNode) do
      node.obj:ActiveTrans(false)
      node.activeNode = false
    end
    return
  end
  local node = self.taskLimitNode[index]
  if node ~= nil then
    if node.activeNode ~= bool then
      node.activeNode = bool
      node.obj:ActiveTrans(bool)
    end
    if bool == true and uiPos then
      L_Vector3.setAnchored(node.trans, uiPos)
      if node.outMap == true and node.resume == false then
        node.effectTimer:resume()
        node.resume = true
      elseif node.outMap == false and node.resume == true then
        node.effectTimer:pause()
        node.resume = false
      end
    end
  end
end

function this:initSelfMarkNode()
  if not table.isEmpty(self.mapMarkNode) then
    for i, v in pairs(self.mapMarkNode) do
      C_GameObject.Destroy(v.obj)
    end
  end
  self.mapMarkNode = {}
  local markData = L_MapStore:getMarkBymapId(self.curAreaId)
  if not table.isEmpty(markData) then
    for i, v in pairs(markData) do
      local mapNodeTpl = _mapNodeTpl:getTplById(v.markId)
      local icon = _mapNodeTpl:getIcon(mapNodeTpl)
      local go = self.pool:syncGameObject("UI/Pages/Map/BigMap/cellMapNode.prefab")
      go.transform:Reset(self.bindComponents.noMaskNode.transform)
      L_Vector3.setSize(go.transform, 1 / self.mapData.imgScale)
      go.transform.localPosition = C_Vector3.zero
      local node = go.transform:Find("nodeIcon").gameObject
      local btnNode = go:GetComponent(typeof(C_LButton))
      local nodeIconBg = go.transform:Find("nodeIconBg").gameObject
      nodeIconBg:ActiveTrans(false)
      btnNode.enabled = false
      local iconSize = 50
      local outDistance = (self.halfMapMaskSize - iconSize) / self.mapData.imgScale / self.mapData.scale
      local markNode = {
        obj = go.transform.gameObject,
        trans = go:GetComponent(typeof(C_RectTransform)),
        isShow = true,
        imgNode = node:GetComponent(_typeImg),
        location = node.transform:Find("location").gameObject,
        locationTrans = node.transform:Find("location").gameObject:GetComponent(typeof(C_RectTransform)),
        pos_x = v.pos_x,
        pos_y = v.pos_y,
        pos_z = v.pos_z,
        guid = v.guid,
        markId = v.markId,
        outDistanceSqr = outDistance * outDistance,
        halfIconSize = iconSize / 2
      }
      markNode.trans:SetUISize(iconSize, iconSize)
      markNode.imgNode:LoadSprite(icon)
      self.mapMarkNode[v.guid] = markNode
    end
  end
end

function this:refreshMapMarkNode(playerV2Pos, playerPos)
  if not self.curAreaId then
    return
  end
  if table.isEmpty(self.mapMarkNode) then
    return
  end
  for _, v in pairs(self.mapMarkNode) do
    local nodeIcon = v
    local entityPos = L_Vector3.new(v.pos_x, v.pos_y, v.pos_z)
    local l2w = self.mapData:worldToLocal(entityPos)
    L_Vector3.setAnchored(nodeIcon.trans, l2w)
    local markPos = l2w
    local isOutMask, result = self:judgeNodeShowStateOutMask(nodeIcon, entityPos, markPos, playerPos, playerV2Pos)
    if isOutMask and L_MapStore:getTracing(v.guid) then
      nodeIcon.outMap = true
      self:activeMapMarkNode(true, result, v.guid)
    elseif nodeIcon ~= nil then
      nodeIcon.outMap = false
      self:activeMapMarkNode(true, markPos, v.guid)
    end
  end
end

function this:activeMapMarkNode(bool, uiPos, guid)
  if guid == nil then
    for _, node in ipairs(self.mapMarkNode) do
      node.obj:ActiveTrans(false)
    end
    return
  end
  local node = self.mapMarkNode[guid]
  if node ~= nil then
    node.obj:ActiveTrans(bool)
    if bool == true and uiPos then
      node.trans.anchoredPosition = L_Vector3.new(uiPos.x, uiPos.y)
    end
  end
end

function this:judgeHeightOffset(offsetHeight)
  if offsetHeight > HEIGHT_RANGE then
    return true, 1
  elseif offsetHeight < -HEIGHT_RANGE then
    return true, 2
  end
  return false, 0
end

function this:checkTimeChangeLock()
  if L_SystemUnlockManager:check(L_SystemConst.enum.timeChange) then
    self.bind.time_root = true
  else
    self.bind.time_root = false
  end
  L_SceneManager:removeListener(L_SceneManager.event.loadSceneEnd, self.checkTimeChangeLock, self)
end

function this:activate()
  self:open()
  self:openModules()
  self:show()
  self:showModules()
end

function this:deActivate()
  self:hide()
  self:hideModules()
  self:close()
  self:closeModules()
end

return this
