local playerManager = require("playerManager")

local battleGeneral,battleOptions,battlePlayer
local onlinePlay

local bannedCharacters = {}


local poofEmitter = Particles.Emitter(0,0,Misc.resolveFile("resources/poofEffect.ini"))

local transformCommand


function bannedCharacters.characterIsBanned(character)
    local modeRuleset = battleOptions.getModeRuleset()

    if modeRuleset.bannedCharacters == nil then
        return false
    end

    if battleOptions.alwaysBannedCharacters[battleGeneral.mode][character] then
        return true
    end

    return modeRuleset.bannedCharacters[character]
end


local function getChangedCharacterMap()
    -- Get a map of banned characters
    local bannedCharacterMap = {}
    local validCharacterList = {}
    local unusedValidCharacterList = {}

    for character = 1,5 do
        if bannedCharacters.characterIsBanned(character) then
            bannedCharacterMap[character] = true
        else
            bannedCharacterMap[character] = false
            table.insert(validCharacterList,character)
            table.insert(unusedValidCharacterList,character)
        end
    end


    local players = battlePlayer.getActivePlayers()
    local needNewCharacterPlayers = {}

    for _,p in ipairs(players) do
        -- This player's character isn't allowed!
        if bannedCharacterMap[p.character] then
            table.insert(needNewCharacterPlayers,p)
        else
            -- Remove this character from the list of unused characters if necessary
            local index = table.ifind(unusedValidCharacterList,p.character)

            if index ~= nil then
                table.remove(unusedValidCharacterList,index)
            end
        end
    end

    -- No character changes needed
    if #needNewCharacterPlayers == 0 then
        return nil
    end

    -- Get the new characters for the relevant players
    local changedCharacterMap = {}

    table.ishuffle(needNewCharacterPlayers) -- for funsies :3

    for _,p in ipairs(needNewCharacterPlayers) do
        local newCharacter

        if #unusedValidCharacterList > 0 then -- there's a character not in use currently, take that
            local index = RNG.randomInt(1,#unusedValidCharacterList)

            changedCharacterMap[p.idx] = table.remove(unusedValidCharacterList,index)
        else -- no unused characters, just pick a random valid one
            changedCharacterMap[p.idx] = RNG.irandomEntry(validCharacterList)
        end
    end

    return changedCharacterMap
end


local function spawnPoofEffects(minX,minY,maxX,maxY)
    local effectGap = 32
    local effectCountX = math.max(1,math.floor((maxX - minX)/effectGap + 0.5))
    local effectCountY = math.max(1,math.floor((maxY - minY)/effectGap + 0.5))

    for effectIndexX = 1,effectCountX do
        for effectIndexY = 1,effectCountY do
            local effectX = (minX + maxX)*0.5
            local effectY = (minY + maxY)*0.5

            if effectCountX > 1 then
                effectX = effectX + effectGap*((effectIndexX - 1)/(effectCountX - 1) - 0.5)
            end

            if effectCountY > 1 then
                effectY = effectY + effectGap*((effectIndexY - 1)/(effectCountY - 1) - 0.5)
            end

            poofEmitter.x = effectX
            poofEmitter.y = effectY
            poofEmitter:emit(1)
        end
    end
end

local function transformPlayers(changedCharacterMap)
    for _,p in ipairs(Player.get()) do
        local newCharacter = changedCharacterMap[p.idx]

        if newCharacter ~= nil then
            local settings = PlayerSettings.get(newCharacter,p.powerup)

            -- New hitbox dimensions
            local newWidth = settings.hitboxWidth
            local newHeight = settings.hitboxHeight
            local newX = p.x + (p.width - newWidth)*0.5
            local newY = p.y + p.height - newHeight

            -- Spawn effects
            local minX = math.min(p.x,newX)
            local minY = math.min(p.y,newY)
            local maxX = math.max(p.x + p.width,newX + newWidth)
            local maxY = math.max(p.y + p.height,newY + newHeight)

            spawnPoofEffects(minX,minY,maxX,maxY)

            -- Actually transform the player
            p.x = newX
            p.y = newY
            p.width = newWidth
            p.height = newHeight

            p.character = newCharacter
            p.frame = 1
        end
    end

    SFX.play(34)
end


function bannedCharacters.transform()
    if onlinePlay.currentMode == onlinePlay.MODE_CLIENT then
        return
    end

    -- Decide what players need to be changed, and what characters they should be changed into
    local changedCharacterMap = getChangedCharacterMap()

    if changedCharacterMap == nil then
        return
    end

    if onlinePlay.currentMode == onlinePlay.MODE_HOST then
        transformCommand:send(0,changedCharacterMap)
    end

    -- Actual transform them
    transformPlayers(changedCharacterMap)
end


function bannedCharacters.onDraw()
    if poofEmitter:count() > 0 then
        poofEmitter:draw(-0.1,true,nil,true,nil,nil,true)
    end
end


function bannedCharacters.onInitAPI()
    battleGeneral = require("scripts/battleGeneral")
    battleOptions = require("scripts/battleOptions")
    battlePlayer = require("scripts/battlePlayer")
    onlinePlay = require("scripts/onlinePlay")

    registerEvent(bannedCharacters,"onDraw")

    -- Transform online command
    transformCommand = onlinePlay.createCommand("battle_bannedCharacters_transform",onlinePlay.IMPORTANCE_MAJOR)

    function transformCommand.onReceive(sourcePlayerIdx, changedCharacterMap)
        if sourcePlayerIdx ~= onlinePlay.hostPlayerIdx then
            return
        end

        transformPlayers(changedCharacterMap)
    end
end


return bannedCharacters