local base = require("Scene.LWBattle.ParkourBattle.Team.ParkourFormation")
local ParkourDynamicTeamFormation = BaseClass("ParkourDynamicTeamFormation", base)
local Const = require("Scene.LWBattle.ParkourBattle.Team.ParkourDynamicTeamConst")
local BattleConst = require("Scene.LWBattle.Const")

function ParkourDynamicTeamFormation:__init(defaultPosMax, defaultHero)
  self.defaultPosMax = defaultPosMax
  self.defaultHero = defaultHero
  self.formationPos = nil
  self.dynamicPos = nil
  self.formationPosCount = 0
  self.dynamicPosCount = 0
  self.posCount = 0
  self.dynamicSizeLevel = nil
  self.dynamicBasePosInfo = nil
end

function ParkourDynamicTeamFormation:__delete()
  self.defaultHero = nil
  self.dynamicPosMax = nil
  self.formationPos = nil
  self.dynamicPos = nil
  self.formationPosCount = nil
  self.dynamicPosCount = nil
  self.posCount = nil
  self.dynamicSizeLevel = nil
  self.dynamicBasePosInfo = nil
end

function ParkourDynamicTeamFormation:Init(initBattleFormation, backwards, defense, formationSpecialType)
  base.Init(self, initBattleFormation, backwards, defense, formationSpecialType)
  self.hasFormationPos = not initBattleFormation
  if self.hasFormationPos then
    self.formationSpecialType = formationSpecialType
    if formationSpecialType then
      self:ReInitHeroPos(initBattleFormation, formationSpecialType, true)
      self:SetFormationCount(#self.formationPos)
    else
      if initBattleFormation then
        self.formationPos = {
          [1] = Vector3.New(-1.8, 0, 2.5),
          [2] = Vector3.New(1.8, 0, 2.5),
          [3] = Vector3.New(-2.9, 0, 0),
          [4] = Vector3.New(0, 0, 0),
          [5] = Vector3.New(2.9, 0, 0)
        }
      else
        self.formationPos = {
          [1] = Vector3.New(-2.3, 0, 2.5),
          [2] = Vector3.New(2.3, 0, 2.5),
          [3] = Vector3.New(-3.3, 0, -1.5),
          [4] = Vector3.New(0, 0, -1.5),
          [5] = Vector3.New(3.3, 0, -1.5)
        }
      end
      self:SetFormationCount(5)
    end
    self.dynamicSizeLevel = Const.SizeLevel.Level1
    local dynamicPosType = Const.PosType.CenterHero
    self.dynamicBasePosInfo = Const.PosInfo[dynamicPosType]
    self.dynamicPosMax = math.min(self.defaultPosMax, #self.dynamicBasePosInfo)
  else
    self.dynamicSizeLevel = self:GetMaxSizeLevelByHeroStr(self.defaultHero)
    local dynamicPosType = Const.PosType.Normal
    self.dynamicBasePosInfo = Const.PosInfo[dynamicPosType]
    self.dynamicPosMax = math.min(self.defaultPosMax, #self.dynamicBasePosInfo)
  end
  self.dynamicPos = {}
  self:SetDynamicPosCount(0)
  self.canResize = self.dynamicPosCount <= self.dynamicPosMax
end

function ParkourDynamicTeamFormation:ChangeToBattleFormation()
  base.ChangeToBattleFormation(self)
  if string.IsNullOrEmpty(self.defaultHero) then
    self:ReSize(self.posCount, true)
  end
end

function ParkourDynamicTeamFormation:GetOffsetByIndex(index)
  base.GetOffsetByIndex(self, index)
  if index <= self.formationPosCount then
    return self.formationPos[index]
  end
  index = index - self.formationPosCount
  if index <= self.dynamicPosCount then
    return self.dynamicPos[index]
  end
  return self.dynamicPos[self.dynamicPosMax]
end

function ParkourDynamicTeamFormation:ReSize(index, force)
  base.ReSize(self, index, force)
  if not self.canResize then
    return self.posCount
  end
  if self.hasFormationPos then
    local addCount = index - self.formationPosCount
    if addCount <= 0 and not force then
      return self.formationPosCount
    end
    if force then
      if self.formationSpecialType then
        self:ReInitHeroPos(true, self.formationSpecialType, false)
      elseif self.formationPos == nil then
        self.formationPos = {
          [1] = Vector3.New(-1.8, 0, 2.5),
          [2] = Vector3.New(1.8, 0, 2.5),
          [3] = Vector3.New(-2.9, 0, 0),
          [4] = Vector3.New(0, 0, 0),
          [5] = Vector3.New(2.9, 0, 0)
        }
      else
        self.formationPos[1] = Vector3.New(-1.8, 0, 2.5)
        self.formationPos[2] = Vector3.New(1.8, 0, 2.5)
        self.formationPos[3] = Vector3.New(-2.9, 0, 0)
        self.formationPos[4] = Vector3.New(0, 0, 0)
        self.formationPos[5] = Vector3.New(2.9, 0, 0)
      end
      self:ResetDynamicBasePosByHeroPos()
    end
  end
  local dynamicIndex = math.min(index - self.formationPosCount, self.dynamicPosMax)
  if dynamicIndex > self.dynamicPosCount then
    local start = self.dynamicPosCount + 1
    local heroSize = Const.HeroSize[self.dynamicSizeLevel]
    for i = start, dynamicIndex do
      local posInfo = self.dynamicBasePosInfo[i]
      table.insert(self.dynamicPos, Vector3.New(posInfo[1] * heroSize, 0, posInfo[2] * heroSize))
    end
    self:SetDynamicPosCount(#self.dynamicPos)
  end
  self.canResize = self.dynamicPosCount <= self.dynamicPosMax
  return self.posCount
end

function ParkourDynamicTeamFormation:GetWeaponPos()
  base.GetWeaponPos(self)
  if self.formationPosCount > 0 then
    return self.formationPos[1]
  end
  return self.dynamicPos[1]
end

function ParkourDynamicTeamFormation:ReInitHeroPos(initBattleFormation, formationSpecialType, force)
  local formationPos = BattleConst.ParkourSpecialCircleTeamFormationPosition.UnInitBattleFormation[formationSpecialType]
  if initBattleFormation then
    formationPos = BattleConst.ParkourSpecialCircleTeamFormationPosition.InitBattleFormation[formationSpecialType]
  end
  if self.formationPos == nil or force then
    self.formationPos = {}
    for i = 1, #formationPos do
      self.formationPos[i] = formationPos[i]:Clone()
    end
  else
    for i = 1, #formationPos do
      self.formationPos[i] = formationPos[i]:Clone()
    end
  end
end

function ParkourDynamicTeamFormation:ResetDynamicPos()
  local posInfo = self.dynamicBasePosInfo
  local heroSize = Const.HeroSize[self.dynamicSizeLevel]
  for i, v in ipairs(self.dynamicPos) do
    local info = posInfo[i]
    v.x = info[1] * heroSize
    v.z = info[2] * heroSize
  end
end

function ParkourDynamicTeamFormation:GetMaxSizeLevelByHeroStr(hero)
  hero = tostring(hero)
  local split = string.split(hero, "|")
  local level = Const.SizeLevel.Level1
  for i, v in ipairs(split) do
    local lv = Const.HeroSizeLevel[v] or Const.SizeLevel.Level2
    if lv and level < lv then
      level = lv
    end
  end
  return level
end

function ParkourDynamicTeamFormation:GetSizeLevelByHeroId(hero)
  hero = tostring(hero)
  local level = Const.SizeLevel.Level1
  local lv = Const.HeroSizeLevel[hero]
  if lv and level < lv then
    level = lv
  end
  return level
end

function ParkourDynamicTeamFormation:OnReplaceAppearance(newHeroId)
  local newLevel = self:GetSizeLevelByHeroId(newHeroId)
  if self.dynamicSizeLevel and newLevel > self.dynamicSizeLevel then
    self.dynamicSizeLevel = newLevel
    self:ResetDynamicPos()
    return true
  end
  return false
end

local InRect = function(posInfo, rect)
  local min = rect.min
  local max = rect.max
  return posInfo[1] >= min[1] and posInfo[1] <= max[1] and posInfo[2] >= min[2] and posInfo[2] <= max[2]
end

function ParkourDynamicTeamFormation:ResetDynamicBasePosByHeroPos()
  local posSplitZ = {}
  for i, v in ipairs(self.formationPos) do
    if posSplitZ[v.z] == nil then
      posSplitZ[v.z] = {}
      posSplitZ[v.z].min = v.x
      posSplitZ[v.z].max = v.x
    elseif v.x < posSplitZ[v.z].min then
      posSplitZ[v.z].min = v.x
    elseif v.x > posSplitZ[v.z].max then
      posSplitZ[v.z].max = v.x
    end
  end
  local heroSize = Const.FormationHeroSize / 2
  local heroRects = {}
  for i, v in pairs(posSplitZ) do
    local rectMin = {
      v.min - heroSize,
      i - heroSize
    }
    local rectMax = {
      v.max + heroSize,
      i + heroSize
    }
    local rect = {}
    rect.min = rectMin
    rect.max = rectMax
    table.insert(heroRects, rect)
  end
  local newBaseInfo = {}
  for i, v in ipairs(self.dynamicBasePosInfo) do
    local canAdd = true
    for j, rect in ipairs(heroRects) do
      if InRect(v, rect) then
        canAdd = false
        break
      end
    end
    if canAdd then
      table.insert(newBaseInfo, v)
    end
  end
  self.dynamicBasePosInfo = newBaseInfo
  self.dynamicPosMax = math.min(self.defaultPosMax, #self.dynamicBasePosInfo)
end

function ParkourDynamicTeamFormation:SetFormationCount(count)
  self.formationPosCount = count
  self.posCount = self.formationPosCount + self.dynamicPosCount
end

function ParkourDynamicTeamFormation:SetDynamicPosCount(count)
  self.dynamicPosCount = count
  self.posCount = self.formationPosCount + self.dynamicPosCount
end

local GenDynamicPos = function()
  local size = 13
  local tab = CS.CSUtils.PoissonDiscSampler(1, size, size)
  local pos = {}
  for i = 0, tab.Length - 1 do
    local x = tab[i].x - size / 2
    local y = tab[i].y - size / 2
    if math.sqrt(x * x + y * y) <= size / 2 then
      table.insert(pos, {x, y})
    end
  end
  local distance = function(p)
    local dx = p[1]
    local dz = p[2]
    return math.sqrt(dx * dx + dz * dz)
  end
  table.sort(pos, function(a, b)
    return distance(a) < distance(b)
  end)
  return pos
end

function ParkourDynamicTeamFormation:ReGenDynamicPos(dynamicSizeLevel)
  self.dynamicBasePosInfo = GenDynamicPos(self)
  self.dynamicPosMax = math.min(self.defaultPosMax, #self.dynamicBasePosInfo)
  self:ResetDynamicPos()
end

return ParkourDynamicTeamFormation
