local battleGeneral = require("scripts/battleGeneral")
local battlePlayer = require("scripts/battlePlayer")
local battleTimer = require("scripts/battleTimer")

local onlinePlay = require("scripts/onlinePlay")
local onlinePlayNPC = require("scripts/onlinePlay_npc")

local maskCollection,tilemap,enemies

local waveManager = {}


-- Enemy Cluster
-- / Enemy Cluster Pattern
--   / Enemy Cluster Pattern Bunch
-- / NPC ID Group


waveManager.WAVE_DIFFICULTY = {
    BEGINNING = 1,
    EASY = 2,
    MEDIUM = 3,
    HARD = 4,
    SPICY = 5,
}


waveManager.baseQuota = 5
waveManager.quotaLinearIncreaseWaves = 5
waveManager.quotaLinearIncreaseAmount = 6
waveManager.quotaModifierMin = 1
waveManager.quotaModifierMax = 1

waveManager.quotaDiminishingWaveGrowth = {
    exponent = 2,
    coeffecient = 0.3,
    growthFactor = 0.25,
}
waveManager.quotaPlayerCountGrowth = {
    exponent = 1.5,
    coeffecient = 0.5,
    growthFactor = 0.5,
}


waveManager.baseEnemyPower = 25
waveManager.enemyPowerModifierMin = 1
waveManager.enemyPowerModifierMax = 1

waveManager.enemyPowerLinearIncreaseStartWave = 15
waveManager.enemyPowerLinearIncreaseRate = 6

waveManager.enemyPowerWaveGrowth = {
    exponent = 1.5,
    coeffecient = 0.25,
    growthFactor = 0.5,
}
waveManager.enemyPowerPlayerCountGrowth = {
    exponent = 1.2,
    coeffecient = 0.25,
    growthFactor = 0.5,
}


waveManager.bigBossHealthPlayerCountGrowth = {
    exponent = 1.5,
    coeffecient = 0.3,
    growthFactor = 0.8,
}


waveManager.bigBossMinWaveIndex = 7
waveManager.bigBossMinGapWaves = 1
waveManager.bigBossMaxGapWaves = 2
waveManager.bigBossMinProjectileEnemyCount = 20


waveManager.difficultyProgressWaveIndices = {
    2, -- EASY
    5, -- MEDIUM
    10, -- HARD
    20, -- SPICY
}


waveManager.clusterSpawnRadius = 192
waveManager.clusterOverlapRadius = 256

waveManager.clusterMinSpawnDuration = 192
waveManager.clusterMaxSpawnDuration = 1024
waveManager.clusterSpawnTimeVariance = 4

waveManager.enemySpawnEndMarginTime = 10

--waveManager.waveLength = 5
waveManager.waveLength = 90


waveManager.waveIndex = 1

waveManager.collectedMasks = 0
waveManager.maskQuota = 0


waveManager.waitingEnemyClusters = {}
waveManager.activeEnemyClusters = {}

waveManager.activatedClusterCount = 0
waveManager.totalClusterCount = 0


waveManager.spawnedMaskCount = 0
waveManager.totalMaskCount = 0

waveManager.spawnedEnemyPower = 0
waveManager.totalEnemyPower = 0

waveManager.projectileEnemyCount = 0

waveManager.bigBossID = 0


waveManager.stageType = nil
waveManager.musicIndex = nil


local desiredMaskSpawnCount,desiredEnemySpawnPower
local scalingPlayerCount


local function getClosestActivePlayer(x,y)
    local closestSquareDistance = math.huge
    local closestPlayer

    for _,p in ipairs(battlePlayer.getActivePlayers()) do
        local distX = p.x + p.width*0.5 - x
        local distY = p.y + p.height*0.5 - y
        local squareDistance = distX*distX + distY*distY

        if squareDistance < closestSquareDistance then
            closestSquareDistance = squareDistance
            closestPlayer = p
        end
    end

    return closestPlayer
end


local function decideEnemyCast()
    local castCounts = waveManager.enemyCastCounts[waveManager.difficulty]

    waveManager.enemyCast = {}

    for _,tier in ipairs(enemies.tiers) do
        waveManager.enemyCast[tier] = {}

        if castCounts[tier] ~= nil then
            local npcIDs = enemies.npcsIDsByTier[tier]
            if tier == enemies.TIER.BASIC and waveManager.bigBossID > 0 then
                npcIDs = enemies.projectileEnemyIDs
            end

            npcIDs = table.iclone(npcIDs)

            local pickAmount = math.min(#npcIDs,RNG.randomInt(castCounts[tier].minAmount,castCounts[tier].maxAmount))

            for i = 1,pickAmount do
                local index = RNG.randomInt(1,#npcIDs)
                local npcID = npcIDs[index]

                if npcID == enemies.fauxPlayerBaseID then
                    if RNG.random() < enemies.fauxPlayerLinkChance then
                        npcID = enemies.fauxPlayerLinkID
                    else
                        npcID = RNG.irandomEntry(enemies.fauxPlayerNormalIDs)
                    end
                end

                table.insert(waveManager.enemyCast[tier],npcID)
                table.remove(npcIDs,index)
            end
        end
    end
end

local function generateEnemiesFromPattern(clusterPattern)
    local npcIDs = {}

    for _,entry in ipairs(clusterPattern) do
        for i = 1,RNG.randomInt(entry.minAmount,entry.maxAmount) do
            local npcID = RNG.irandomEntry(waveManager.enemyCast[entry.tier])

            table.insert(npcIDs,npcID)
        end
    end

    return npcIDs
end

local function calculateMaskCountFromEnemies(npcIDs)
    local maskCount = 0

    for _,npcID in ipairs(npcIDs) do
        maskCount = maskCount + enemies.getNPCMaskCount(npcID)
    end

    return maskCount
end

local function calculatePowerFromEnemies(npcIDs)
    local enemyPower = 0

    for _,npcID in ipairs(npcIDs) do
        enemyPower = enemyPower + enemies.getNPCPower(npcID)
    end

    return enemyPower
end

local function calculateProjectileCountFromEnemies(npcIDs)
    local count = 0

    for _,npcID in ipairs(npcIDs) do
        if enemies.projectileEnemyIDMap[npcID] then
            count = count + 1
        end
    end

    return count
end

local function calculateLargestSpawnSpaceFromEnemies(npcIDs)
    local spawnWidth = 0
    local spawnHeight = 0

    for _,npcID in ipairs(npcIDs) do
        local npcSpawnWidth,npcSpawnHeight = enemies.getNecessarySpawnSpace(npcID)

        spawnWidth = math.max(spawnWidth,npcSpawnWidth)
        spawnHeight = math.max(spawnHeight,npcSpawnHeight)
    end

    return spawnWidth,spawnHeight
end

local function generateEnemyIDs(requireMasks)
    -- Generate a bunch of groups of NPC IDs
    local patterns = waveManager.enemyClusterPatterns[waveManager.difficulty]

    local npcIDGroups = {}
    local groupWeights = {}
    local totalWeight = 0

    for _,pattern in ipairs(patterns) do
        local npcIDGroup = {}

        for _,bunch in ipairs(pattern) do
            local npcID = RNG.irandomEntry(waveManager.enemyCast[bunch.tier])

            for i = 1,RNG.randomInt(bunch.minAmount,bunch.maxAmount) do
                table.insert(npcIDGroup,npcID)
            end
        end

        local groupMaskCount = calculateMaskCountFromEnemies(npcIDGroup)
        local groupEnemyPower = calculatePowerFromEnemies(npcIDGroup)
        local groupProjectileCount = calculateProjectileCountFromEnemies(npcIDGroup)

        if not (requireMasks and groupMaskCount == 0) and not (waveManager.bigBossID > 0 and waveManager.totalEnemyPower > desiredEnemySpawnPower and groupProjectileCount == 0) then
            local desireMaskCountDifference = waveManager.totalMaskCount + groupMaskCount - desiredMaskSpawnCount
            local desireEnemyPowerDifference = waveManager.totalEnemyPower + groupEnemyPower - desiredEnemySpawnPower

            local maskWeight = 1 - math.clamp(desireMaskCountDifference/5)
            local powerWeight = 1 - math.clamp(desireEnemyPowerDifference/10)
            local projectileWeight = 0

            if waveManager.bigBossID > 0 then
                local desireProjectileCountDifference = waveManager.projectileEnemyCount + groupProjectileCount - waveManager.bigBossMinProjectileEnemyCount

                projectileWeight = 1 - math.clamp(desireProjectileCountDifference/5)
            end

            local weight = (pattern.weight or 1)*(maskWeight*4 + powerWeight*8 + projectileWeight*8)

            table.insert(npcIDGroups,npcIDGroup)
            table.insert(groupWeights,weight)

            totalWeight = totalWeight + weight
        end
    end

    if totalWeight == 0 then
        return nil
    end

    -- Pick one
    local randomValue = RNG.random(0,totalWeight)

    for i = 1,(#npcIDGroups - 1) do
        randomValue = randomValue - groupWeights[i]

        if randomValue <= 0 then
            return npcIDGroups[i]
        end
    end

    return npcIDGroups[#npcIDGroups]
end

local function generateSpawnTimes(count)
    -- Generate time values with random gaps between each other
    local times = {0}
    local totalTime = 0

    for i = 1,(count - 1) do
        totalTime = totalTime + RNG.random(1,waveManager.clusterSpawnTimeVariance)
        table.insert(times,totalTime)
    end

    -- Normalise it such that the final time will always be 1
    for i,time in ipairs(times) do
        times[i] = i/totalTime
    end

    return times
end

local function spotIsWithinRecentCluster(spot)
    for clusterIndex = math.max(1,#waveManager.waitingEnemyClusters - 3),#waveManager.waitingEnemyClusters do
        local cluster = waveManager.waitingEnemyClusters[clusterIndex]

        local distX = spot.x - cluster.baseSpot.x
        local distY = spot.y - cluster.baseSpot.y
        local squareDistance = distX*distX + distY*distY

        if squareDistance < waveManager.clusterOverlapRadius*waveManager.clusterOverlapRadius then
            return true
        end
    end

    return false
end

local function getNearbySpots(spawnSpots,baseSpot)
    local spots = {}

    for _,spot in ipairs(spawnSpots) do
        if spot ~= baseSpot then
            local distX = spot.x - baseSpot.x
            local distY = spot.y - baseSpot.y
            local squareDistance = distX*distX + distY*distY

            if squareDistance < waveManager.clusterSpawnRadius*waveManager.clusterSpawnRadius then
                table.insert(spots,spot)
            end
        end
    end

    if #spots == 0 then
        return {baseSpot}
    end

    return spots
end

local function addEnemyCluster(npcIDs)
    -- Find suitable spots for this cluster to be based on
    local maxSpawnWidth,maxSpawnHeight = calculateLargestSpawnSpaceFromEnemies(npcIDs)
    local spawnSpots = tilemap.getFittingSpawnSpots(maxSpawnWidth,maxSpawnHeight)

    -- Filter down to only spawn spots that do not overlap another, recent cluster's spot
    local filteredSpawnSpots = {}

    for _,spot in ipairs(spawnSpots) do
        if not spotIsWithinRecentCluster(spot) then
            table.insert(filteredSpawnSpots,spot)
        end
    end

    if #filteredSpawnSpots == 0 then
        filteredSpawnSpots = spawnSpots
    end

    -- Pick spots to use
    local baseSpot = RNG.irandomEntry(filteredSpawnSpots)
    local usedSpots = {baseSpot}

    local nearbySpots = getNearbySpots(filteredSpawnSpots,baseSpot)
    local pickableSpots = table.iclone(nearbySpots)

    for i = 1,(#npcIDs - 1) do
        if #pickableSpots == 0 then
            pickableSpots = table.iclone(nearbySpots)
        end

        table.insert(usedSpots,table.remove(pickableSpots,RNG.randomInt(1,#pickableSpots)))
    end

    -- Add the new cluster
    table.insert(waveManager.waitingEnemyClusters,{
        enemyCount = #npcIDs,
        npcIDs = npcIDs,
        usedSpots = usedSpots,
        baseSpot = baseSpot,

        spawnDuration = RNG.random(waveManager.clusterMinSpawnDuration,waveManager.clusterMaxSpawnDuration),
        spawnTimes = generateSpawnTimes(#npcIDs),
        spawnTimer = 0,
        spawnCount = 0,
    })

    waveManager.totalEnemyPower = waveManager.totalEnemyPower + calculatePowerFromEnemies(npcIDs)
    waveManager.totalMaskCount = waveManager.totalMaskCount + calculateMaskCountFromEnemies(npcIDs)

    waveManager.projectileEnemyCount = waveManager.projectileEnemyCount + calculateProjectileCountFromEnemies(npcIDs)

    waveManager.totalClusterCount = waveManager.totalClusterCount + 1

    --Misc.dialog(waveManager.totalEnemyPower,waveManager.totalMaskCount,desiredEnemySpawnPower,desiredMaskSpawnCount)
end

local function decideEnemyClusters()
    waveManager.waitingEnemyClusters = {}

    waveManager.totalEnemyPower = 0
    waveManager.totalMaskCount = 0
    waveManager.projectileEnemyCount = 0

    if waveManager.bigBossID > 0 then
        waveManager.totalEnemyPower = waveManager.totalEnemyPower + enemies.getNPCPower(waveManager.bigBossID)
        waveManager.totalMaskCount = waveManager.totalMaskCount + enemies.getNPCMaskCount(waveManager.bigBossID)
    end


    while (waveManager.totalMaskCount < desiredMaskSpawnCount) do
        local npcIDs = generateEnemyIDs(true)

        if npcIDs == nil then
            break
        end

        addEnemyCluster(npcIDs)
    end

    while (waveManager.totalEnemyPower < desiredEnemySpawnPower or (waveManager.bigBossID > 0 and waveManager.projectileEnemyCount < waveManager.bigBossMinProjectileEnemyCount)) do
        local npcIDs = generateEnemyIDs(false)

        if npcIDs == nil then
            break
        end

        addEnemyCluster(npcIDs)
    end

    table.ishuffle(waveManager.waitingEnemyClusters)

    -- Add the special big boss cluster
    if waveManager.bigBossID > 0 then
        local cluster = {
            npcIDs = {waveManager.bigBossID},
            enemyCount = 1,

            spawnDuration = waveManager.clusterMinSpawnDuration,
            spawnTimes = {0},
            spawnTimer = 0,
            spawnCount = 0,
        }

        if waveManager.bigBossID == enemies.lankyNPCID then
            cluster.lankySpot = RNG.irandomEntry(tilemap.lankySpawnSpots)
        else
            local spawnWidth,spawnHeight = enemies.getNecessarySpawnSpace(waveManager.bigBossID)

            cluster.baseSpot = RNG.irandomEntry(tilemap.getFittingSpawnSpots(spawnWidth,spawnHeight))
            cluster.usedSpots = {cluster.baseSpot}
        end

        local insertIndex = #waveManager.waitingEnemyClusters - RNG.randomInt(2,math.floor(#waveManager.waitingEnemyClusters*0.5)) + 1

        table.insert(waveManager.waitingEnemyClusters,insertIndex,cluster)
    end
end


local function spawnFromEnemyCluster(cluster)
    cluster.spawnCount = cluster.spawnCount + 1

    local npcID = cluster.npcIDs[cluster.spawnCount]

    if npcID == enemies.lankyNPCID then
        local p = getClosestActivePlayer(cluster.lankySpot.x,cluster.lankySpot.y)
        local direction = ((p.x + p.width*0.5) > cluster.lankySpot.x and DIR_RIGHT) or DIR_LEFT

        local npc = NPC.spawn(npcID,cluster.lankySpot.x,Section(maskCollection.section).boundary.top - 256,maskCollection.section,false,true)

        npc.data.startY = cluster.lankySpot.y
        npc.direction = direction

        if onlinePlay.currentMode ~= onlinePlay.MODE_OFFLINE then
            onlinePlayNPC.claimNPCSpawn(npc)
        end
    else
        local spot = cluster.usedSpots[cluster.spawnCount]

        local p = getClosestActivePlayer(spot.x,spot.y)
        local direction = ((p.x + p.width*0.5) > spot.x and DIR_RIGHT) or DIR_LEFT

        enemies.spawn(npcID,spot.x,spot.y,direction)
    end

    waveManager.spawnedEnemyPower = waveManager.spawnedEnemyPower + enemies.getNPCPower(npcID)
    waveManager.spawnedMaskCount = waveManager.spawnedMaskCount + enemies.getNPCMaskCount(npcID)
end

local function updateActiveEnemyCluster(cluster)
    cluster.spawnTimer = cluster.spawnTimer + 1

    while (cluster.spawnCount < cluster.enemyCount and cluster.spawnTimer >= cluster.spawnTimes[cluster.spawnCount]*cluster.spawnDuration) do
        spawnFromEnemyCluster(cluster)
    end
end

local function activateEnemyCluster(cluster)
    waveManager.activatedClusterCount = waveManager.activatedClusterCount + 1
    table.insert(waveManager.activeEnemyClusters,cluster)

    spawnFromEnemyCluster(cluster)
end


local function calculateDiminishingGrowth(index,settings)
    return 1 + settings.coeffecient*math.log(index*settings.growthFactor + 1,settings.exponent)
end

local function calculateQuota(waveIndex,playerCount)
    local quota = waveManager.baseQuota

    -- Account for wave number
    if waveIndex > 1 then
        -- For the first few waves, there's just a simple linear increase...
        quota = quota + waveManager.quotaLinearIncreaseAmount*math.min(1,waveIndex/waveManager.quotaLinearIncreaseWaves)

        -- But for later waves, there's a growth curve with exponential falloff
        if waveIndex > waveManager.quotaLinearIncreaseWaves then
            quota = quota*calculateDiminishingGrowth(waveIndex - waveManager.quotaLinearIncreaseWaves,waveManager.quotaDiminishingWaveGrowth)
        end

        -- Slight randomness
        quota = quota*RNG.random(waveManager.quotaModifierMin,waveManager.quotaModifierMax)
    end

    -- Account for player count
    if playerCount > 1 then
        quota = quota*calculateDiminishingGrowth(playerCount - 1,waveManager.quotaPlayerCountGrowth)
    end

    return math.floor(quota + 0.5)
end

local function calculateEnemyPower(waveIndex,playerCount)
    local enemyPower = waveManager.baseEnemyPower

    -- Account for wave number
    if waveIndex > 1 then
        enemyPower = enemyPower*calculateDiminishingGrowth(waveIndex - 1,waveManager.enemyPowerWaveGrowth)

        -- Linear increase after a certain wave
        if waveIndex >= waveManager.enemyPowerLinearIncreaseStartWave then
            enemyPower = enemyPower + (waveIndex - waveManager.enemyPowerLinearIncreaseStartWave + 1)*waveManager.enemyPowerLinearIncreaseRate
        end

        -- Slight randomness
        enemyPower = enemyPower*RNG.random(waveManager.enemyPowerModifierMin,waveManager.enemyPowerModifierMax)
    end

    -- Account for player count
    if playerCount > 1 then
        enemyPower = enemyPower*calculateDiminishingGrowth(playerCount - 1,waveManager.enemyPowerPlayerCountGrowth)
    end

    return math.floor(enemyPower + 0.5)
end

local function getBigBossID()
    if waveManager.waveIndex < waveManager.bigBossMinWaveIndex then
        return 0
    end

    local saveFile = maskCollection.getSaveFile()

    if saveFile == nil or saveFile.bigBossGapWaves > 0 then
        return 0
    end

    -- Always make the first big boss Lanky
    if saveFile.lastBigBossID == nil then
        if #tilemap.lankySpawnSpots > 0 then
            return enemies.lankyNPCID
        else
            return 0
        end
    end

    -- Pick a random one, except not allowing for getting the same boss twice
    local npcIDs = {}

    for _,npcID in ipairs(enemies.npcsIDsByTier[enemies.TIER.BIG_BOSS]) do
        if npcID ~= saveFile.lastBigBossID and not (npcID == enemies.lankyNPCID and #tilemap.lankySpawnSpots == 0) then
            table.insert(npcIDs,npcID)
        end
    end

    return RNG.irandomEntry(npcIDs)
end


function waveManager.calculateBigBossHealthMultiplier(playerCount)
    if playerCount > 1 then
        return calculateDiminishingGrowth(playerCount - 1,waveManager.bigBossHealthPlayerCountGrowth)
    end

    return 1
end


function waveManager.debugPrintQuotas(playerCount)
    for _,waveIndex in ipairs{1,2,3,4,5,6,7,8,9,10,15,20,25,30,35,40,45,50,60,70,80,90,100,125,150,175,200} do
        print(string.format("Wave %d: %d %d",waveIndex,calculateQuota(waveIndex,playerCount),calculateEnemyPower(waveIndex,playerCount)))
    end

    print(waveManager.calculateBigBossHealthMultiplier(playerCount))
end


function waveManager.getDifficulty(waveIndex)
    local difficulty = waveManager.WAVE_DIFFICULTY.BEGINNING
    local index = 1

    while (difficulty < waveManager.WAVE_DIFFICULTY.SPICY and waveIndex >= waveManager.difficultyProgressWaveIndices[index]) do
        difficulty = difficulty + 1
        index = index + 1
    end

    return difficulty
end


function waveManager.startWave()
    scalingPlayerCount = maskCollection.getActivePlayerInSectionCount()

    waveManager.maskQuota = calculateQuota(waveManager.waveIndex,scalingPlayerCount)
    desiredMaskSpawnCount = math.floor(waveManager.maskQuota*RNG.random(1.5,1.75) + 0.5)
    desiredEnemySpawnPower = calculateEnemyPower(waveManager.waveIndex,scalingPlayerCount)

    waveManager.collectedMasks = 0

    waveManager.waitingEnemyClusters = {}
    waveManager.activeEnemyClusters = {}

    waveManager.activatedClusterCount = 0
    waveManager.totalClusterCount = 0
    waveManager.spawnedMaskCount = 0
    waveManager.spawnedEnemyPower = 0

    waveManager.bigBossID = getBigBossID()

    decideEnemyCast()
    decideEnemyClusters()

    maskCollection.changeGameState(maskCollection.GAME_STATE.WAVE,{
        maskQuota = waveManager.maskQuota,
        difficulty = waveManager.difficulty,
    })
end


function waveManager.onTick()
    if maskCollection.gameState ~= maskCollection.GAME_STATE.WAVE or onlinePlay.currentMode == onlinePlay.MODE_CLIENT then
        return
    end

    -- Activate new clusters
    local normalisedTime = math.clamp(1 - (battleTimer.framesLeft - waveManager.enemySpawnEndMarginTime*battleTimer.secondLength)/((waveManager.waveLength - waveManager.enemySpawnEndMarginTime)*battleTimer.secondLength))
    local activateClusterCount = math.min(waveManager.totalClusterCount,math.floor(normalisedTime*waveManager.totalClusterCount) + 1)

    while (waveManager.activatedClusterCount < activateClusterCount) do
        activateEnemyCluster(table.remove(waveManager.waitingEnemyClusters))
    end

    -- Spawn enemies from active clusters
    local i = 1

    while (waveManager.activeEnemyClusters[i] ~= nil) do
        local cluster = waveManager.activeEnemyClusters[i]
 
        updateActiveEnemyCluster(cluster)

        if cluster.spawnTimer >= cluster.spawnDuration then
            table.remove(waveManager.activeEnemyClusters,i)
        else
            i = i + 1
        end
    end


    --[[if player.rawKeys.altRun == KEYS_PRESSED then
        spawnEnemy()
    end]]
end

function waveManager.onDraw()
    --[[if maskCollection.gameState == maskCollection.GAME_STATE.WAVE then
        Text.print(string.format("Clusters: %d/%d",waveManager.activatedClusterCount,waveManager.totalClusterCount),32,32)
        Text.print(string.format("Power: %d/%d",waveManager.spawnedEnemyPower,waveManager.totalEnemyPower),32,64)
        Text.print(string.format("Masks: %d/%d",waveManager.spawnedMaskCount,waveManager.totalMaskCount),32,96)
    end]]
end


function waveManager.onInitAPI()
    registerEvent(waveManager,"onTick")
    registerEvent(waveManager,"onDraw")
    registerEvent(waveManager,"onPostNPCKill")

    maskCollection = require("maskCollection/scripts/maskCollection")
    tilemap = require("maskCollection/scripts/tilemap")
    enemies = require("maskCollection/scripts/enemies")

    waveManager.enemyClusterPatterns = {
        [waveManager.WAVE_DIFFICULTY.BEGINNING] = {
            {
                weight = 1,
                {tier = enemies.TIER.BASIC,minAmount = 2,maxAmount = 6},
            },
            {
                weight = 1,
                {tier = enemies.TIER.BOSS,minAmount = 1,maxAmount = 1},
                {tier = enemies.TIER.BASIC,minAmount = 2,maxAmount = 4},
            },
        },
        [waveManager.WAVE_DIFFICULTY.EASY] = {
            {
                weight = 1,
                {tier = enemies.TIER.BASIC,minAmount = 3,maxAmount = 8},
            },
            {
                weight = 0.5,
                {tier = enemies.TIER.BOSS,minAmount = 1,maxAmount = 1},
                {tier = enemies.TIER.BASIC,minAmount = 2,maxAmount = 4},
            },
            {
                weight = 0.5,
                {tier = enemies.TIER.BOSS,minAmount = 1,maxAmount = 1},
                {tier = enemies.TIER.BOSS,minAmount = 0,maxAmount = 1},
            },
        },
        [waveManager.WAVE_DIFFICULTY.MEDIUM] = {
            {
                weight = 1,
                {tier = enemies.TIER.BASIC,minAmount = 3,maxAmount = 8},
            },
            {
                weight = 0.5,
                {tier = enemies.TIER.BOSS,minAmount = 1,maxAmount = 1},
                {tier = enemies.TIER.BASIC,minAmount = 2,maxAmount = 4},
            },
            {
                weight = 0.5,
                {tier = enemies.TIER.BOSS,minAmount = 2,maxAmount = 2},
                {tier = enemies.TIER.BOSS,minAmount = 0,maxAmount = 1},
            },
            {
                weight = 0.5,
                {tier = enemies.TIER.ADVANCED,minAmount = 1,maxAmount = 2},
            },
            {
                weight = 0.5,
                {tier = enemies.TIER.ADVANCED,minAmount = 1,maxAmount = 1},
                {tier = enemies.TIER.BASIC,minAmount = 2,maxAmount = 4},
            },
        },
        [waveManager.WAVE_DIFFICULTY.HARD] = {
            {
                weight = 1,
                {tier = enemies.TIER.BASIC,minAmount = 5,maxAmount = 10},
            },
            {
                weight = 1,
                {tier = enemies.TIER.BOSS,minAmount = 1,maxAmount = 1},
                {tier = enemies.TIER.BASIC,minAmount = 4,maxAmount = 6},
            },
            {
                weight = 1,
                {tier = enemies.TIER.BOSS,minAmount = 2,maxAmount = 2},
                {tier = enemies.TIER.BOSS,minAmount = 0,maxAmount = 2},
            },
            {
                weight = 0.5,
                {tier = enemies.TIER.ADVANCED,minAmount = 1,maxAmount = 2},
                {tier = enemies.TIER.ADVANCED,minAmount = 1,maxAmount = 2},
            },
            {
                weight = 1,
                {tier = enemies.TIER.ADVANCED,minAmount = 1,maxAmount = 1},
                {tier = enemies.TIER.BASIC,minAmount = 4,maxAmount = 6},
            },
        },
        [waveManager.WAVE_DIFFICULTY.SPICY] = {
            {
                weight = 1,
                {tier = enemies.TIER.BASIC,minAmount = 5,maxAmount = 10},
            },
            {
                weight = 1,
                {tier = enemies.TIER.BOSS,minAmount = 1,maxAmount = 1},
                {tier = enemies.TIER.BASIC,minAmount = 4,maxAmount = 6},
            },
            {
                weight = 1,
                {tier = enemies.TIER.BOSS,minAmount = 2,maxAmount = 2},
                {tier = enemies.TIER.BOSS,minAmount = 0,maxAmount = 2},
            },
            {
                weight = 0.5,
                {tier = enemies.TIER.ADVANCED,minAmount = 1,maxAmount = 2},
                {tier = enemies.TIER.ADVANCED,minAmount = 1,maxAmount = 2},
            },
            {
                weight = 1,
                {tier = enemies.TIER.ADVANCED,minAmount = 1,maxAmount = 1},
                {tier = enemies.TIER.BASIC,minAmount = 4,maxAmount = 6},
            },
        },
    }

    waveManager.enemyCastCounts = {
        [waveManager.WAVE_DIFFICULTY.BEGINNING] = {
            [enemies.TIER.BASIC] = {minAmount = 1,maxAmount = 2},
            [enemies.TIER.BOSS] = {minAmount = 1,maxAmount = 1},
        },
        [waveManager.WAVE_DIFFICULTY.EASY] = {
            [enemies.TIER.BASIC] = {minAmount = 2,maxAmount = 3},
            [enemies.TIER.BOSS] = {minAmount = 2,maxAmount = 2},
        },
        [waveManager.WAVE_DIFFICULTY.MEDIUM] = {
            [enemies.TIER.BASIC] = {minAmount = 3,maxAmount = 4},
            [enemies.TIER.ADVANCED] = {minAmount = 1,maxAmount = 2},
            [enemies.TIER.BOSS] = {minAmount = 2,maxAmount = 2},
        },
        [waveManager.WAVE_DIFFICULTY.HARD] = {
            [enemies.TIER.BASIC] = {minAmount = 3,maxAmount = 5},
            [enemies.TIER.ADVANCED] = {minAmount = 2,maxAmount = 3},
            [enemies.TIER.BOSS] = {minAmount = 2,maxAmount = 4},
        },
        [waveManager.WAVE_DIFFICULTY.SPICY] = {
            [enemies.TIER.BASIC] = {minAmount = 3,maxAmount = 6},
            [enemies.TIER.ADVANCED] = {minAmount = 2,maxAmount = 3},
            [enemies.TIER.BOSS] = {minAmount = 3,maxAmount = 4},
        },
    }
end


return waveManager