local base = require("Scene.LWBattle.ParkourBattle.Monster.MonsterImpl.MonsterObj")
local Const = require("Scene.LWBattle.Const")
local BornState = require("Scene.LWBattle.BarrageBattle.ZombieState.ZombieStateBorn")
local IdleAndSearchState = require("Scene.LWBattle.BarrageBattle.ZombieState.ZombieStateIdle")
local DefenseRunState = require("Scene.LWBattle.ParkourBattle.Monster.MonsterImpl.FSM.CommonAI.DefenseRunState")
local RunState = require("Scene.LWBattle.BarrageBattle.ZombieState.ZombieStateRun")
local AttackState = require("Scene.LWBattle.ParkourBattle.Monster.MonsterImpl.FSM.CommonAI.ParkourMonsterAttackNormalState")
local AttackToIdleState = require("Scene.LWBattle.ParkourBattle.Monster.MonsterImpl.FSM.CommonAI.ParkourMonsterAttackToIdleState")
local DieState = require("Scene.LWBattle.ParkourBattle.Monster.MonsterImpl.FSM.CommonAI.DieState")
local GrayDieState = require("Scene.LWBattle.ParkourBattle.Monster.MonsterImpl.FSM.CommonAI.GrayDieState")
local ZombieStateHardControl = require("Scene.LWBattle.BarrageBattle.ZombieState.ZombieStateHardControl")
local FSM = require("Framework.Common.FSMWithPool")
local MultHpBarCell = require("DataCenter.ZombieBattle.HpBar.MultHpBarCell")
local SkillManager = require("Scene.LWBattle.Skill.SkillManager")
local pveUnitViewUtil = require("Scene.LWBattle.BarrageBattle.Unit.PveUnitViewUtil")
local UnitViewFacade = CS.PVEBattleLogic.Unit.UnitViewFacade
local CommonAIMonster = BaseClassCache("CommonAIMonster", base)

function CommonAIMonster:Init(logic, mgr, guid, x, y, monsterMeta)
  base.Init(self, logic, mgr, guid, x, y, monsterMeta)
  self.currState = nil
  self.stateList = {}
  self.tombstone = nil
  self.attackTargetId = nil
  self.pathIndex = 2
  self.isVisible = true
  self.agent = nil
  self.sid = 0
  self.skillManager = SkillManager.New(self.logic, self)
  self.outOfRangeCD = 1
  self.isBoss = monsterMeta.is_boss == 1 or monsterMeta.monster_type == Const.MonsterType.Boss
  if self.isBoss then
    EventManager:GetInstance():Broadcast(EventId.ParkourBossEnterBattle)
  end
  if self.logic.battleType and self.logic.battleType == Const.ParkourBattleType.Defense then
    self.eulerY = 180
  end
  self.bloodDirty = false
  self.isCheckOutOfRenderRange = self.monsterMeta.monster_type ~= Const.MonsterType.Boss and not self:IsIgnoreOutOfRenderRange()
  self.layer = LayerMask.NameToLayer("Zombie")
  self.singleDamageLimitPercent = 0
end

function CommonAIMonster:DestroyView()
  base.DestroyView(self)
  if self.hpBar then
    self.hpBar:Destroy()
    self.hpBar = nil
  end
  if self.hpBarHandle then
    pveUnitViewUtil.DestroyHpBar(self.hpBarHandle)
    self.hpBarHandle = nil
  end
  if self.fsm then
    self.fsm:Delete()
    ObjectPool:GetInstance():Save(self.fsm)
    self.fsm = nil
  end
  if self.skillManager then
    self.skillManager:DestroyView()
  end
  if self.agent then
    CS.UnityEngine.GameObject.Destroy(self.agent)
    self.agent = nil
  end
  self.anim = nil
  self.singleDamageLimitPercent = 0
end

function CommonAIMonster:DestroyData()
  base.DestroyData(self)
  if self.skillManager then
    self.skillManager:DestroyData()
    ObjectPool:GetInstance():Save(self.skillManager)
    self.skillManager = nil
  end
  self.isAlert = nil
end

function CommonAIMonster:ComponentDefine()
  base.ComponentDefine(self)
  self.anim = self.gameObject:GetComponentInChildren(typeof(CS.SimpleAnimation), true)
  if not self.anim then
    Logger.LogError("\232\175\165\229\141\149\228\189\141\228\184\139\233\157\162\230\178\161\230\140\130SimpleAnimation\232\132\154\230\156\172\239\188\140gameObject:" .. self.gameObject.name)
  end
  self.collider.gameObject.layer = LayerMask.NameToLayer("Zombie")
end

function CommonAIMonster:OnLoadComplete()
  self.agent = pveUnitViewUtil.AddAgent(self.mgr.logic.rvoMgr, self.viewHandle, 5.0, self.monsterMeta.collide_radius * 1.0)
  self:InitSkills()
  self:InitFsm()
  if self.monsterMeta.monster_type == Const.MonsterType.Boss then
    self:InitHpBar()
  end
  self.hasExtFirePath = false
  local fire_paths = self.monsterMeta.fire_paths
  if fire_paths then
    local firePathCount = #fire_paths
    if 0 < firePathCount then
      local append = UnitViewFacade.InitFirePoints(self.viewHandle, self.monsterMeta.id, firePathCount)
      if append then
        for i = 1, firePathCount do
          UnitViewFacade.AppendFirePoint(self.viewHandle, fire_paths[i])
        end
      end
      self.hasExtFirePath = true
    end
  end
  if self.viewHandle then
    UnitViewFacade.MPBResetGray(self.viewHandle)
  else
    for _, v in pairs(self.renders) do
      CS.AppearenceUtils.GrayEffect(v.renderer, false)
    end
  end
end

function CommonAIMonster:InitHpBar()
  ProfilerUtil.BeginSample("CommonAIMonster.InitHpBar")
  if self.isBoss and self.logic.useViewBossHpBar then
    EventManager:GetInstance():Broadcast(EventId.ParkourBossHpViewChanged)
    return
  end
  if self.monsterMeta.hp_bar_num > 1 then
    if not self.hpBar then
      self.hpBar = MultHpBarCell.New(Const.HPBarStyle.Enemy, self.transform, self.monsterMeta.hp_bar_height, self.monsterMeta.hp_bar_num)
      self.hpBar:LoadAndSetHp(self.curBlood, self.maxBlood)
    end
  elseif not self.hpBarHandle then
    self.hpBarHandle = pveUnitViewUtil.CreateEnemyHpBarWithHandle(self.viewHandle, self.monsterMeta.hp_bar_height * 1.0, nil, self.curBlood, self.maxBlood, self:GetShieldValue())
  end
  ProfilerUtil.EndSample()
end

function CommonAIMonster:InitSkills()
  if self.monsterMeta.skill then
    for _, skillId in pairs(self.monsterMeta.skill) do
      local skillMeta = DataCenter.HeroSkillTemplateManager:GetTemplate(skillId)
      if skillMeta == nil then
        Logger.LogError("\230\138\128\232\131\189\232\161\168\228\184\173\230\178\161\230\156\137id\228\184\186" .. skillId .. "\231\154\132\230\138\128\232\131\189")
      end
      self.skillManager:AddSkill(skillMeta)
    end
  end
end

function CommonAIMonster:TriggerSkill(triggerType, param)
  if self.skillManager then
    self.skillManager:PassiveCast(triggerType, param)
  end
end

function CommonAIMonster:InitFsm()
  self.fsm = ObjectPool:GetInstance():Load(FSM)
  self.fsm:Init(self)
  local isHasBornState = self.isFromSummon == true
  if isHasBornState then
    local bornState = ObjectPool:GetInstance():Load(BornState)
    bornState:Init(self)
    self.fsm:AddState(ZombieState.Born, bornState)
  end
  local stateCls
  if self.logic.battleType and self.logic.battleType == Const.ParkourBattleType.Defense then
    stateCls = DefenseRunState
  else
    stateCls = IdleAndSearchState
  end
  local stateObj = ObjectPool:GetInstance():Load(stateCls)
  stateObj:Init(self)
  self.fsm:AddState(ZombieState.Idle, stateObj)
  local runState = ObjectPool:GetInstance():Load(RunState)
  runState:Init(self)
  self.fsm:AddState(ZombieState.Run, runState)
  local actionType = self.monsterMeta.action_type
  if actionType == nil then
    actionType = 0
  end
  if actionType == 1 then
    stateCls = AttackToIdleState
  else
    stateCls = AttackState
  end
  stateObj = ObjectPool:GetInstance():Load(stateCls)
  stateObj:Init(self)
  self.fsm:AddState(ZombieState.Attack, stateObj)
  local dieStateObj
  if self.logic and self.logic.data and self.logic.data.deathGray then
    dieStateObj = ObjectPool:GetInstance():Load(GrayDieState)
  else
    dieStateObj = ObjectPool:GetInstance():Load(DieState)
  end
  dieStateObj:Init(self)
  self.fsm:AddState(ZombieState.Die, dieStateObj)
  local hardControlState = ObjectPool:GetInstance():Load(ZombieStateHardControl)
  hardControlState:Init(self)
  self.fsm:AddState(ZombieState.HardControl, hardControlState)
  if isHasBornState then
    self.fsm:ChangeState(ZombieState.Born)
  else
    self.fsm:ChangeState(ZombieState.Idle)
  end
end

function CommonAIMonster:SetVisible(visible)
  self.isVisible = visible
  if self.gameObject then
    self.gameObject:SetActive(visible)
  end
end

function CommonAIMonster:OnUpdate(deltaTime)
  if self.bloodDirty then
    self.bloodDirty = false
    if self.curBlood > 0 then
      if self.hpBar then
        self.hpBar:SetHp(self.curBlood, self.maxBlood, self:GetShieldValue())
      elseif self.hpBarHandle then
        pveUnitViewUtil.SetHpBar(self.hpBarHandle, self.curBlood, self.maxBlood, self:GetShieldValue())
      else
        self:InitHpBar()
      end
    end
  end
  self:UpdateBuffManager()
  self:UpdateFlashCountdown(deltaTime)
  if self.fsm then
    self.fsm:OnUpdate(deltaTime)
  end
  if self.skillManager then
    self.skillManager:OnUpdate(deltaTime)
  end
  if self.hpBar then
    self.hpBar:Update()
  end
  if self.isCheckOutOfRenderRange then
    self.outOfRangeCD = self.outOfRangeCD - deltaTime
    if 0 > self.outOfRangeCD then
      self.outOfRangeCD = 1
      self:CheckOutOfRenderRange()
    end
  end
end

function CommonAIMonster:CheckOutOfRenderRange()
  if self.battleMgr.team:GetPosition().z - self:GetPosition().z > ZOMBIE_REMOVE_DISTANCE_Z then
    self.mgr:RemoveMonster(self.guid)
  end
end

function CommonAIMonster:GetCurBlood()
  return self.curBlood
end

function CommonAIMonster:BeAttack(hurt, hitPoint, hitDir, whiteTime, stiffTime, dir, hitEff)
  local hpPercentBefore = self.curBlood / self.maxBlood
  base.BeAttack(self, hurt, hitPoint, hitDir, whiteTime, stiffTime, dir, hitEff)
  local hpPercentAfter = self.curBlood / self.maxBlood
  if not self.logic.battleType or self.logic.battleType == Const.ParkourBattleType.Attack then
    self.isAlert = true
  end
  if 0 < hurt and self.curBlood > 0 then
    self.bloodDirty = true
  end
  self:BeAttackEffect(stiffTime, dir)
  local param = {
    hpPercentBefore = hpPercentBefore,
    hpPercentAfter = hpPercentAfter,
    hurt = hurt
  }
  self:TriggerSkill(SkillTriggerType.BeHitNew, param)
  if self.curBlood <= 0 then
    self:TriggerSkill(SkillTriggerType.Death)
    if self.logic and self.logic.detailLog then
      Logger.LogInfo("parkour monsterDeath metaId : " .. self.monsterMetaId .. ". maxBlood : " .. (self.maxBlood or 0) .. ". hitCounter : " .. (self.hitCounter or 0))
    end
  end
end

function CommonAIMonster:BeAttackEffect(stiffTime, dir)
  if dir and self.meta.ignore_hit_back == 0 then
    if 0 < self.curBlood then
      self.fsm:ChangeState(ZombieState.HardControl, HardControlType.Imprison, 0.5)
    end
    self.transform:DOMove(self.transform.position + dir, 0.5):SetEase(CS.DG.Tweening.Ease.OutCirc)
  elseif stiffTime and 0 < stiffTime and self.meta.ignore_hit_stiff == 0 and 0 < self.curBlood then
    self.fsm:ChangeState(ZombieState.HardControl, HardControlType.Stiff, stiffTime)
  end
end

function CommonAIMonster:Death()
  if self.fsm then
    self.fsm:ChangeState(ZombieState.Die)
  end
  if self.hpBar then
    self.hpBar:Destroy()
    self.hpBar = nil
  end
  if self.hpBarHandle then
    pveUnitViewUtil.DestroyHpBar(self.hpBarHandle)
    self.hpBarHandle = nil
  end
  if self.isBoss and self.logic.useViewBossHpBar then
    EventManager:GetInstance():Broadcast(EventId.ParkourBossHpViewDead)
    return
  end
end

function CommonAIMonster:CreateBeHitEffect()
end

function CommonAIMonster:SetDestination(x, z)
  return self.agent:SetTargetPosition(x, z)
end

function CommonAIMonster:RemoveDestination()
  return self.agent:SetActive(false)
end

function CommonAIMonster:CheckEnemyInAlertRange()
  local posZ = self.mgr.logic.team:GetPositionZ()
  return math.abs(self.transform.position.z - posZ) <= self.meta.alert_range
end

function CommonAIMonster:GetFirePointById(id)
  if self.hasExtFirePath then
    local point = UnitViewFacade.GetFirePointById(self.viewHandle, id)
    if point ~= nil then
      return point, false
    end
  end
  return self:GetFirePoint()
end

function CommonAIMonster:GetFirePoint()
  return self.transform, false
end

function CommonAIMonster:OnBuffAdded(buff)
  if buff.meta.type == BuffType.Imprison and self.agent then
    self.agent.speed = 0
  end
  if buff.meta.type == BuffType.SingleSkillDamageLimit then
    self.singleDamageLimitPercent = tonumber(buff.meta.rawPara) / 10000
  end
  base.OnBuffAdded(self, buff)
end

function CommonAIMonster:OnBuffRemoved(buff)
  if buff.meta.type == BuffType.Imprison then
    if not self:IsImprisoning() and self.agent and self.logic and self.fsm then
      if self.fsm:GetStateIndex() == ZombieState.Idle then
        if self.logic.battleType and self.logic.battleType == Const.ParkourBattleType.Defense then
          self.agent.speed = self.logic:GetMoveSpeedZ()
        else
          self.agent.speed = 1
        end
      else
        self.agent.speed = self.meta.move_speed
      end
    end
  elseif buff.meta.type == BuffType.SingleSkillDamageLimit then
    self.singleDamageLimitPercent = 0
  end
  base.OnBuffRemoved(self, buff)
end

function CommonAIMonster:GetFreezeXAxisMoveMinDistance()
  return self.logic and self.logic:GetFreezeXAxisMoveMinDistance() or nil
end

function CommonAIMonster:GetDieStayTime()
  local stayTime = 2
  if self.logic and self.logic.data then
    if self.isBoss and self.logic.data.bossStayTime then
      stayTime = self.logic.data.bossStayTime
    elseif self.logic.data.monsterStayTime then
      stayTime = self.logic.data.monsterStayTime
    end
  end
  return stayTime
end

function CommonAIMonster:IsIgnoreOutOfRenderRange()
  local isIgnore = false
  if self.logic and self.logic.GetPVEType and self.logic:GetPVEType() == PVEType.LastStand then
    isIgnore = true
  end
  return isIgnore
end

return CommonAIMonster
