local DamageTextManager = BaseClass("DamageTextManager")
local Resource = CS.GameEntry.Resource
local Localization = CS.GameEntry.Localization
local DamageFlyText = "Assets/Main/Prefabs/LWBattle/DamageFlyText.prefab"
local BuffFlyText = "Assets/Main/Prefabs/LWBattle/BuffFlyText.prefab"
local White = Color.New(1, 1, 1, 1)
local Red = Color.New(0.9333333, 0, 0, 1)
local Yellow = Color.New(1, 0.909, 0)
local Cyan = Color.New(0, 1, 1, 1)
local Transparent = Color.New(0, 0, 0, 0)
local Crit_Path = "Assets/Main/Sprites/UI/LWCommon/Sprite/zyf_tubiao_baoji_bai.png"
local Shield_Path = "Assets/Main/Sprites/UI/LWCommon/Sprite/zyf_guanka_dun.png"
local TACTICAL_WEAPON_PATH = "Assets/Main/Sprites/UI/LWCommon/Sprite/zyf_zhanbao_wurenji_icon.png"
local CRIT_PHYSICS_PATH = "Assets/Main/Sprites/UI/UIHeroCommon/zyf_jinengleixin_wulishanghai_new.png"
local CRIT_MAGIC_PATH = "Assets/Main/Sprites/UI/LWCommon/Sprite/zyf_jinengleixin_nengliangshanghai.png"
local MAX_INSTANCE_PERFRAME = 20
local MAX_INSTANCE_ALIVE = 40

function DamageTextManager:__init()
end

function DamageTextManager:__delete()
  self:Destroy()
end

function DamageTextManager:Init()
  self.flyTextReqs = {}
  self.flyTextList = list:new()
  self.flyTextMap = {}
  self.flyTextPool = {}
  self.curFrameTaskCount = 0
  self.lastFrameTaskCount = 0
  self.flyTextsType = {}
  self.normalAttackQueue = list:new()
  self.normalAttackMap = {}
  self.paramsPool = {}
end

function DamageTextManager:Destroy()
  if self.flyTextReqs then
    for _, v in ipairs(self.flyTextReqs) do
      v:Destroy()
    end
  end
  self.flyTextReqs = nil
  self.flyTextList = nil
  self.flyTextMap = nil
  self.flyTextPool = nil
  self.curFrameTaskCount = 0
  self.lastFrameTaskCount = 0
  self.flyTextsType = {}
  self.normalAttackQueue = nil
  self.normalAttackMap = nil
end

local MAGIC_COLOR = CommonUtil.CreatColorGammaToLinear255(200, 128, 236, 255)
local PHYSICS_COLOR = CommonUtil.CreatColorGammaToLinear255(255, 172, 64, 255)
local DEBUFF_COLOR = CommonUtil.CreatColorGammaToLinear255(255, 121, 103, 255)
local BUFF_COLOR = CommonUtil.CreatColorGammaToLinear255(170, 241, 146, 255)

function DamageTextManager.GetDamageTypeColor(damageType)
  if damageType and damageType == DamageType.Physics then
    return PHYSICS_COLOR, CRIT_PHYSICS_PATH
  elseif damageType and damageType == DamageType.Magic then
    return MAGIC_COLOR, CRIT_MAGIC_PATH
  else
    return White, Crit_Path
  end
end

local color_cache = {}
local LUA_MATH_FLOOR = math.floor

function DamageTextManager.GetColor32(color)
  if color_cache[color] then
    return color_cache[color]
  end
  local color32 = Color32.New(LUA_MATH_FLOOR(color.r * 255), LUA_MATH_FLOOR(color.g * 255), LUA_MATH_FLOOR(color.b * 255), LUA_MATH_FLOOR(color.a * 255))
  color_cache[color] = color32
  return color32
end

local AddNewFlyTextToList = function(self, flyText, style)
  local node = self.flyTextList:push(flyText)
  self.flyTextMap[flyText] = node
  if style == DamageTextType.HeroNormalAttack then
    node = self.normalAttackQueue:push(node)
    self.normalAttackMap[flyText] = node
  end
end
local RemoveFlyTextFromListIndex = function(self, index)
  if index <= 0 then
    return
  end
  local node = self.flyTextList:findByIndex(index)
  if 0 < index and node then
    local flyText = node.value
    self.flyTextList:remove(node)
    if flyText then
      self.flyTextMap[flyText] = nil
    end
    local normalAttackNode = self.normalAttackMap[flyText]
    if normalAttackNode then
      self.normalAttackQueue:remove(normalAttackNode)
      self.normalAttackMap[flyText] = nil
    end
    self:FlyTextInPool(flyText)
  end
end
local RemoveFlyTextFromList = function(self, flyText)
  if flyText and self.flyTextMap[flyText] then
    local node = self.flyTextMap[flyText]
    self.flyTextList:remove(node)
    self.flyTextMap[flyText] = nil
    local normalAttackNode = self.normalAttackMap[flyText]
    if normalAttackNode then
      self.normalAttackQueue:remove(normalAttackNode)
      self.normalAttackMap[flyText] = nil
    end
    self:FlyTextInPool(flyText)
  end
end
local To_Delete = {}

function DamageTextManager:GenText(params)
  local style = params.style
  local time = params.time
  self.curFrameTaskCount = self.curFrameTaskCount + 1
  if self.lastFrameTaskCount > MAX_INSTANCE_PERFRAME and style == DamageTextType.HeroNormalAttack then
    return
  end
  for i = 1, #To_Delete do
    To_Delete[i] = nil
  end
  local aliveCount = self.flyTextList.length
  if aliveCount >= MAX_INSTANCE_ALIVE then
    local needToUnload = aliveCount - MAX_INSTANCE_ALIVE
    local unloadCnt = 0
    for i = 1, needToUnload do
      local flyTextInst = self.normalAttackQueue:pop()
      if not IsNull(flyTextInst) then
        unloadCnt = unloadCnt + 1
        To_Delete[unloadCnt] = flyTextInst.value
      else
        break
      end
    end
  end
  for i = 1, #To_Delete do
    RemoveFlyTextFromList(self, To_Delete[i])
  end
  local prefabPath = DamageFlyText
  if style == DamageTextType.GetBuff then
    prefabPath = BuffFlyText
  end
  local flyTime = 1
  if time then
    flyTime = tonumber(time) or 1
  end
  local flyText = self:GetFlyTextFromPool(prefabPath)
  if flyText then
    self:InitFlyText(params, flyTime, flyText)
    flyText.time = flyTime
    AddNewFlyTextToList(self, flyText, style)
    return
  end
  local flyTextInst = Resource:InstantiateAsync(prefabPath, ObjectPoolTag.Battle)
  table.insert(self.flyTextReqs, flyTextInst)
  flyTextInst:completed("+", function(req)
    local go = req.gameObject
    local req_trans = go.transform
    local numText = req_trans:Find("Scale/num"):GetComponent(typeof(CS.TextMeshProEx))
    local critIcon = req_trans:Find("Scale/num/critIcon"):GetComponent(typeof(CS.UnityEngine.SpriteRenderer))
    local simpleAnim = req_trans:GetComponent(typeof(CS.SimpleAnimation))
    local newFlyText = {
      go = go,
      req_trans = req_trans,
      numText = numText,
      critIcon = critIcon,
      simpleAnim = simpleAnim
    }
    newFlyText.prefabPath = prefabPath
    self:InitFlyText(params, flyTime, newFlyText)
    newFlyText.time = flyTime
    AddNewFlyTextToList(self, newFlyText, style)
    self:RecycleParam(params)
  end)
end

function DamageTextManager:InitFlyText(param, flyTime, flyText)
  local damage = param.damage or 0
  local position = param.position
  local style = param.style
  local damageType = param.damageType
  local isCritical = param.isCritical
  local combo = param.combo
  local req_trans = flyText.req_trans
  local numText = flyText.numText
  local critIcon = flyText.critIcon
  local simpleAnim = flyText.simpleAnim
  local prevCritIconShowState = flyText.critIconShowState
  local prevIconPath = flyText.iconPath
  local txt = ""
  local isShield = false
  local scale = 0.8
  local textColor = self.GetColor32(White)
  local spriteColor = White
  local iconPath = Crit_Path
  local animName = "fly"
  local hasOffset = false
  local showIcon = false
  local formattedDamage = ""
  if damage and type(damage) == "number" then
    formattedDamage = string.GetFormattedStr2(damage)
  else
    formattedDamage = damage
  end
  local yOffset = 0.1
  if style == DamageTextType.HeroNormalAttack then
    txt = "-" .. formattedDamage
    if isCritical then
      scale = 1.1
      showIcon = true
      txt = "-" .. formattedDamage .. "!"
    end
    local color = self.GetDamageTypeColor(damageType)
    textColor = self.GetColor32(color)
  elseif style == DamageTextType.ZombieNormalAttack then
    textColor = self.GetColor32(Red)
    spriteColor = Red
    txt = "-" .. formattedDamage
    if isCritical then
      scale = 1.1
      showIcon = true
      txt = "-" .. formattedDamage .. "!"
    end
  elseif style == DamageTextType.HeroUltimate then
    scale = 1.15
    hasOffset = true
    local color, icon = self.GetDamageTypeColor(damageType)
    textColor = self.GetColor32(color)
    showIcon = true
    iconPath = icon
    txt = "-" .. formattedDamage
    animName = "skill"
    if isCritical then
      animName = "CritSkill"
      txt = "-" .. formattedDamage .. "!"
      scale = 1.35
    end
  elseif style == DamageTextType.ZombieUltimate then
    textColor = self.GetColor32(Red)
    spriteColor = Red
    scale = 1.1
    animName = "skill"
    hasOffset = true
    txt = "-" .. formattedDamage
    if isCritical then
      showIcon = true
      txt = "-" .. formattedDamage .. "!"
    end
  elseif style == DamageTextType.GetBuff then
    scale = 1.5
    txt = param.txt or ""
    animName = "Default"
    local isDebuff = param.isDebuff
    if isDebuff then
      animName = "Debuff"
      textColor = self.GetColor32(DEBUFF_COLOR)
      spriteColor = White
    else
      animName = "Buff"
      textColor = self.GetColor32(BUFF_COLOR)
      spriteColor = White
    end
    local _iconPath = param.iconPath
    if not string.IsNullOrEmpty(_iconPath) then
      showIcon = true
      iconPath = _iconPath
    else
      showIcon = false
    end
    yOffset = 0
  elseif style == DamageTextType.ReduceDamageBuff then
    isShield = true
    iconPath = Shield_Path
    textColor = self.GetColor32(Cyan)
    spriteColor = White
    scale = 1.25
    animName = "hold"
    showIcon = true
    hasOffset = true
    txt = "-" .. formattedDamage
  elseif style == DamageTextType.Drone then
    scale = 1.2
    animName = "skill"
    hasOffset = true
    showIcon = true
    iconPath = TACTICAL_WEAPON_PATH
    local color = self.GetDamageTypeColor(damageType)
    textColor = self.GetColor32(color)
    spriteColor = color
    txt = "-" .. formattedDamage
  elseif style == DamageTextType.Dot then
    scale = 0.8
    animName = "Dot"
    hasOffset = true
    local color, icon = self.GetDamageTypeColor(damageType)
    textColor = self.GetColor32(color)
    showIcon = false
    txt = "-" .. formattedDamage
    if isCritical then
      txt = "-" .. formattedDamage .. "!"
      scale = 1.2
    end
  elseif style == DamageTextType.Miss then
    txt = "MISS"
  elseif style == DamageTextType.Splash then
    animName = "Dot"
    hasOffset = true
    scale = 0.6
    local color, icon = self.GetDamageTypeColor(damageType)
    textColor = self.GetColor32(color)
    showIcon = false
    txt = "-" .. formattedDamage
    if isCritical then
      txt = "-" .. formattedDamage .. "!"
      scale = 1.2
    end
  elseif style == DamageTextType.DisperseEffect then
    animName = "Default"
    scale = 1.5
    txt = param.txt or ""
    local isDebuff = param.isDebuff
    if isDebuff then
      animName = "Debuff"
      textColor = self.GetColor32(DEBUFF_COLOR)
      spriteColor = White
      iconPath = LuaEntry.DataConfig:TryGetStr("qusan_action", "k1")
      if combo and 1 < combo then
        txt = Localization:GetString("lw_action_qusan") .. "\195\151" .. combo
      else
        txt = Localization:GetString("lw_action_qusan")
      end
    else
      animName = "Buff"
      textColor = self.GetColor32(BUFF_COLOR)
      spriteColor = White
      iconPath = LuaEntry.DataConfig:TryGetStr("qusan_action", "k2")
      if combo and 1 < combo then
        txt = Localization:GetString("lw_buff_jinghua") .. "\195\151" .. combo
      else
        txt = Localization:GetString("lw_buff_jinghua")
      end
    end
    showIcon = true
  end
  simpleAnim:Stop()
  numText.text = txt
  numText.color32 = textColor
  req_trans:Set_localScale(scale, scale, scale)
  local posX = position.x
  local posY = position.y
  local posZ = position.z
  if hasOffset then
    posX = posX + math.random() * 3 - 2
    posY = posY + math.random() * 1.5 + 2
    req_trans:Set_localPosition(posX, posY, posZ)
  else
    req_trans:Set_localPosition(posX, posY, posZ)
  end
  req_trans:Set_localRotation(0, 0, 0, 1)
  if showIcon then
    if prevIconPath == nil or iconPath ~= prevIconPath then
      critIcon:LoadSprite(iconPath)
      flyText.iconPath = iconPath
    end
    critIcon.color = spriteColor
    local width = numText:GetPreferredValues(txt).x
    critIcon.transform:Set_localPosition(-0.6 - width / 2, yOffset, 0)
    if prevCritIconShowState == nil or prevCritIconShowState == false then
      critIcon.transform:Set_localScale(1, 1, 1)
    end
  elseif prevCritIconShowState == nil or prevCritIconShowState == true then
    critIcon.transform:Set_localScale(0, 0, 0)
  end
  flyText.critIconShowState = showIcon
  simpleAnim:Play(animName)
  if 0 < flyTime and flyTime ~= 1 then
    simpleAnim:SetStateSpeed(animName, 1 / flyTime)
  else
    simpleAnim:SetStateSpeed(animName, 1)
  end
end

function DamageTextManager:GetFlyTextFromPool(prefabPath)
  local pool = self.flyTextPool[prefabPath]
  if pool == nil then
    self.flyTextPool[prefabPath] = {}
    return nil
  end
  local count = #pool
  if 0 < count then
    local text = table.remove(pool, count)
    text.go:SetActive(true)
    return text
  end
  return nil
end

function DamageTextManager:FlyTextInPool(flyText)
  local prefabPath = flyText.prefabPath
  local pool = self.flyTextPool[prefabPath]
  if pool then
    table.insert(pool, flyText)
    flyText.go:SetActive(false)
  end
end

function DamageTextManager:OnUpdate()
  self.lastFrameTaskCount = self.curFrameTaskCount
  self.curFrameTaskCount = 0
  local deltaTime = Time.deltaTime
  if 0 >= self.flyTextList.length then
    return
  end
  for k, flyText in ilist(self.flyTextList) do
    local time = flyText.time
    time = time - deltaTime
    if time <= 0 then
      RemoveFlyTextFromList(self, flyText)
    else
      flyText.time = time
    end
  end
end

function DamageTextManager:RecycleParam(param)
  for k, v in pairs(param) do
    param[k] = nil
  end
  param.style = 0
  param.damage = ""
  self.paramsPool[#self.paramsPool + 1] = param
end

function DamageTextManager:GetParam()
  local param
  if #self.paramsPool > 0 then
    param = self.paramsPool[#self.paramsPool]
    self.paramsPool[#self.paramsPool] = nil
  else
    param = {}
  end
  return param
end

return DamageTextManager
