local BattleController = require "app/module/battle/controller/battle_controller"
local BattleControllerDungeonRune = class("BattleControllerDungeonRune", BattleController)
local BattleConst = GConst.BattleConst
local SIDE_ATK = BattleConst.SIDE_ATK
local SIDE_DEF = BattleConst.SIDE_DEF
local PASSIVE_EVENT = BattleConst.PASSIVE_EVENT
local UNIT_STATE = BattleConst.UNIT_STATE

function BattleControllerDungeonRune:getBoardConfig()
    return ConfigManager:getConfig("chapter_board_rune")
end

function BattleControllerDungeonRune:getChapterConfig()
    return ConfigManager:getConfig("chapter_dungeon_rune")
end

function BattleControllerDungeonRune:getChapterId()
    local runeData = DataManager.DungeonData:getDungeonDataByType(ModuleManager.MODULE_KEY.RUNES_OPEN)
    return runeData:getCurFightChapterId()
end

function BattleControllerDungeonRune:initOther()
    local runeData = DataManager.DungeonData:getDungeonDataByType(ModuleManager.MODULE_KEY.RUNES_OPEN)
    self.canRebirthTimes = 1 -- 每次只能复活一次
    self.addRoundCount = runeData:getRebirthAddRoundCount()
    self.dungeonRuneRemainRoundCount = self:getChapterConfig()[self.chapterId].round or 1
    self.dungeonRuneMaxRoundCount = self.dungeonRuneRemainRoundCount

    if self.battleUI then
        local desc = self.dungeonRuneRemainRoundCount
        self.battleUI:refreshWave(desc, GConst.ATLAS_PATH.COMMON, "common_dec_15")
    end

    self.runeMaxLv = runeData:getBattleMaxlv() or 1
    self.battleData:setMaxBattleLv(self.runeMaxLv)
end

function BattleControllerDungeonRune:addBattleExp(...)
    if self.battleData:getBattleLv() >= self.runeMaxLv then
        return
    end

    BattleController.addBattleExp(self, ...)
end

-- 怪物攻击力固定
function BattleControllerDungeonRune:getMonsterAtkFixed()
	if not self.monsterAtkFixed then
        self.monsterAtkFixed = self:getChapterConfig()[self.chapterId].atk or 0
    end
    return self.monsterAtkFixed
end

-- 怪物血量固定
function BattleControllerDungeonRune:getMonsterHpFixed()
	if not self.monsterHpFixed then
        self.monsterHpFixed = self:getChapterConfig()[self.chapterId].hp or 0
    end
    return self.monsterHpFixed
end

function BattleControllerDungeonRune:getMaxWave()
    self:getInitBoard()
    if not self.cacheMaxWave then
        self.cacheMaxWave = #self.monsterList
        if self.cacheMaxWave > 1 then
            self.cacheMaxWave = 9999999 -- 无限波次
        end
    end
    return self.cacheMaxWave
end

function BattleControllerDungeonRune:enterNextWave()
    local waveIndex = self:getWaveIndex()
    if waveIndex ~= 0 and self.curWaveMonsterDead then
        if self.isBossWave then
            self:addTaskProgress(BattleConst.BATTLE_TASK_FIELD.KILL_BOSS, 1)
        else
            self:addTaskProgress(BattleConst.BATTLE_TASK_FIELD.KILL_NORMAL_MONSTER, 1)
        end
        self:setTaskProgress(BattleConst.BATTLE_TASK_FIELD.PASS_WAVE, waveIndex)
    end

    -- 完成任务
    local taskOver = self:getRuneTaskAllOver()
    if self.curWaveMonsterDead and taskOver then
        self.victory = true
        self:postWaveOver(false)
        self:battleEnd()
        return
    end

    if waveIndex >= self.maxWaveIndex then
        self.victory = self.curWaveMonsterDead
        self:postWaveOver(not self.curWaveMonsterDead)
        self:battleEnd()
        return
    end

    local atkTeam = self.battleData:getAtkTeam()
    if not atkTeam or atkTeam:getIsDead() then
        if self:tryShowRebirth(function() self:onEnterNextWave() end) then
            return
        end

        self.victory = false
        self:postWaveOver(true)
        self:battleEnd()
        return
    end

    self:onEnterNextWave()
end

function BattleControllerDungeonRune:onEnterNextWave()
    local waveIndex = self:getWaveIndex()
    if waveIndex ~= 0 then -- 第一波
        self:postWaveOver(false)
    end

    self.curWaveMonsterDead = false
    self:addWaveIndex(1)
    waveIndex = self:getWaveIndex()
    self:refreshWave(waveIndex)
    if waveIndex == 1 then -- 第一波
        self.needWaitingBoardOver = true
        self:generateBoard(true)
    else
        -- 上报关卡结束
        self.defTeam:prepare()
    end

    self.waveDurationTime = 0
    self.eliminateCount = 0

    self:snapshotBattleInfo()

    self.isBossWave = self.defTeam:getMainUnit().unitEntity:getIsBoss()
    self:postFightStart()
    if not self.needWaitingBoardOver then
        self:enterRoundBegin()
    end
    if not self.isBossWave then
        self.battleUI:hideBuffTips()
    end
end

function BattleControllerDungeonRune:enterRoundBegin()
    if self.battleUI.refreshTaskNode then
        self.battleUI:refreshTaskNode()
    end
    -- 完成任务
    local taskOver = self:getRuneTaskAllOver()
    if taskOver and not self.isBossChapter then
        self.needWaitingBoardOver = nil
        self.victory = true
        self:postWaveOver(false)
        self:battleEnd()
        return
    end
    if self.dungeonRuneRemainRoundCount <= 0 then
        if self.canRebirthTimes > 0 and not self.isBossChapter then
            ModuleManager.DungeonRuneManager:showRebirthUI(function()
                self.canRebirthTimes = self.canRebirthTimes - 1
                local mainUnit = self.atkTeam:getMainUnit()
                mainUnit:rebirth()
                local hp = mainUnit.unitEntity:getMaxHp() * 5000 // GConst.BattleConst.DEFAULT_FACTOR -- 回血50%
                local x = mainUnit:takeDamageOrCure(mainUnit, hp, GConst.BattleConst.EFFECT_TYPE.HEAL, 0, BattleConst.SPECIAL_DAMAGE_OR_CURE_TYPE.AD_REBIRTH)
                local hpPercent = mainUnit.unitEntity:getHpPercent()
                self:refreshHp(mainUnit:getSide(), mainUnit.unitEntity:getHp(), hpPercent)
                self.atkTeam:checkPassiveEvent(PASSIVE_EVENT.HP_LOWER_THAN, mainUnit, hpPercent)
                mainUnit:changeState(UNIT_STATE.IDLE)
                mainUnit:initPosition()

                self.dungeonRuneRemainRoundCount = self.dungeonRuneRemainRoundCount + self.addRoundCount
                self.dungeonRuneMaxRoundCount = self.dungeonRuneMaxRoundCount + self.addRoundCount
                self.dungeonRuneRemainRoundCount = self.dungeonRuneRemainRoundCount - 1
                if self.battleUI then
                    local desc = self.dungeonRuneRemainRoundCount + 1
                    self.battleUI:refreshWave(desc, GConst.ATLAS_PATH.COMMON, "common_dec_15")
                end
                BattleController.enterRoundBegin(self)
            end, function()
                self.victory = false
                self:postWaveOver(false)
                self:battleEnd()
            end, false)
        else
            self.victory = false
            self:postWaveOver(false)
            self:battleEnd()
        end
        return
    end

    self.dungeonRuneRemainRoundCount = self.dungeonRuneRemainRoundCount - 1
    if self.isBossChapter and self.dungeonRuneRemainRoundCount <= 4 then
        ModuleManager.DungeonRuneManager:showRoundTipUI(self.dungeonRuneRemainRoundCount + 1)
    end
    if self.battleUI then
        local desc = self.dungeonRuneRemainRoundCount + 1
        self.battleUI:refreshWave(desc, GConst.ATLAS_PATH.COMMON, "common_dec_15")
    end
    BattleController.enterRoundBegin(self)
end

function BattleControllerDungeonRune:onLinkOverDone(...)
    if self.battleUI.refreshTaskNode then
        self.battleUI:refreshTaskNode()
    end
    BattleController.onLinkOverDone(self, ...)
end

function BattleControllerDungeonRune:getNextMonsterId(waveIndex)
    self:getInitBoard()
    waveIndex = waveIndex or self:getWaveIndex() + 1
    local monsterIndex = math.random(1, #self.monsterList)
    return self.monsterList[monsterIndex]
end

function BattleControllerDungeonRune:getInitBoard()
    if not self.boradList then
        self.boradList = {}
        self.fixedRandomGrid = {}
        self.mysteryBoxIndexMap = {}
        self.monsterList = {}
        self.elementWeightMap = {}
        self.isBossChapter = false

        local boardCfg = self:getBoardConfig()
        local info = self:getChapterConfig()[self.chapterId]
        if info then
            self.monsterList = GFunc.getTable(info.monster)
            local cfg = boardCfg[info.chapter_board]
            if cfg then
                table.insert(self.boradList, {board = GFunc.getTable(cfg.board), gridEdge = GFunc.getTable(cfg.grid_edge)})
            end
            if info.weights then
                for index, v in ipairs(info.weights) do
                    self.elementWeightMap[v[1]] = v[2]
                end
            end
        end
        if #self.monsterList <= 1 then
            self.isBossChapter = true
        end
    end

    return self.boradList, self.fixedRandomGrid, self.mysteryBoxIndexMap
end

function BattleControllerDungeonRune:refreshWave()
    if not self.battleUI then
        return
    end
    -- local desc = self.dungeonRuneRemainRoundCount + 1
    -- self.battleUI:refreshWave(desc, GConst.ATLAS_PATH.COMMON, "common_dec_15")
end

function BattleControllerDungeonRune:getRandomGridInfo()
    local list, fixedRandomGrid = self:getInitBoard()
    local fixedRandomList = fixedRandomGrid[self.curBoardIndex]

    local gridType = 0
    local elementType
    if fixedRandomList and fixedRandomList[1] then
        elementType = table.remove(fixedRandomList, 1)
    else
        local map = self.battleData:getElementTypeMap()
        self.getRandomGridInfoIndexs = table.clearOrCreate(self.getRandomGridInfoIndexs)
        self.getRandomGridInfoTypeList = table.clearOrCreate(self.getRandomGridInfoTypeList)
        for typeName, typeNum in pairs(BattleConst.ELEMENT_TYPE) do
            if not self:getSealElementType()[typeNum] and self:getSkillEntityByElement(typeNum) then
                local weight = 100
                if self.elementWeightMap and self.elementWeightMap[typeNum] then
                    weight = weight + self.elementWeightMap[typeNum]
                end
                table.insert(self.getRandomGridInfoIndexs, weight)
                table.insert(self.getRandomGridInfoTypeList, typeNum)
            end
        end

        local index = GFunc.getRandomIndex(self.getRandomGridInfoIndexs)
        elementType = self.getRandomGridInfoTypeList[index]
    end

    return {gridType = gridType, elementType = elementType}
end

function BattleControllerDungeonRune:onLoadComplete(...)
    -- 处理技能
    local unlockAllSkill = function(side)
        local skillPool = self.battleData:getSkillPool(side)
        if skillPool then
            for elementType, list in pairs(skillPool) do -- 先遍历一下未解锁的技能
                if not self.battleData:isUnlockedSkillElementType(elementType, side) then
                    local skillEntity = self.battleData:getSkillEntityByElement(elementType, side)
                    if skillEntity then
                        local skillId = skillEntity:getUnlockId()
                        if skillId then
                            self:dealSelectSkill(skillId, nil, side)
                        end
                    end
                    for _, skillId in ipairs(list) do
                        self:dealSelectSkill(skillId, nil, side)
                    end
                end
            end
            self.battleUI:refreshSkill(nil, nil, side)
        end
    end
    unlockAllSkill(SIDE_ATK)

    BattleController.onLoadComplete(self, ...)
end

function BattleControllerDungeonRune:enterRoundEnd()
    self.roundStep = BattleConst.BATTLE_ROUND_STEP.ON_END
    local defTeam = self.battleData:getDefTeam()
    if not defTeam or defTeam:getIsDead() then -- 怪物死了， 直接进入刷新逻辑
        self.curWaveMonsterDead = true
        self.atkTeam:onRoundEnd()
        self:enterNextWave()
        return
    end

    local atkTeam = self.battleData:getAtkTeam()
    if not atkTeam or atkTeam:getIsDead() then -- 英雄死了， 直接结算
        if self:tryShowRebirth(function() self:enterRoundEnd() end) then
            return
        end

        self.defTeam:onRoundEnd()
        self:enterNextWave()
        return
    end

    self.atkTeam:onRoundEnd()
    self.defTeam:onRoundEnd()

    self:checkTeamIsDead(function() self:enterNextWave() end, function()
        self:onRoundEnd()
    end)
end

function BattleControllerDungeonRune:checkTeamIsDead(callback, falseCallback)
    if self:tryShowRebirth(function()
        BattleController.checkTeamIsDead(self, callback, falseCallback)
    end) then
        return true
    end
    BattleController.checkTeamIsDead(self, callback, falseCallback)
end

function BattleControllerDungeonRune:tryShowRebirth(callback)
    local atkTeam = self.battleData:getAtkTeam()
    if atkTeam:getIsDead() and self.canRebirthTimes > 0 and not self.isBossChapter then
        ModuleManager.DungeonRuneManager:showRebirthUI(function()
            self.canRebirthTimes = self.canRebirthTimes - 1
            local mainUnit = self.atkTeam:getMainUnit()
            mainUnit:rebirth()
            local hp = mainUnit.unitEntity:getMaxHp() * 5000 // GConst.BattleConst.DEFAULT_FACTOR -- 回血50%
            local x = mainUnit:takeDamageOrCure(mainUnit, hp, GConst.BattleConst.EFFECT_TYPE.HEAL, 0, BattleConst.SPECIAL_DAMAGE_OR_CURE_TYPE.AD_REBIRTH)
            local hpPercent = mainUnit.unitEntity:getHpPercent()
            self:refreshHp(mainUnit:getSide(), mainUnit.unitEntity:getHp(), hpPercent)
            self.atkTeam:checkPassiveEvent(PASSIVE_EVENT.HP_LOWER_THAN, mainUnit, hpPercent)
            mainUnit:changeState(UNIT_STATE.IDLE)
            mainUnit:initPosition()

            self.dungeonRuneRemainRoundCount = self.dungeonRuneRemainRoundCount + self.addRoundCount
            self.dungeonRuneMaxRoundCount = self.dungeonRuneMaxRoundCount + self.addRoundCount
            if self.battleUI then
                local desc = self.dungeonRuneRemainRoundCount + 1
                self.battleUI:refreshWave(desc, GConst.ATLAS_PATH.COMMON, "common_dec_15")
            end
            if callback then
                callback()
            end
        end, function()
            self.canRebirthTimes = self.canRebirthTimes - 1
            if callback then
                callback()
            end
        end, true, self.dungeonRuneRemainRoundCount <= 0)
        return true
    end
end

function BattleControllerDungeonRune:controllBattleEnd()
    self.combatReport = {
        battleType = GConst.BattleConst.BATTLE_TYPE.DUNGEON_RUNE,
        wave = self:getWaveIndex(),
        victory = self.victory,
    }
    local atkReport = {}
    local teamEntity = self.battleData:getAtkTeam()
    local members = teamEntity:getAllMembers()
    for k, v in pairs(members) do
        local report = {
            heroId = v:getId(),
            dmg = v:getDamageCount(),
        }
        table.insert(atkReport, report)
    end
    self.combatReport.atkReport = atkReport
    if not self.victory then
        self.combatReport.wave = self.combatReport.wave - 1
    end
    BIReport:postFightHeroReport(self.combatReport.battleType, atkReport)
    ModuleManager.DungeonRuneManager:reqFightSettlement(self.chapterId, self.combatReport, self.taskProgress, self.dungeonRuneRemainRoundCount)
end

function BattleControllerDungeonRune:postWaveOver(atkDead, isQuit)
    local deathType = BIReport.FIGHT_DEATH_TYPE.SURVIVE
    local waveEndType = BIReport.FIGHT_WAVE_END_TYPE.WIN
    if atkDead then
        if self.isBossWave then
            deathType = BIReport.FIGHT_DEATH_TYPE.BOSS_FAIL
        else
            deathType = BIReport.FIGHT_DEATH_TYPE.NORMAL_FAIL
        end
        waveEndType = BIReport.FIGHT_WAVE_END_TYPE.FAIL
    end

    if isQuit then
        waveEndType = BIReport.FIGHT_WAVE_END_TYPE.QUIT
    end

    local duration = self.waveDurationTime
    local totalTime = self.totalDurationTime
    local runeData = DataManager.DungeonData:getDungeonDataByType(ModuleManager.MODULE_KEY.RUNES_OPEN)
    local startTimes = runeData:getChapterFightCount(self.chapterId)
    local isFirstWin = false
    if runeData:getPassedMaxId() < self.chapterId and self.victory then
        isFirstWin = true
    end

    local isFianlStep = self:getWaveIndex() >= self.maxWaveIndex

    BIReport:postFightEnd(GConst.BattleConst.BATTLE_TYPE.DUNGEON_RUNE, self.battleData, self.chapterId, self:getWaveIndex(), duration, totalTime, self.eliminateCount, self.eliminateTotalCount, waveEndType, deathType, startTimes, isFirstWin, isFianlStep, self.maxLinkCount)
end

function BattleControllerDungeonRune:postFightStart()
    local runeData = DataManager.DungeonData:getDungeonDataByType(ModuleManager.MODULE_KEY.RUNES_OPEN)
    local startTimes = runeData:getChapterFightCount(self.chapterId)
    local unlockMaxChapter = runeData:getPassedMaxId() + 1
    BIReport:postFightBegin(GConst.BattleConst.BATTLE_TYPE.DUNGEON_RUNE, self:getWaveIndex(), self.chapterId, unlockMaxChapter, startTimes)
end

function BattleControllerDungeonRune:getRuneTaskNumByType(taskInfo)
    local taskType = taskInfo[1]
    local taskParams1 = taskInfo[2]
    if taskType == GConst.DungeonRuneConst.TASK_TYPE.ELIMINATION_ELEMENT then
        return self.totalEliminateCountMap[taskParams1] or 0
    elseif taskType == GConst.DungeonRuneConst.TASK_TYPE.BREAK_GRID_TYPE then
        return self.totalBreakedGridType[taskParams1] or 0
    elseif taskType == GConst.DungeonRuneConst.TASK_TYPE.KILL_MONSTER then
        return self.taskProgress[GConst.BattleConst.BATTLE_TASK_FIELD.KILL_NORMAL_MONSTER] or 0
    end
    return 0
end

function BattleControllerDungeonRune:getRuneTaskAllOver()
    local runeData = DataManager.DungeonData:getDungeonDataByType(ModuleManager.MODULE_KEY.RUNES_OPEN)
    local chapterCondition = runeData:getChapterCondition(self.chapterId)
    local taskProgress = ModuleManager.DungeonRuneManager:getTaskStatus(self, chapterCondition)
    local taskOver = true
    for index, info in pairs(taskProgress) do
        if not info.over and index ~= 1 then
            taskOver = false
            break
        end
    end
    return taskOver
end

return BattleControllerDungeonRune