local BattleBuffHandle = require "app/module/battle/helper/battle_buff_handle"
local BattleConst = require "app/module/battle/battle_const"
local BattleHelper = require "app/module/battle/helper/battle_helper"

local BattleTeam = class("BattleTeam")

function BattleTeam:init(side, battleController)
	self.side = side
	self.battleController = battleController
	self.unitList = {}
	self.unitMap = {}
	self.buffList = {}
	self.sameBuffCount = {}
	self.sameBuffEffectNum = {} -- 相同的buff参数，用于判定buff数值上限
	self.shieldBuffList = {}
	self.loopFxMap = {}
	self.comboCount = 0
	self.cacheBuffDecr = {}
	self.cacheComboTag = {}
end

function BattleTeam:addUnit(unit, isMainUnit)
	unit:setTeam(self)
	self.unitMap[unit:getMatchType()] = unit
	table.insert(self.unitList, unit)
	if isMainUnit then
		self.mainUnit = unit
	end
end

function BattleTeam:checkPassiveEvent(...)
	for k, v in ipairs(self.unitList) do
		v:checkPassiveEvent(...)
	end
end

function BattleTeam:prepare()
	for k, v in ipairs(self.unitList) do
		v:prepare()
	end
end

function BattleTeam:getBuffList()
	return self.buffList
end

function BattleTeam:getMainUnit()
	return self.mainUnit
end

function BattleTeam:removeAllUnits()
	for k, v in pairs(self.unitMap) do
		self.unitMap[k] = nil
	end
	local count = #self.unitList
	for i = 1, count do
		self.unitList[i]:recycle()
		table.remove(self.unitList)
	end
	self.mainUnit = nil
end

function BattleTeam:useNormalSkill(matchType, count, isFinalAction, effectType, actionState, callback)
	self.isFinalAction = isFinalAction
	local unit = nil
	if matchType == nil then
		unit = self.unitList[1]
	else
		unit = self.unitMap[matchType]
	end
    if unit == nil then
        return callback()
    end
	if unit:getIsLimit() then
		return callback()
	end
	self.mainUnit = unit
	unit:beforeAttack(actionState)
	unit:resetBeforeAttack()
	unit:useNormalSkill(count, effectType, callback)
end

function BattleTeam:useSkill(matchType, count, isFinalAction, effectType, actionState, callback)
	self.isFinalAction = isFinalAction
	local unit = nil
	if matchType == nil then
		unit = self.unitList[1]
	else
		unit = self.unitMap[matchType]
	end
    if unit == nil then
        return callback()
    end
	if unit:getIsLimit() then
		return callback()
	end
	local lastMainUnit = self.mainUnit
	self.mainUnit = unit
	unit:beforeAttack(actionState)
	unit:resetBeforeAttack()
	if unit:useSkill(1, count, callback) then
		if lastMainUnit and lastMainUnit ~= unit then
			lastMainUnit:playSwitchOut()
		end
	else
		if lastMainUnit and lastMainUnit ~= unit then
			self.mainUnit = lastMainUnit
		end
	end
end

-- function BattleTeam:useAssistingSkill(assistingList, isFinalAction, callback)
-- 	self.isFinalAction = isFinalAction
-- 	if self.mainUnit == nil then
-- 		return callback()
-- 	end
-- 	if self.mainUnit:getIsLimit() then
-- 		return callback()
-- 	end
-- 	local count = #assistingList
-- 	if count <= 0 then
-- 		return callback()
-- 	end
-- 	self:setCentralizedAttack(true)
-- 	local function finish()
-- 		count = count - 1
-- 		if count == 0 then
-- 			self:setCentralizedAttack(false)
-- 			callback()
-- 		end
-- 	end
-- 	local delay = 0
-- 	for _, v in ipairs(assistingList) do
-- 		local unit = self.unitMap[v.skillMatch]
-- 		if unit then
-- 			unit:resetBeforeAttack()
-- 			unit:useAssistingSkill(v.count, delay, finish)
-- 			delay = delay + BattleHelper:getSupportInterval()
-- 		else
-- 			finish()
-- 		end
-- 	end
-- end

function BattleTeam:mainUnitUseAllSkills(actionState, callback)
	self.isFinalAction = true
	if self.mainUnit == nil then
		return callback()
	end
	if self.mainUnit:getIsLimit() then
		return callback()
	end
	self.mainUnit:beforeAttack(actionState)
	self.mainUnit:resetBeforeAttack()
	self.mainUnit:useAllSkills(callback)
end

function BattleTeam:changeMainUnit(matchType)
	if self.mainUnit and matchType == self.mainUnit:getMatchType() then
		return
	end
	local unit = self.unitMap[matchType]
	if unit == nil then
		return
	end
	self.mainUnit:playSwitchOut()
	self.mainUnit = unit
	unit:playSwitchIn()
end

function BattleTeam:changeDead()
	if not self.mainUnit then
		return
	end
	self.mainUnit:changeState(BattleConst.UNIT_STATE.DEAD)
end

function BattleTeam:playHurt()
	if not self.mainUnit then
		return
	end
	self.mainUnit:playHurt()
end

function BattleTeam:getMainUnitLocalPosition(backup)
	if not self.mainUnit then
		return backup.baseObject:fastGetLocalPosition()
	end
	return self.mainUnit.baseObject:fastGetLocalPosition()
end

-- 回合结束的时候要结算buff和技能
function BattleTeam:onRoundEnd()
	for k, v in ipairs(self.unitList) do
		v:onRoundEnd()
	end
	self:doBuffWork()
	self:doFinalBuffWork()
	self.comboCount = 0
	if self:getMainUnit():isInitState() then
		return
	end
	self:getMainUnit():changeState(BattleConst.UNIT_STATE.IDLE)
end

function BattleTeam:addShield(buffEffect)
	if buffEffect then
		table.insert(self.shieldBuffList, buffEffect)
	end
end

function BattleTeam:addInvincibleShield(buffEffect)
	if buffEffect then
		table.insert(self.shieldBuffList, 1, buffEffect)
	end
end

function BattleTeam:removeShield(buffEffect)
	for k, v in ipairs(self.shieldBuffList) do
		if v == buffEffect then
			table.remove(self.shieldBuffList, k)
			break
		end
	end
end

function BattleTeam:handleShield(reduceShield, unit)
	if #self.shieldBuffList <= 0 then
		return
	end

	local needReedRefreshBuff = false
	local currShieldBuff = self.shieldBuffList[1]
	while currShieldBuff do
		if reduceShield <= 0 and currShieldBuff.buff and currShieldBuff.buff:getName() == BattleConst.BUFF_NAME.INVINCIBLE_SHIELD then
			reduceShield = currShieldBuff.result - 1
			needReedRefreshBuff = true
		else
			reduceShield = reduceShield + currShieldBuff.result
		end

		if reduceShield > 0 then
			currShieldBuff.result = reduceShield
			reduceShield = 0
			break
		else
			currShieldBuff.result = 0
			for k, v in ipairs(self.buffList) do
				if v == currShieldBuff then
					if not needReedRefreshBuff and currShieldBuff.buff:getIcon() then
						needReedRefreshBuff = true
					end
					self:updateBuffState(currShieldBuff.buff, -1)
					table.remove(self.buffList, k)
					BattleBuffHandle.removeBuff(unit, currShieldBuff)
					currShieldBuff = nil
					break
				end
			end
			if currShieldBuff then
				table.remove(self.shieldBuffList, 1)
			end
		end
		currShieldBuff = self.shieldBuffList[1]
	end
	if needReedRefreshBuff then
		self.battleController:refreshBuff(self.side, self.buffList)
	end
	self:calSameBuffEffectNum()
end

function BattleTeam:addBuff(buffEffect)
	if not self:judgeBuffEffectEffectNumOverflow(buffEffect.buff) then
		return
	end

	local stack = buffEffect.buff:getStack()
	local needRecycle
	if not stack or stack == BattleConst.BUFF_STACK_TYPE.CANT_ADD then
		local buffName = buffEffect.buff:getName()
		local buffNum = self.sameBuffCount[buffName]
		if buffNum and buffNum > 0 then
			for _, bEffect in ipairs(self.buffList) do
				if bEffect.buff:getName() == buffName then
					if bEffect.round < buffEffect.round then
						bEffect.round = buffEffect.round
						for fieldName, v in pairs(bEffect) do
							if fieldName ~= "buff" and fieldName ~= "round" and fieldName ~= "result" then
								bEffect[fieldName] = buffEffect[fieldName]
							end
						end
						if bEffect.buff:getEffectNum() < buffEffect.buff:getEffectNum() then
							bEffect.buff:setEffectNum(buffEffect.buff:getEffectNum())
						end
						needRecycle = bEffect
						break
					elseif bEffect.buff:getEffectNum() < buffEffect.buff:getEffectNum() then
						bEffect.buff:setEffectNum(buffEffect.buff:getEffectNum())
						needRecycle = bEffect
						break
					else
						return bEffect
					end
				end
			end
		end
	elseif stack == BattleConst.BUFF_STACK_TYPE.ADD_ROUND then
		local buffName = buffEffect.buff:getName()
		local buffNum = self.sameBuffCount[buffName]
		if buffNum and buffNum > 0 then
			for _, bEffect in ipairs(self.buffList) do
				if bEffect.buff:getName() == buffName then
					bEffect.round = bEffect.round + buffEffect.round
					for fieldName, v in pairs(bEffect) do
						if fieldName ~= "buff" and fieldName ~= "round" and fieldName ~= "result" then
							bEffect[fieldName] = buffEffect[fieldName]
						end
					end
					if bEffect.buff:getEffectNum() < buffEffect.buff:getEffectNum() then
						bEffect.buff:setEffectNum(buffEffect.buff:getEffectNum())
					end
					needRecycle = bEffect
					break
				end
			end
		end
	elseif stack == BattleConst.BUFF_STACK_TYPE.ADD then

	elseif stack == BattleConst.BUFF_STACK_TYPE.REMOVE_LAST then
		local buffName = buffEffect.buff:getName()
		self:removeBuffByName(buffName)
	end

	if needRecycle then
		BattleHelper:recycleBuffEffect(buffEffect)
	else
		table.insert(self.buffList, buffEffect)
		self:updateBuffState(buffEffect.buff, 1)
		needRecycle = buffEffect
	end

	if buffEffect.buff:getIcon() then
		self.battleController:refreshBuff(self.side, self.buffList)
	end

	self:calSameBuffEffectNum()
	return needRecycle
end

function BattleTeam:removeAllBuff()
	local buffEffect = nil
	local count = #self.buffList
	for i = count, 1, -1 do
		buffEffect = self.buffList[i]
		if buffEffect then
			self:updateBuffState(buffEffect.buff, -1)
			table.remove(self.buffList, i)
			BattleBuffHandle.removeBuff(self.mainUnit, buffEffect)
		end
	end

	if self.cacheBuffDecr then
		for decr, list in pairs(self.cacheBuffDecr) do
			count = #list
			for i = count, 1, -1 do
				buffEffect = self.cacheBuffDecr[decr][i]
				table.remove(self.cacheBuffDecr[decr], i)
				BattleHelper:recycleBuffEffect(buffEffect)
			end
		end
	end

	self.battleController:clearBuff(self.side)
	self:calSameBuffEffectNum()
end

function BattleTeam:putCacheBuff(buffEffect)
	local buffDecr = buffEffect.buff:getDecr()
	if not self.cacheBuffDecr[buffDecr] then
		self.cacheBuffDecr[buffDecr] = {}
	end
	table.insert(self.cacheBuffDecr[buffDecr], buffEffect)
end

function BattleTeam:putCacheBuffByDecr(buffDecr)
	if not buffDecr then
		return
	end

	local needRefresh = false
	local buffEffect = nil
	local count = #self.buffList
	for i = count, 1, -1 do
		buffEffect = self.buffList[i]
		if buffEffect and buffEffect.buff:getDecr() == buffDecr and not buffEffect.buff:isCantRemove() then
			self:updateBuffState(buffEffect.buff, -1)
			table.remove(self.buffList, i)
			BattleBuffHandle.removeBuff(self.mainUnit, buffEffect, true)
			if not self.cacheBuffDecr[buffDecr] then
				self.cacheBuffDecr[buffDecr] = {}
			end

			table.insert(self.cacheBuffDecr[buffDecr], buffEffect)
			if buffEffect.buff:getIcon() then
				needRefresh = true
			end
		end
	end

	if needRefresh then
		self.battleController:refreshBuff(self.side, self.buffList)
	end

	self:calSameBuffEffectNum()
end

function BattleTeam:popCacheBuffByDecr(buffDecr)
	if not buffDecr or not self.cacheBuffDecr or not self.cacheBuffDecr[buffDecr] then
		return
	end

	local list = self.cacheBuffDecr[buffDecr]

	local needRefresh = false
	local count = #list
	local buffEffect
	for i = count, 1, -1 do
		buffEffect = self.cacheBuffDecr[buffDecr][i]
		self:getMainUnit():reTakeEffectByBuffEffect(buffEffect)
		table.remove(self.cacheBuffDecr[buffDecr])
		if buffEffect.buff:getIcon() then
			needRefresh = true
		end
	end

	if needRefresh then
		self.battleController:refreshBuff(self.side, self.buffList)
	end

	self:calSameBuffEffectNum()
end

function BattleTeam:calSameBuffEffectNum()
	if not self.buffList then
		return
	end
	self.sameBuffEffectNum = table.clearOrCreate(self.sameBuffEffectNum)
	for _, buffEffect in ipairs(self.buffList) do
		local buffEntity = buffEffect.buff
		if buffEntity then
			local buffName = buffEntity:getName()
			local buffNum = buffEntity:getEffectNum()
			if buffName and buffNum then
				self.sameBuffEffectNum[buffName] = (self.sameBuffEffectNum[buffName] or 0) + buffNum
			end
		end
	end
end

function BattleTeam:judgeBuffEffectEffectNumOverflow(buffEntity)
	if not buffEntity then
		return true
	end
	local limitNum = buffEntity:getLimitParameter()
	if not limitNum then
		return true
	end

	local buffName = buffEntity:getName()
	local buffNum = buffEntity:getEffectNum()
	local curNum = self.sameBuffEffectNum[buffName] or 0
	local remainNum = limitNum - curNum
	if remainNum <= 0 then -- 已经超出上限了，直接返回失败
		return false
	end

	if buffNum > remainNum then
		buffEntity:setEffectNum(remainNum)
	end
	return true
end

function BattleTeam:doBuffWork()
	local count = nil
	local buffEffect = nil
	if self.cacheBuffDecr then
		for decr, list in pairs(self.cacheBuffDecr) do
			count = #list
			for i = count, 1, -1 do
				buffEffect = self.cacheBuffDecr[decr][i]
				buffEffect.round = buffEffect.round - 1
				if buffEffect.round <= 0 then
					table.remove(self.cacheBuffDecr[decr], i)
					BattleHelper:recycleBuffEffect(buffEffect)
				end
			end
		end
	end

	count = #self.buffList
	if count <= 0 then
		return
	end
	buffEffect = nil
	for i = count, 1, -1 do
		buffEffect = self.buffList[i]
		if not buffEffect then
			break
		end
		if not BattleConst.FINAL_WORK_BUFF[buffEffect.buff:getName()] then
			buffEffect.round = buffEffect.round - 1
			local target  = self.mainUnit
			if buffEffect.target then
				target = buffEffect.target
			end
			BattleBuffHandle.doBuffWork(target, buffEffect)
			if not self.buffList[i] then -- dot伤害致死后，buff已经全部移除
				break
			end
			if buffEffect.round <= 0 then
				self:updateBuffState(buffEffect.buff, -1)
				table.remove(self.buffList, i)
				BattleBuffHandle.removeBuff(target, buffEffect)
			end
		end
	end
	self.battleController:refreshBuff(self.side, self.buffList)

	self:calSameBuffEffectNum()
end

-- 比如复活类，需要最后触发buff
function BattleTeam:doFinalBuffWork()
	if not self:getMainUnit() or self:getMainUnit().unitEntity:getIsDead() or self:getMainUnit().unitEntity:getIsRebirth() then
		return
	end

	local count = nil
	local buffEffect = nil
	count = #self.buffList
	if count <= 0 then
		return
	end
	local refreshUI = false
	for i = count, 1, -1 do
		buffEffect = self.buffList[i]
		if not buffEffect then
			break
		end
		if BattleConst.FINAL_WORK_BUFF[buffEffect.buff:getName()] then
			refreshUI = true
			buffEffect.round = buffEffect.round - 1
			local target  = self.mainUnit
			if buffEffect.target then
				target = buffEffect.target
			end
			BattleBuffHandle.doBuffWork(target, buffEffect)
			if not self.buffList[i] then -- dot伤害致死后，buff已经全部移除
				break
			end
			if buffEffect.round <= 0 then
				self:updateBuffState(buffEffect.buff, -1)
				table.remove(self.buffList, i)
				BattleBuffHandle.removeBuff(target, buffEffect)
			end
		end
	end
	if refreshUI then
		self.battleController:refreshBuff(self.side, self.buffList)
	end
	self:calSameBuffEffectNum()
end

function BattleTeam:removeBuffByName(buffName)
	local count = #self.buffList
	if count <= 0 then
		return
	end
	local buffEffect = nil
	for i = count, 1, -1 do
		buffEffect = self.buffList[i]
		if buffEffect.buff:getName() == buffName and not buffEffect.buff:isCantRemove() then
			self:updateBuffState(buffEffect.buff, -1)
			table.remove(self.buffList, i)
			BattleBuffHandle.removeBuff(self.mainUnit, buffEffect)
		end
	end
	self.battleController:refreshBuff(self.side, self.buffList)
	self:calSameBuffEffectNum()
end

function BattleTeam:removeBuffByDecr(buffDecr)
	local count = #self.buffList
	if count <= 0 then
		return
	end
	local buffEffect = nil
	for i = count, 1, -1 do
		buffEffect = self.buffList[i]
		if buffEffect.buff:getDecr() == buffDecr and not buffEffect.buff:isCantRemove() then
			self:updateBuffState(buffEffect.buff, -1)
			table.remove(self.buffList, i)
			BattleBuffHandle.removeBuff(self.mainUnit, buffEffect)
		end
	end
	self.battleController:refreshBuff(self.side, self.buffList)
	self:calSameBuffEffectNum()
end

function BattleTeam:removeAllShield()
	local count = nil
	count = #self.buffList
	if count <= 0 then
		return
	end
	local buffEffect = nil
	for i = count, 1, -1 do
		buffEffect = self.buffList[i]
		if not buffEffect then
			break
		end
		local buff = buffEffect.buff
		if buff:isShield() and not buff:isCantRemove()  then
			local target  = self.mainUnit
			self:updateBuffState(buff, -1)
			table.remove(self.buffList, i)
			BattleBuffHandle.removeBuff(target, buffEffect)
		end
	end
	self.battleController:refreshBuff(self.side, self.buffList)

	self:calSameBuffEffectNum()
end

function BattleTeam:updateBuffState(buff, num)
	local buffName = buff:getName()
	local buffNum = (self.sameBuffCount[buffName] or 0) + num
	self.sameBuffCount[buffName] = buffNum
	if buffNum == 0 then -- 移除buff
		local fxList = buff:getFxContinued()
		if fxList then
			local fxCfg = BattleHelper:getFxConfig()
			for k, v in ipairs(fxList) do
				local fxInfo = fxCfg[v]
				if fxInfo then
					local res = fxInfo.res
					local count = self.loopFxMap[res] or 0
					self.loopFxMap[res] = count - 1
					if count == 1 then
						for k2, v2 in ipairs(self.unitList) do
							v2:removeFx(res)
						end
					end
				end
			end
		end
		local fxDisappear = buff:getFxDisappear()
		if fxDisappear then
			local fxCfg = BattleHelper:getFxConfig()
			for k, v in ipairs(fxDisappear) do
				local fxInfo = fxCfg[v]
				if fxInfo then
					self.mainUnit:getEffectAndPlay(fxInfo, false)
				end
			end
		end
	elseif buffNum == 1 then -- 新添加buff
		local fxList = buff:getFxContinued()
		if fxList then
			local fxCfg = BattleHelper:getFxConfig()
			for k, v in ipairs(fxList) do
				local fxInfo = fxCfg[v]
				if fxInfo then
					local res = fxInfo.res
					local count = self.loopFxMap[res] or 0
					self.loopFxMap[res] = count + 1
					if count == 0 then
						for k2, v2 in ipairs(self.unitList) do
							v2:getEffectAndPlay(fxInfo, true)
						end
					end
				end
			end
		end
	end
end

function BattleTeam:getBuffCountByName(buffName)
	return self.sameBuffCount[buffName] or 0
end

function BattleTeam:getLoopFxResCount(res)
	return self.loopFxMap[res] or 0
end

function BattleTeam:getUnitComp()
	return self.unitMap
end

function BattleTeam:getIsBoss()
	if self.mainUnit == nil then
		return false
	end
	return self.mainUnit:getIsBoss()
end

function BattleTeam:playRunAction()
	if self.mainUnit then
		self.mainUnit:playRunAction()
	end
end

function BattleTeam:stopRunAction()
	if self.mainUnit then
		self.mainUnit:stopRunAction()
	end
end

function BattleTeam:recoverHpOnWaveOver(callback)
	if self.mainUnit then
		self.mainUnit:recoverHpOnWaveOver(callback)
	else
		callback()
	end
end

function BattleTeam:getCentralizedAttack()
	return self.centralizedAttack
end

function BattleTeam:setCentralizedAttack(centralizedAttack)
	if not self.isFinalAction and not centralizedAttack then
		return
	end
	self.centralizedAttack = centralizedAttack
end

function BattleTeam:setIsFinalBlock(isFinalBlock)
	if not self.isFinalBlock and not isFinalBlock then
		return
	end
	self.isFinalBlock = isFinalBlock
end

function BattleTeam:getIsFinalBlock()
	if self.isFinalBlock == nil then
		return true
	end

	return self.isFinalBlock
end

function BattleTeam:addCombo()
	if self.side ~= BattleConst.SIDE_ATK then
		if not BattleConst.IS_PVP_BATTLE[self.battleController.battleType] then
			return
		end
	end

	self.comboCount = self.comboCount + 1
	self.battleController:showCombo(self.comboCount, self.side)

	if self.side == BattleConst.SIDE_ATK then -- 任务
		if self.comboCount == 10 then
			self.battleController:addTaskProgress(BattleConst.BATTLE_TASK_FIELD.COMBO_OVER_10, 1)
		end
	end
end

function BattleTeam:addHurtComboTag(matchType)
	self.cacheComboTag[matchType] = (self.cacheComboTag[matchType] or 0) + 1
end

function BattleTeam:reduceHurtComboTag(matchType)
	if not self.cacheComboTag[matchType] or self.cacheComboTag[matchType] <= 0 then
		return
	end
	self.cacheComboTag[matchType] = (self.cacheComboTag[matchType] or 0) - 1
end

function BattleTeam:getHurtComboTag(matchType)
	local combotag = self.cacheComboTag[matchType]
	if not combotag or combotag <= 0 then
		combotag = self.cacheComboTag[BattleConst.COMBO_DEFAULT_POSITION] -- 全部
	end
	if not combotag or combotag <= 0 then
		return false
	end
	return true
end

function BattleTeam:clearHurtComboTag()
	table.clear(self.cacheComboTag)
end

function BattleTeam:onActionOver()
	self:setIsFinalBlock(true)
	self:setCentralizedAttack(false)
	self:clearHurtComboTag()
	-- 处理反击
	local counterAttackCount = self:getMainUnit().unitEntity:getCounterAttackCount()
	if counterAttackCount <= 0 then
		return
	end
	self:getMainUnit().unitEntity:clearCounterAttackCount()

	local teamAction = function()
		---- 普攻
		local skillMatch
		if self.side == BattleConst.SIDE_ATK then
			self.battleController.roundStep = BattleConst.BATTLE_ROUND_STEP.ON_ATK_STEP
			skillMatch = self:getMainUnit().unitEntity:getMatchType()
		else
			self.battleController.roundStep = BattleConst.BATTLE_ROUND_STEP.ON_DEF_STEP
			skillMatch = self:getMainUnit().unitEntity:getMatchType()
		end

		self:useNormalSkill(skillMatch, counterAttackCount, true, BattleConst.EFFECT_TYPE.DIRECT, BattleConst.ATTACK_ACTION_STATE.COUNTERATTACK, function()
			self.battleController:enterNextTeamAction()
		end)
	end
	self.battleController:addTeamActionList(teamAction, 1)
	self.comboCount = 0
end

function BattleTeam:tick(dt)
	for k, v in ipairs(self.unitList) do
		v:tick(dt)
	end
end

return BattleTeam