--luacheck: ignore
--map by mewmew and kyte

require 'maps.island_troopers.map_intro'
require 'utils.functions.noise_vector_path'
require 'modules.shopping_chests'
require 'modules.no_turrets'
require 'modules.dangerous_goods'
require 'modules.rpg'
require 'modules.difficulty_vote'
require 'maps.island_troopers.enemies'
require 'maps.island_troopers.terrain'
local Difficulty = require 'modules.difficulty_vote'

max_island_radius = 128

local function create_stage_gui(player)
    if player.gui.top.stage_gui then
        return
    end
    local element = player.gui.top.add({ type = 'frame', name = 'stage_gui', caption = ' ' })
    local style = element.style
    style.minimal_height = 38
    style.maximal_height = 38
    style.minimal_width = 140
    style.top_padding = 2
    style.left_padding = 4
    style.right_padding = 4
    style.bottom_padding = 2
    style.font_color = { r = 155, g = 85, b = 25 }
    style.font = 'default-large-bold'
end

function update_stage_gui()
    if not storage.stages then
        return
    end
    local caption = 'Level: ' .. storage.current_level
    caption = caption .. '  |  Stage: '
    local stage = storage.current_stage
    if stage > #storage.stages - 1 then
        stage = #storage.stages - 1
    end
    caption = caption .. stage
    caption = caption .. '/'
    caption = caption .. #storage.stages - 1
    caption = caption .. '  |  Bugs remaining: '
    caption = caption .. storage.alive_enemies

    for _, player in pairs(game.connected_players) do
        if player.gui.top.stage_gui then
            player.gui.top.stage_gui.caption = caption
        end
    end
end

local function bring_players()
    local surface = game.surfaces[1]
    for _, player in pairs(game.connected_players) do
        if player.position.y < -1 then
            if player.character then
                if player.character.valid then
                    local p = surface.find_non_colliding_position('character', { 0, 2 }, 8, 0.5)
                    if not p then
                        player.teleport({ 0, 2 }, surface)
                    end
                    player.teleport(p, surface)
                end
            end
        end
    end
end

local function drift_corpses_toward_beach()
    local surface = game.surfaces[1]
    for _, corpse in pairs(surface.find_entities_filtered({ name = 'character-corpse' })) do
        if corpse.position.y < 0 then
            if surface.get_tile(corpse.position).collides_with('resource') then
                corpse.clone {
                    position = { corpse.position.x, corpse.position.y + (math.random(50, 250) * 0.01) },
                    surface = surface,
                    force = corpse.force.name
                }
                corpse.destroy()
            end
        end
    end
end

local function get_island_size()
    local r_min = storage.current_level + 16
    if r_min > math.floor(max_island_radius * 0.5) then
        r_min = math.floor(max_island_radius * 0.5)
    end
    local r_max = storage.current_level * 2 + 32
    if r_max > max_island_radius then
        r_max = max_island_radius
    end
    return math.random(r_min, r_max)
end

local function set_next_level()
    storage.alive_enemies = 0
    storage.alive_boss_enemy_count = 0

    storage.current_level = storage.current_level + 1
    if storage.current_level > 1 then
        bring_players()
    end

    storage.current_stage = 1
    storage.stage_amount = math.floor(storage.current_level * 0.33) + 3
    if storage.stage_amount > 9 then
        storage.stage_amount = 9
    end

    storage.path_tiles = nil

    local island_size = get_island_size()

    storage.stages = {}
    storage.stages[1] = {
        path_length = 16 + island_size * 1.5,
        size = island_size
    }

    for i = 1, storage.stage_amount - 1, 1 do
        island_size = get_island_size()
        storage.stages[#storage.stages + 1] = {
            path_length = 16 + island_size * 1.5,
            size = island_size
        }
    end
    storage.stages[#storage.stages + 1] = {
        path_length = max_island_radius * 7,
        size = false
    }

    --game.print("Level " .. storage.current_level)
    update_stage_gui()

    storage.gamestate = 2
end

local function earn_credits(amount)
    for _, player in pairs(game.connected_players) do
        player.play_sound { path = 'utility/armor_insert', volume_modifier = 0.85 }
    end
    game.print(amount .. ' credits have been transfered to the factory.', { r = 255, g = 215, b = 0 })
    storage.credits = storage.credits + amount
end

local function slowmo()
    if not storage.slowmo then
        storage.slowmo = 0.15
    end
    game.speed = storage.slowmo
    storage.slowmo = storage.slowmo + 0.01
    if game.speed < 1 then
        return
    end
    for _, p in pairs(game.connected_players) do
        if p.gui.left['slowmo_cam'] then
            p.gui.left['slowmo_cam'].destroy()
        end
    end
    storage.slowmo = nil
    storage.gamestate = 4
end

local function wait_until_stage_is_beaten()
    if storage.alive_enemies > 0 then
        return
    end
    local reward_amount = false
    local gamestate = 2
    local base_reward = 250 * storage.current_level

    if storage.stages[storage.current_stage].size then
        if storage.current_stage < #storage.stages - 1 then
            reward_amount = base_reward + storage.current_stage * storage.current_level * 50
        else
            reward_amount = base_reward + storage.current_stage * storage.current_level * 150
        end
    else
        game.print('Final Stage complete!')
        game.print('Level is collapsing !!', { r = 255, g = 0, b = 0 })
        gamestate = 5
    end

    if reward_amount then
        earn_credits(reward_amount)
        update_stage_gui()
    end
    storage.current_stage = storage.current_stage + 1
    storage.gamestate = gamestate
end

local function on_player_joined_game(event)
    local player = game.players[event.player_index]
    create_stage_gui(player)
    if player.gui.left['slowmo_cam'] then
        player.gui.left['slowmo_cam'].destroy()
    end

    update_stage_gui()

    if player.online_time > 0 then
        return
    end
    player.insert({ name = 'pistol', count = 1 })
    player.insert({ name = 'firearm-magazine', count = 32 })
end

local function on_init()
    Difficulty.get()
    game.create_force('enemy_spawners')
    game.forces.enemy_spawners.set_friend('enemy', true)
    game.forces.enemy.set_friend('enemy_spawners', true)

    local surface = game.surfaces[1]
    surface.request_to_generate_chunks({ x = 0, y = 0 }, 16)
    surface.force_generate_chunk_requests()

    --storage.tree_raffle = {}
    --for _, e in pairs(prototypes.entity) do
    --	if e.type == "tree" then
    --		table.insert(storage.tree_raffle, e.name)
    --	end
    --end

    local blacklist = {
        ['dark-mud-decal'] = true,
        ['sand-dune-decal'] = true,
        ['light-mud-decal'] = true,
        ['puberty-decal'] = true,
        ['sand-decal'] = true,
        ['red-desert-decal'] = true
    }
    storage.decorative_names = {}
    for k, v in pairs(prototypes.decorative) do
        if not blacklist[k] then
            if v.autoplace_specification then
                storage.decorative_names[#storage.decorative_names + 1] = k
            end
        end
    end

    Difficulty.difficulty_poll_closing_timeout = 3600 * 10
    storage.level_vectors = {}
    storage.alive_boss_enemy_entities = {}
    storage.current_level = 0
    storage.gamestate = 1

    game.forces.player.set_spawn_position({ 0, 2 }, surface)
end

local msg = {
    'We got the brainbug!',
    'Good job troopers!',
    "I'm doing my part!"
}

local function on_entity_died(event)
    local entity = event.entity
    if not entity.valid then
        return
    end

    if entity.force.name == 'enemy_spawners' then
        if entity.type == 'unit' then
            return
        end
        storage.alive_enemies = storage.alive_enemies - 1
        return
    end

    if entity.force.name ~= 'enemy' then
        return
    end

    storage.alive_enemies = storage.alive_enemies - 1
    update_stage_gui()

    if entity.type ~= 'unit' then
        return
    end
    if not storage.alive_boss_enemy_entities[entity.unit_number] then
        return
    end

    storage.alive_boss_enemy_entities[entity.unit_number] = nil
    storage.alive_boss_enemy_count = storage.alive_boss_enemy_count - 1
    if storage.alive_boss_enemy_count == 0 then
        for _, p in pairs(game.connected_players) do
            if p.gui.left['slowmo_cam'] then
                p.gui.left['slowmo_cam'].destroy()
            end
            local frame = p.gui.left.add({ type = 'frame', name = 'slowmo_cam', caption = msg[math.random(1, #msg)] })
            local camera = frame.add({ type = 'camera', name = 'mini_cam_element', position = entity.position, zoom = 1.5, surface_index = 1 })
            camera.style.minimal_width = 400
            camera.style.minimal_height = 400
        end
        storage.gamestate = 8
    end
end

local gamestate_functions = {
    [1] = set_next_level,
    [2] = draw_path_to_next_stage,
    [3] = draw_the_island,
    [4] = wait_until_stage_is_beaten,
    [5] = kill_the_level,
    [8] = slowmo
}

local function on_tick()
    gamestate_functions[storage.gamestate]()
    if game.tick % 150 == 0 then
        drift_corpses_toward_beach()
    end
end

local event = require 'utils.event'
event.on_init(on_init)
event.add(defines.events.on_tick, on_tick)
event.add(defines.events.on_entity_died, on_entity_died)
event.add(defines.events.on_player_joined_game, on_player_joined_game)

require 'utils.functions.boss_unit'
