local this = {}
local sConcat = string.concat
local _petTpl = L_GameTpl:getPetTpl()
local _petDetailTpl = L_GameTpl:getPetDetailUiParamTpl()
local _unitTpl = L_GameTpl:getUnitTpl()
local PETSCENEPATH = "Program/UIScene/pre_pethouse_ui_01.prefab"
local PETGETBLACKPATH = "Program/UIScene/pre_KiboAddBlack.prefab"

function this:loadPetDetailScene()
  if self.runtimeData and self.runtimeData.petScene then
    self.runtimeData.petScene:ActiveTrans(true)
    return
  end
  self.runtimeData = require("ui.manager.pet.petSceneRuntimeData").new(L_ResPool:syncGameObject(PETSCENEPATH))
  local sceneTrans = self.runtimeData.petScene.transform
  sceneTrans:SetParent(C_LuaUtility.GetRootNode())
  L_Vector3.setPos(sceneTrans, L_Vector3.zero)
  L_Vector3.setRot(sceneTrans, L_Vector3.zero)
end

function this:removePetScene()
  L_TimerManager:clearTimer(self)
  if self._petTransMatJob then
    co.stop(self._petTransMatJob)
    self._petTransMatJob = nil
  end
  self.runtimeData:destroy()
  self.runtimeData = nil
end

function this:loadPetModel(petConfigId, isSpecial, isFlash)
  if math.isEmpty(petConfigId) then
    self:removePetModel()
    return
  end
  local curPlacePet = self:_getInPlacePet()
  if not table.isEmpty(curPlacePet) and curPlacePet.configId == petConfigId then
    self:_setPetFlashEft(isFlash, curPlacePet.obj)
    return
  end
  self:removePetModel()
  local pointTrans = self.runtimeData.petPoints
  local tpl = _petDetailTpl:getTplById(petConfigId)
  local posList = _petDetailTpl:getUnitCameraPosition(tpl)
  self:setCameraNodePos(L_Vector3.new(posList[1], posList[2], posList[3]))
  local petObj, entity
  if not table.isEmpty(self.runtimeData.placePets) then
    petObj = self.runtimeData.placePets.obj
    entity = self.runtimeData.placePets.entity
  else
    petObj, entity = self:_fetchPetModel(petConfigId, isSpecial)
  end
  local rotationOffset = C_GameObject("RotationOffset")
  rotationOffset.transform:SetParent(pointTrans)
  L_Vector3.setLocalPos(rotationOffset.transform, L_Vector3.zero)
  L_Vector3.setLocalRot(rotationOffset.transform, L_Vector3.zero)
  L_Vector3.setSize(rotationOffset.transform, 1)
  petObj.transform:SetParent(rotationOffset.transform)
  local unitId = L_GameUtil.getUnitIdByRole(petConfigId, L_Const.roleType.pet)
  local all_stage_scale = _petDetailTpl:getUnitScale(tpl) or 10000
  local unitTpl = _unitTpl:getTplById(unitId)
  local worldScale = _unitTpl:getModelScale(unitTpl)
  local scale = all_stage_scale / 10000
  L_Vector3.setSize(petObj.transform, scale * worldScale)
  local position = _petDetailTpl:getUnitPosition(tpl)
  if table.isEmpty(position) then
    L_Vector3.setLocalPos(petObj.transform, L_Vector3.zero)
  else
    L_Vector3.setLocalPos(petObj.transform, L_Vector3.new(position[1], position[2], position[3]))
  end
  local rot = L_Vector3.zero
  L_Vector3.setLocalRot(petObj.transform, rot)
  petObj:ActiveTrans(true)
  local petTpl = _petTpl:getTplById(petConfigId)
  local rotationType = _petTpl:getUnitRotationType(petTpl)
  self.runtimeData.placePets = {
    obj = petObj,
    configId = petConfigId,
    entity = entity,
    unitId = unitId,
    rotationType = rotationType,
    rotationRoot = pointTrans
  }
  self:_setPetFlashEft(isFlash, petObj)
  local hipBoneTrans = L_GameUtil.findTranByName(petObj, "Hips")
  local neckBoneTrans = L_GameUtil.findTranByName(petObj, "Neck")
  if hipBoneTrans and neckBoneTrans then
    local hipPos = hipBoneTrans.position
    local neckPos = neckBoneTrans.position
    local centerPos = (hipPos + neckPos) * 0.5
    rotationOffset.transform.position = centerPos
    local localPos = L_Vector3.getLocalPos(rotationOffset.transform)
    local newPos = L_Vector3.new(-localPos.x, 0, -localPos.z)
    L_Vector3.setLocalPos(rotationOffset.transform, newPos)
  end
  self._petTransMatJob = co.start(function()
    co.step(2)
    local renderers = petObj:GetComponentsInChildren(typeof(CS.UnityEngine.SkinnedMeshRenderer))
    for i = 0, renderers.Length - 1 do
      local materials = renderers[i].sharedMaterials
      for j = 0, materials.Length - 1 do
        local material = materials[j]
        if material and material.shader then
          local shaderName = material.shader.name
          if string.find(shaderName, "Transparent") or string.find(shaderName, "Expression") or string.find(shaderName, "ImmuneBuff") then
            material.renderQueue = material.renderQueue + 1500
          end
        else
          print("Material: null")
        end
      end
    end
  end)
end

function this:loadBlackPetModel(petConfigId)
  if math.isEmpty(petConfigId) then
    self:removePetModel()
    return
  end
  local curPlacePet = self:_getInPlacePet()
  if not table.isEmpty(curPlacePet) and curPlacePet.configId == petConfigId then
    self:_setPetFlashEft(isFlash, curPlacePet.obj)
    return
  end
  self:removePetModel()
  local pointTrans = self.runtimeData.petPoints
  local tpl = _petDetailTpl:getTplById(petConfigId)
  local posList = _petDetailTpl:getUnitCameraPosition(tpl)
  self:setCameraNodePos(L_Vector3.new(posList[1], posList[2], posList[3]))
  local petObj, entity
  if not table.isEmpty(self.runtimeData.placePets) then
    petObj = self.runtimeData.placePets.obj
    entity = self.runtimeData.placePets.entity
  else
    petObj, entity = self:_fetchPetModel(petConfigId)
  end
  petObj.transform:SetParent(pointTrans)
  local unitId = L_GameUtil.getUnitIdByRole(petConfigId, L_Const.roleType.pet)
  local all_stage_scale = _petDetailTpl:getUnitScale(tpl) or 10000
  local unitTpl = _unitTpl:getTplById(unitId)
  local worldScale = _unitTpl:getModelScale(unitTpl)
  local scale = all_stage_scale / 10000
  L_Vector3.setSize(petObj.transform, scale * worldScale)
  local position = _petDetailTpl:getUnitPosition(tpl)
  if table.isEmpty(position) then
    L_Vector3.setLocalPos(petObj.transform, L_Vector3.zero)
  else
    L_Vector3.setLocalPos(petObj.transform, L_Vector3.new(position[1], position[2], position[3]))
  end
  local rot = L_Vector3.zero
  L_Vector3.setLocalRot(petObj.transform, rot)
  petObj:ActiveTrans(true)
  local petTpl = _petTpl:getTplById(petConfigId)
  local rotationType = _petTpl:getUnitRotationType(petTpl)
  self.runtimeData.placePets = {
    obj = petObj,
    configId = petConfigId,
    entity = entity,
    unitId = unitId,
    rotationType = rotationType,
    rotationRoot = pointTrans
  }
  local blackObj = L_ResPool:syncGameObject(PETGETBLACKPATH)
  blackObj.transform:Reset(petObj.transform)
  blackObj.transform.localScale = L_Vector3.one
  blackObj.transform.localPosition = L_Vector3.zero
  blackObj.name = "blackRoot"
  local EffectMaterialHelperCom = blackObj.transform:GetComponent(typeof(CS.EffectMaterialHelper))
  if EffectMaterialHelperCom then
    EffectMaterialHelperCom.characterRoot = petObj
  end
  local EffectAutoMultiColCom = blackObj.transform:GetComponent(typeof(CS.EffectAutoMultiCol))
  if EffectAutoMultiColCom then
    EffectAutoMultiColCom.characterRoot = petObj
  end
  self.runtimeData.blackObj = blackObj
  self:_setPetBlackColor(C_Color(0, 0, 0, 1))
  blackObj:SetActive(false)
  blackObj:SetActive(true)
end

function this:_setPetBlackColor(color)
  if table.isEmpty(self.runtimeData) or self.runtimeData.blackObj == nil then
    return
  end
  local blackObj = self.runtimeData.blackObj
  local effectMaterialHelper = blackObj.transform:GetComponent(typeof(CS.EffectMaterialHelper))
  local EffectAutoMultiColCom = blackObj.transform:GetComponent(typeof(CS.EffectAutoMultiCol))
  local material = effectMaterialHelper.material
  material:SetColor("_IB_Color", color)
  local percent = 1 - color.a
  EffectAutoMultiColCom.effectMultiColor = C_Color(percent, percent, percent, 1)
end

function this:removePetModel()
  if table.isEmpty(self.runtimeData.placePets) then
    return
  end
  self:_recyclePetModel(self.runtimeData.placePets.configId, self.runtimeData.placePets.obj)
  self.runtimeData.placePets = nil
  self:_setPetFlashEft(false)
end

function this:_getInPlacePet()
  return self.runtimeData.placePets
end

function this:_fetchPetModel(petConfigId, isSpecial)
  local tpl = _petTpl:getTplById(petConfigId)
  local unitId = _petTpl:getUnitID(tpl)
  local obj
  if self.runtimeData.petCache[petConfigId] then
    obj = self.runtimeData.petCache[petConfigId]
  else
    local path = L_GameUtil.getAvatarModelPathByUnitId(unitId, L_Const.avatarModelIndex.UnitModel)
    if isSpecial and isSpecial == true then
      local unitTpl = L_GameTpl:getUnitTpl()
      local specialParm = unitTpl:getSpecialParm(unitTpl:getTplById(unitId))
      local replaceStr = specialParm[1]
      if replaceStr then
        path = string.gsub(path, ".prefab", "_" .. replaceStr .. ".prefab")
      end
    end
    obj = L_ResPool:syncGameObject(path)
  end
  local animator = L_CommonUtil.getModComponent(typeof(C_PlayableAnimator), obj)
  animator.cullingMode = C_AnimatorCullingMode.AlwaysAnimate
  animator.updateMode = C_PlayableAnimator.UpdateMode.LateUpdate
  animator.rootMotionController.selfApplyRootMotion = true
  obj.name = sConcat("pet_", petConfigId)
  local entity = L_EntityManager:generatePreviewPet(petConfigId, obj)
  entity.playableAnimator:Play("Idle")
  return obj, entity
end

function this:_recyclePetModel(petConfigId, petObj)
  self.runtimeData.placePets.entity:onDestroy()
  petObj:SetActive(false)
  local petTrans = petObj.transform
  petTrans:SetParent(self.runtimeData.modelCache)
  L_Vector3.setLocalPos(petTrans, L_Vector3.one)
  L_Vector3.setLocalRot(petTrans, L_Vector3.zero)
end

function this:_setPetFlashEft(bShow, petObj)
  if not bShow then
    if self.runtimeData.petFlashObj then
      L_CommonUtil.setObjActive(self.runtimeData.petFlashObj, false)
      self.runtimeData.petFlashObj.transform:SetParent(self.runtimeData.modelCache)
    end
    return
  end
  local obj
  if self.runtimeData.petFlashObj then
    obj = self.runtimeData.petFlashObj
  else
    local mediaPackTpl = L_GameTpl:getMediaPackTplTpl()
    local petFlashKey = L_Config:getConfig("game", "PET_FLASH_EFFECT_PATH")
    local path = mediaPackTpl:getPath(mediaPackTpl:getTplByKey(petFlashKey))
    obj = L_ResPool:syncGameObject(path)
  end
  local EffectMaterialHelperCom = obj.transform:GetComponentInChildren(typeof(CS.EffectMaterialHelper))
  if EffectMaterialHelperCom then
    EffectMaterialHelperCom.characterRoot = petObj
  end
  local ImmuneBuffHelperCom = obj.transform:GetComponentInChildren(typeof(CS.ImmuneBuffHelper))
  if ImmuneBuffHelperCom then
    ImmuneBuffHelperCom.characterRoot = petObj
  end
  local BindMeshEffectCom = obj.transform:GetComponentInChildren(typeof(CS.Lens.Gameplay.Modules.BigWorld.BindMeshEffect))
  if BindMeshEffectCom then
    BindMeshEffectCom:BindMeshBody(petObj)
  end
  obj.transform:SetParent(petObj.transform)
  L_CommonUtil.setObjActive(obj, false)
  L_CommonUtil.setObjActive(obj, true)
  L_Vector3.setLocalPos(obj.transform, L_Vector3.zero)
  L_Vector3.setLocalRot(obj.transform, L_Vector3.zero)
  L_Vector3.setSize(obj.transform, 1)
  self.runtimeData.petFlashObj = obj
end

function this:setSceneArtActive(active)
  if L_CommonUtil.isValid(self.runtimeData.petScene) then
    self.runtimeData.petScene:SetActive(active)
  end
end

function this:setScenePetRot(rot)
  if not table.isEmpty(self.runtimeData.placePets) then
    if self.runtimeData.placePets.rotationType == 1 then
      local newRot = L_Vector3.new(rot.x, rot.y - 45, rot.z)
      L_Vector3.setLocalRot(self.runtimeData.placePets.rotationRoot.transform, newRot)
    elseif self.runtimeData.placePets.rotationType == 2 then
      L_Vector3.setLocalRot(self.runtimeData.placePets.obj.transform, rot)
    end
  end
end

function this:setCameraNodePos(pos)
  if L_CommonUtil.isValid(self.runtimeData.cameraNode) then
    L_Vector3.setLocalPos(self.runtimeData.cameraNode.transform, pos)
  end
end

function this:setCameraNodePosOffset(pos)
  if L_CommonUtil.isValid(self.runtimeData.cameraNode) then
    local oldPos = L_Vector3.getLocalPos(self.runtimeData.cameraNode.transform)
    local newPos = oldPos + pos
    L_Vector3.setLocalPos(self.runtimeData.cameraNode.transform, newPos)
  end
end

function this:setCameraNodeRotOffset(rot)
  if L_CommonUtil.isValid(self.runtimeData.cameraNode) then
    local oldRot = L_Vector3.getLocalRot(self.runtimeData.cameraNode.transform)
    local newRot = oldRot + rot
    L_Vector3.setLocalRot(self.runtimeData.cameraNode.transform, newRot)
  end
end

return this
