local BattleConst = require "app/module/battle/battle_const"

local BattleBoardSkillHandle = {}

local SKILL_TYPE = BattleConst.SKILL_TYPE
local SKILL_METHOD_TYPE = BattleConst.SKILL_METHOD_TYPE

local function _takeElimination(posId, skillEntity, gridEntities, sequenceEntities, battleController)
    local boardrange = skillEntity:getBoardRange()
    if boardrange then
        local cludePosIdsMap = {}
        for posId, entity in pairs(gridEntities) do
            if entity:isEmptyIdle() then
                cludePosIdsMap[posId] = true
            end
        end
        for _, entity in ipairs(sequenceEntities) do
            cludePosIdsMap[entity:getPosId()] = nil
        end
        local maxRow = battleController.battleData:getRowCount()
        local posIdInfos = ModuleManager.BattleManager:getAroundPosIdsByList(posId, boardrange, cludePosIdsMap, maxRow)
        cludePosIdsMap = {}
        for _, info in ipairs(posIdInfos) do
            cludePosIdsMap[info.posId] = info
        end

        battleController.battleData:cacheSkillInfluenceGrids(cludePosIdsMap)
    end
end

local function _takeChangeAround(posId, skillEntity, gridEntities, sequenceEntities, battleController)
    local boardrange = skillEntity:getBoardRange()
    if boardrange then
        local cludePosIdsMap = {}
        for posId, entity in pairs(gridEntities) do
            if entity:canChangeInfo() then
                cludePosIdsMap[posId] = true
            end
        end
        local maxRow = battleController.battleData:getRowCount()
        local posIdInfos = ModuleManager.BattleManager:getAroundPosIdsByList(posId, boardrange, cludePosIdsMap, maxRow)
        local mainElementType
        for _, info in ipairs(battleController.battleData:getGridSequence()) do
            local entity = gridEntities[info.posId]
            local skillId = entity:getSkillId()
            if not skillId then
                mainElementType = entity:getElementType()
                break
            end
        end
        if not mainElementType then
            return
        end
        for _, info in ipairs(posIdInfos) do
            local entity = gridEntities[info.posId]
            if entity then
                entity:setElementType(mainElementType)
            end
        end
    end
end

local function _taleReleaseGridType(atkUnitComp, skillEntity, battleController)
    if skillEntity:getEliminateSkillParameter() then
        battleController:generateGridType(skillEntity:getEliminateSkillParameter(), atkUnitComp:getBaseObject():getTransform().position)
    end
end

local function _takeEliminationGridAndElement(posId, skillEntity, gridEntities, sequenceEntities, battleController)
    local boardrange = skillEntity:getBoardRange()
    if boardrange then
        local cludePosIdsMap = {}
        for posId, entity in pairs(gridEntities) do
            if entity:isNotObstacleIdle() then
                cludePosIdsMap[posId] = true
            end
        end
        local maxRow = battleController.battleData:getRowCount()
        local posIdInfos = ModuleManager.BattleManager:getAroundPosIdsByList(posId, boardrange, cludePosIdsMap, maxRow)
        cludePosIdsMap = {}
        for _, info in ipairs(posIdInfos) do
            cludePosIdsMap[info.posId] = info
        end

        battleController.battleData:cacheSkillInfluenceGrids(cludePosIdsMap)
    end
end

local function _takeChangeAllElementType(atkUnitComp, skillEntity, battleController)
    local battleData = battleController.battleData
    if not battleData or not battleData:getGridEnties() then
        return
    end

    local elementMap = {}
    for k, v in pairs(BattleConst.ELEMENT_TYPE) do
        if v ~= BattleConst.ELEMENT_TYPE.NONE then
            elementMap[v] = {list = {}, count = 0}
            for k2, v2 in pairs(BattleConst.ELEMENT_TYPE) do
                if v2 ~= BattleConst.ELEMENT_TYPE.NONE and v2 ~= v then
                    table.insert(elementMap[v].list, v2)
                    elementMap[v].count = elementMap[v].count + 1
                end
            end
        end
    end

    local map = {}
    for posId, entity in pairs(battleData:getGridEnties()) do
        if entity:isEmptyIdle() then
            local eType = entity:getElementType()
            if elementMap[eType] and elementMap[eType].count > 0 then
                map[entity] = elementMap[eType].list[math.random(1, elementMap[eType].count)]
            end
        end
    end

    battleController:changeElementTypeByMap(map)
end

local function _takeRandomKillSkillGrid(atkUnitComp, skillEntity, battleController)
    local battleData = battleController.battleData
    if not battleData or not battleData:getGridEnties() or not skillEntity:getEliminateSkillParameter() then
        return
    end

    local count = skillEntity:getEliminateSkillParameter()[1]
    if count <= 0 then
        return
    end

    local list = {}
    local listCount = 0
    for posId, entity in pairs(battleData:getGridEnties()) do
        if entity:getSkillId() and not entity:getIsIdle() then
            table.insert(list, posId)
            listCount = listCount + 1
        end
    end

    -- 处理表现
    for index, posId in ipairs(list) do
        if index > count then
            break
        end
        battleController.battleUI:showGridEffectSfx(posId, BattleConst.GRID_KILL_SFX.skill, function()
            local entity = battleData:getGridEntity(posId)
            entity:setIsIdle(true)
            battleController.battleUI:removeOneGridOutOfScreen(posId)
        end, 0.08)
    end
end

local function _takeShuffleBoard(atkUnitComp, skillEntity, battleController)
    local battleData = battleController.battleData
    if not battleData or not battleData:getGridEnties() then
        return
    end

    battleController:shuffleBoard()
end

local function _takeRandomKillRowOrColumn(atkUnitComp, skillEntity, battleController)
    local battleData = battleController.battleData
    if not battleData or not battleData:getGridEnties() or not skillEntity:getEliminateSkillParameter() then
        return
    end

    local count = skillEntity:getEliminateSkillParameter()[1]
    if count <= 0 then
        return
    end
    local rCount = math.random(0, count)
    local cCount = count - rCount
    local rowMap = {}
    local columnMap = {}
    local rList = {}
    local rListCount = 0 
    local cList = {}
    local cListCount = 0
    for posId, entity in pairs(battleData:getGridEnties()) do
        if entity:isEmptyIdle() then
            local rc = ModuleManager.BattleManager:getPosRC(posId)
            local r = rc.r
            local c = rc.c
            if not rowMap[r] then
                table.insert(rList, r)
                rListCount = rListCount + 1
                rowMap[r] = {}
            end
            if not columnMap[c] then
                table.insert(cList, c)
                cListCount = cListCount + 1
                columnMap[c] = {}
            end
            table.insert(rowMap[r], posId)
            table.insert(columnMap[c], posId)
        end
    end

    local infoList = {}
    rCount = math.min(rCount, rListCount)
    cCount = math.min(cCount, cListCount)
    for i = 1, rCount do
        local r = table.remove(rList, math.random(1, rListCount))
        rListCount = rListCount - 1
        if rowMap[r] then
            table.insert(infoList, {
                r = true,
                centerPos = ModuleManager.BattleManager:getPosId(r, 4),
                posList = rowMap[r]
            })
        end
    end

    for i = 1, cCount do
        local c = table.remove(cList, math.random(1, cListCount))
        cListCount = cListCount - 1
        if columnMap[c] then
            table.insert(infoList, {
                c = true,
                centerPos = ModuleManager.BattleManager:getPosId(4, c),
                posList = columnMap[c]
            })
        end
    end


    local temp_map = {}
    -- 处理表现
    for index, info in ipairs(infoList) do
        for _, posId in ipairs(info.posList) do
            if not temp_map[posId] then
                local entity = battleData:getGridEntity(posId)
                entity:setIsIdle(true)
                battleController:addFillWaitingCount()
                temp_map[posId] = true
            end
        end
        local zEuler = 0
        if info.c then
            zEuler = 90
        end
        battleController.battleUI:showGridEffectSfx(info.centerPos, BattleConst.GRID_KILL_SFX.line, function()
            for _, posId in ipairs(info.posList) do
                local entity = battleData:getGridEntity(posId)
                if entity:getCell() then
                    entity:getCell():doScale(0, 1, function()
                        battleController:reduceFillWaitingCount()
                        battleController.battleUI:removeOneGridOutOfScreen(posId)
                        entity:getCell():hideAni()
                    end)
                end
            end
        end, 1, zEuler)
    end
end

local function _takeKillMaxElementAndHeal(atkUnitComp, skillEntity, battleController)
    local battleData = battleController.battleData
    if not battleData or not battleData:getGridEnties() or not skillEntity:getEliminateSkillParameter() then
        return
    end

    local effectNum = skillEntity:getEliminateSkillParameter()[1]
    if not effectNum or effectNum <= 0 then
        return
    end

    local elementMap = {}
    local elementCount = {}
    for posId, entity in pairs(battleData:getGridEnties()) do
        if entity:isEmptyIdle() then
            local elementType = entity:getElementType()
            if not elementMap[elementType] then
                elementMap[elementType] = {}
            end
            table.insert(elementMap[elementType], posId)
            elementCount[elementType] = (elementCount[elementType] or 0) + 1
        end
    end

    local count = 0
    local maxElement
    for elementType, num in pairs(elementCount) do
        if num > count then
            count = num
            maxElement = elementType
        end
    end

    if not maxElement then
        return
    end

    local list = elementMap[maxElement]
    local heal = count * effectNum * atkUnitComp.unitEntity:getAtk() // GConst.BattleConst.DEFAULT_FACTOR
    atkUnitComp:takeDamageOrCure(atkUnitComp, heal, BattleConst.EFFECT_TYPE.HEAL, 0, BattleConst.SPECIAL_DAMAGE_OR_CURE_TYPE.KILL_MAX_ELEMENT_AND_HEAL)

    -- 处理表现
    for _, posId in ipairs(list) do
        if BattleConst.GRID_KILL_SFX[maxElement] then
            battleController.battleUI:showGridEffectSfx(posId, BattleConst.GRID_KILL_SFX[maxElement], function()
                local entity = battleData:getGridEntity(posId)
                entity:setIsIdle(true)
                battleController.battleUI:removeOneGridOutOfScreen(posId)
            end, 0.08)
        else
            local entity = battleData:getGridEntity(posId)
            entity:setIsIdle(true)
            battleController.battleUI:removeOneGridOutOfScreen(posId)
        end
    end
end


local function _takeKillRowAndColumn(atkUnitComp, skillEntity, battleController)
    local battleData = battleController.battleData
    if not battleData or not battleData:getGridEnties() or not skillEntity:getEliminateSkillParameter() then
        return
    end

    local params = skillEntity:getEliminateSkillParameter()
    local rCount, cCount = params[1], params[2]
    if rCount <= 0 and cCount <= 0 then
        return
    end

    local rowMap = {}
    local columnMap = {}
    local rList = {}
    local rListCount = 0
    local cList = {}
    local cListCount = 0
    for posId, entity in pairs(battleData:getGridEnties()) do
        if entity:isEmptyIdle() then
            local rc = ModuleManager.BattleManager:getPosRC(posId)
            local r = rc.r
            local c = rc.c
            if not rowMap[r] then
                table.insert(rList, r)
                rListCount = rListCount + 1
                rowMap[r] = {}
            end
            if not columnMap[c] then
                table.insert(cList, c)
                cListCount = cListCount + 1
                columnMap[c] = {}
            end
            table.insert(rowMap[r], posId)
            table.insert(columnMap[c], posId)
        end
    end

    local infoList = {}
    rCount = math.min(rCount, rListCount)
    cCount = math.min(cCount, cListCount)
    for i = 1, rCount do
        local r = table.remove(rList, math.random(1, rListCount))
        rListCount = rListCount - 1
        if rowMap[r] then
            table.insert(infoList, {
                r = r,
                centerPos = ModuleManager.BattleManager:getPosId(r, 4),
                posList = rowMap[r]
            })
        end
    end

    for i = 1, cCount do
        local c = table.remove(cList, math.random(1, cListCount))
        cListCount = cListCount - 1
        if columnMap[c] then
            table.insert(infoList, {
                c = c,
                centerPos = ModuleManager.BattleManager:getPosId(4, c),
                posList = columnMap[c]
            })
        end
    end

    local temp_map = {}
    -- 处理表现
    for index, info in ipairs(infoList) do
        for _, posId in ipairs(info.posList) do
            if not temp_map[posId] then
                local entity = battleData:getGridEntity(posId)
                entity:setIsIdle(true)
                battleController:addFillWaitingCount()
                temp_map[posId] = true
            end
        end
        local zEuler = 0
        if info.c then
            zEuler = 90
        end
        battleController.battleUI:showGridEffectSfx(info.centerPos, BattleConst.GRID_KILL_SFX.line, function()
            for _, posId in ipairs(info.posList) do
                local entity = battleData:getGridEntity(posId)
                if entity:getCell() then
                    entity:getCell():doScale(0, 1, function()
                        battleController:reduceFillWaitingCount()
                        battleController.battleUI:removeOneGridOutOfScreen(posId)
                        entity:getCell():hideAni()
                    end)
                end
            end
        end, 1, zEuler)
    end
end

local function _takeAddSkillEnergy(atkUnitComp, skillEntity, battleController)
    local params = skillEntity:getEliminateSkillParameter()
    if not params[2] or params[2] <= 0 then
        return
    end
    local elementType = params[1]
    local addEnergy = params[2]
    local elementTypeMap
    if elementType == BattleConst.ELEMENT_TYPE.NONE then
        local boardSkills
        local count = 0
        for eType, entity in pairs(battleController:getSkillEntities()) do
            if entity:getUnlocked() then
                if not boardSkills then
                    boardSkills = {}
                end
                table.insert(boardSkills, eType)
                count = count + 1
            end
        end
        if count <= 0 then
            return
        end
        elementType = boardSkills[math.random(1, count)]
    end

    local side =  battleController:getCurActionSide()
    if atkUnitComp then
        side = atkUnitComp:getSide()
    end
    elementTypeMap = {[elementType] = addEnergy}
    battleController:addSkillEnergy(elementTypeMap, side)
    battleController.battleUI:refreshSkill(nil, nil, side)
end

local function _takeChangeElementType(atkUnitComp, skillEntity, battleController)
    local params = skillEntity:getEliminateSkillParameter()
    if not params[2] or params[2] <= 0 then
        return
    end
    local elementType = params[1]
    local changeCount = params[2]

    battleController:changeElementType(changeCount, elementType, atkUnitComp and atkUnitComp:getSide())
end


BattleBoardSkillHandle._activeBoardSkill = {
	[SKILL_TYPE.ELIMINATION] = _takeElimination,
    [SKILL_TYPE.CHANGE_AROUND] = _takeChangeAround,
    [SKILL_TYPE.ELIMINATION_GRID_AND_ELEMENT] = _takeEliminationGridAndElement,
}

BattleBoardSkillHandle._activeAttackOverSkill = {
    [SKILL_TYPE.RELEASE_GRID_TYPE] = _taleReleaseGridType,
    [SKILL_TYPE.CHANGE_ALL_ELEMENT_TYPE] = _takeChangeAllElementType,
    [SKILL_TYPE.RANDOM_KILL_SKILL_GRID] = _takeRandomKillSkillGrid,
    [SKILL_TYPE.SHUFFLE_BOARD] = _takeShuffleBoard,
    [SKILL_TYPE.RANDOM_KILL_ROW_OR_COLUMN] = _takeRandomKillRowOrColumn,
    [SKILL_TYPE.KILL_MAX_ELEMENT_AND_HEAL] = _takeKillMaxElementAndHeal,
    [SKILL_TYPE.KILL_ROW_AND_COLUMN] = _takeKillRowAndColumn,
    [SKILL_TYPE.ADD_SKILL_ENERGY] = _takeAddSkillEnergy,
    [SKILL_TYPE.CHANGE_ELEMENT_TYPE] = _takeChangeElementType,
}

function BattleBoardSkillHandle.activeBoardSkill(posId, skillEntity, gridEntities, sequenceEntities, battleController)
    if not skillEntity then
        return
    end

    local func = BattleBoardSkillHandle._activeBoardSkill[skillEntity:getSkillType()]
    if func then
        func(posId, skillEntity, gridEntities, sequenceEntities, battleController)
    end
end

function BattleBoardSkillHandle.activeAttackOverSkill(atkUnitComp, skillEntity, battleController)
    if not skillEntity then
        return
    end

    local func = BattleBoardSkillHandle._activeAttackOverSkill[skillEntity:getEliminateSkillType()]
    if func then
        func(atkUnitComp, skillEntity, battleController)
    end
end

return BattleBoardSkillHandle