--luacheck: ignore
local math_abs = math.abs
local math_random = math.random
local GetNoise = require 'utils.math.get_noise'
local Public = {}

local hatchery_position = { x = 192, y = 0 }

local function get_replacement_tile(surface, position)
    for i = 1, 128, 1 do
        local vectors = { { 0, i }, { 0, i * -1 }, { i, 0 }, { i * -1, 0 } }
        table.shuffle_table(vectors)
        for _, v in pairs(vectors) do
            local tile = surface.get_tile(position.x + v[1], position.y + v[2])
            if not tile.collides_with('resource') then
                return tile.name
            end
        end
    end
    return 'landfill'
end

local function draw_spawn_ore(surface, position)
    local ores = { 'copper-ore', 'iron-ore', 'coal', 'stone' }
    table.shuffle_table(ores)

    local seed = math_random(1, 1000000)
    local r = 25
    local r_square = r ^ 2

    for x = -32, 32, 1 do
        for y = -32, 32, 1 do
            local position = { x = position.x + x + 0.5, y = position.y + y + 0.5 }
            if x ^ 2 + y ^ 2 + math_abs(GetNoise('decoratives', position, seed) * 300) < r_square then
                local name = ores[1]
                if y <= 0 and x < 0 then
                    name = ores[2]
                end
                if y >= 0 and x >= 0 then
                    name = ores[3]
                end
                if y >= 0 and x < 0 then
                    name = ores[4]
                end
                for _, e in pairs(surface.find_entities_filtered({ position = position })) do
                    e.destroy()
                end
                local tile = surface.get_tile(position)
                if tile.name == 'water' or tile.name == 'deepwater' then
                    surface.set_tiles({ { name = get_replacement_tile(surface, position), position = position } }, true)
                end
                surface.create_entity({ name = name, position = position, amount = math_random(800, 1000) })
            end
        end
    end
end

function Public.reset_nauvis(hatchery)
    local surface = game.surfaces.nauvis
    local mgs = surface.map_gen_settings
    ---@diagnostic disable-next-line: assign-type-mismatch
    mgs.seed = math_random(1, 99999999)
    mgs.water = 1
    mgs.starting_area = 1
    mgs.terrain_segmentation = 2
    mgs.cliff_settings = { cliff_elevation_interval = 0, cliff_elevation_0 = 0 }
    mgs.autoplace_controls = {
        ['coal'] = { frequency = 8, size = 0.7, richness = 0.5 },
        ['stone'] = { frequency = 8, size = 0.7, richness = 0.5 },
        ['copper-ore'] = { frequency = 8, size = 0.7, richness = 0.75 },
        ['iron-ore'] = { frequency = 8, size = 0.7, richness = 1 },
        ['uranium-ore'] = { frequency = 5, size = 0.5, richness = 0.5 },
        ['crude-oil'] = { frequency = 5, size = 1, richness = 1 },
        ---@diagnostic disable-next-line: assign-type-mismatch
        ['trees'] = { frequency = math.random(4, 32) * 0.1, size = math.random(4, 16) * 0.1, richness = math.random(1, 10) * 0.1 },
        ['enemy-base'] = { frequency = 0, size = 0, richness = 0 }
    }
    surface.map_gen_settings = mgs
    surface.clear(false)
    for chunk in surface.get_chunks() do
        surface.delete_chunk({ chunk.x, chunk.y })
    end
    hatchery.gamestate = 'prepare_east'
    -- game.print('preparing east', {150, 150, 150})
    print(hatchery.gamestate)
end

function Public.prepare_east(hatchery)
    if game.tick % 90 ~= 0 then
        return
    end
    local surface = game.surfaces.nauvis
    surface.request_to_generate_chunks({ hatchery_position.x, 0 }, 7)

    if not surface.is_chunk_generated({ 13, 0 }) then
        return
    end

    local r = 32
    local seed = surface.map_gen_settings.seed
    for x = r * -1, r, 1 do
        for y = r * -1, r, 1 do
            local p = { x = hatchery_position.x + x, y = hatchery_position.y + y }
            if math.sqrt(x ^ 2 + y ^ 2) + math_abs(GetNoise('decoratives', p, seed) * 9) < r then
                local tile = surface.get_tile(p)
                if tile.name == 'water' or tile.name == 'deepwater' then
                    surface.set_tiles({ { name = get_replacement_tile(surface, p), position = p } }, true)
                end
            end
        end
    end
    draw_spawn_ore(surface, { x = 240, y = 0 })
    hatchery.gamestate = 'clear_west'
    -- game.print('clearing west chunks', {150, 150, 150})
    print(hatchery.gamestate)
end

function Public.clear_west(hatchery)
    if game.tick % 90 ~= 0 then
        return
    end
    local surface = game.surfaces.nauvis
    for chunk in surface.get_chunks() do
        if chunk.x < 0 then
            surface.delete_chunk({ chunk.x, chunk.y })
        end
    end
    hatchery.mirror_queue = {}
    hatchery.gamestate = 'prepare_west'
    -- game.print('preparing west chunks', {150, 150, 150})
    print(hatchery.gamestate)
end

function Public.prepare_west(hatchery)
    if game.tick % 90 ~= 0 then
        return
    end
    local surface = game.surfaces.nauvis
    surface.request_to_generate_chunks({ hatchery_position.x * -1, 0 }, 7)
    surface.force_generate_chunk_requests()
    if hatchery.mirror_queue[1] then
        return
    end
    hatchery.gamestate = 'draw_team_nests'
    print(hatchery.gamestate)
end

function Public.draw_team_nests(hatchery)
    if game.tick % 90 ~= 0 then
        return
    end
    -- game.print('placing nests', {150, 150, 150})
    local surface = game.surfaces.nauvis
    local x = hatchery_position.x

    local e = surface.create_entity({ name = 'biter-spawner', position = { x * -1, 0 }, force = 'west' })
    if not e or not e.valid then
        return
    end
    for _, p in pairs({ { x * -1 + 6, 0 }, { x * -1 + 3, 6 }, { x * -1 + 3, -5 } }) do
        surface.create_entity({ name = 'small-worm-turret', position = p, force = 'west' })
        surface.create_decoratives { check_collision = false, decoratives = { { name = 'enemy-decal', position = p, amount = 1 } } }
    end
    e.active = false
    storage.map_forces.west.hatchery = e
    storage.map_forces.east.target = e
    surface.create_decoratives { check_collision = false, decoratives = { { name = 'enemy-decal', position = e.position, amount = 3 } } }

    local e = surface.create_entity({ name = 'biter-spawner', position = { x, 0 }, force = 'east' })
    if not e or not e.valid then
        return
    end
    for _, p in pairs({ { x - 6, 0 }, { x - 3, 6 }, { x - 3, -5 } }) do
        surface.create_entity({ name = 'small-worm-turret', position = p, force = 'east' })
        surface.create_decoratives { check_collision = false, decoratives = { { name = 'enemy-decal', position = p, amount = 1 } } }
    end

    e.active = false
    storage.map_forces.east.hatchery = e
    storage.map_forces.west.target = e
    surface.create_decoratives { check_collision = false, decoratives = { { name = 'enemy-decal', position = e.position, amount = 3 } } }

    hatchery.gamestate = 'draw_border_beams'
    print(hatchery.gamestate)
end

function Public.draw_border_beams(hatchery)
    if game.tick % 90 ~= 0 then
        return
    end
    local surface = game.surfaces.nauvis
    surface.create_entity({ name = 'electric-beam', position = { 4, -96 }, source = { 4, -96 }, target = { 4, 96 } })
    surface.create_entity({ name = 'electric-beam', position = { -4, -96 }, source = { -4, -96 }, target = { -4, 96 } })
    hatchery.gamestate = 'spawn_players'
    print(hatchery.gamestate)
end

function Public.combat_area(event)
    local surface = event.surface
    local left_top = event.area.left_top
    local seed = surface.map_gen_settings.seed

    if left_top.x >= 256 or left_top.y < -192 or left_top.y > 192 then
        return
    end
    for _, tile in pairs(surface.find_tiles_filtered({ area = event.area, name = { 'water', 'deepwater' } })) do
        if tile.position.x + math_abs(GetNoise('cave_rivers', { x = 0, y = tile.position.y }, seed) * 64) < 224 then
            if math_abs(GetNoise('n5', tile.position, seed)) < 0.25 then
                surface.set_tiles({ { name = get_replacement_tile(surface, tile.position), position = tile.position } }, true)
            end
        end
    end

    if left_top.x ~= 0 then
        return
    end
    if left_top.y >= 96 then
        return
    end
    if left_top.y < -96 then
        return
    end

    for _, tile in pairs(surface.find_tiles_filtered({ area = event.area })) do
        if tile.position.x < 4 then
            surface.set_tiles({ { name = 'water-shallow', position = tile.position } }, true)
        end
    end
end

local function mirror_tiles(east_left_top)
    local surface = game.surfaces.nauvis
    surface.request_to_generate_chunks({ x = east_left_top.x + 16, y = east_left_top.y + 16 }, 0)
    surface.force_generate_chunk_requests()
    local mirror_area = { { east_left_top.x, east_left_top.y }, { east_left_top.x + 32, east_left_top.y + 32 } }
    for _, tile in pairs(surface.find_tiles_filtered({ area = mirror_area })) do
        surface.set_tiles({ { name = tile.name, position = { x = tile.position.x * -1 - 1, y = tile.position.y } } }, true)
    end
end

local function mirror_entities(east_left_top)
    local surface = game.surfaces.nauvis
    local mirror_area = { { east_left_top.x, east_left_top.y }, { east_left_top.x + 32, east_left_top.y + 32 } }
    for _, entity in pairs(surface.find_entities_filtered({ area = mirror_area })) do
        if surface.can_place_entity({ name = entity.name, position = { x = entity.position.x * -1, y = entity.position.y } }) then
            entity.clone({ position = { x = entity.position.x * -1, y = entity.position.y }, surface = surface, force = 'neutral' })
        end
    end
end

local function mirror_decoratives(east_left_top)
    local surface = game.surfaces.nauvis
    local mirror_area = { { east_left_top.x, east_left_top.y }, { east_left_top.x + 32, east_left_top.y + 32 } }
    for _, decorative in pairs(surface.find_decoratives_filtered { area = mirror_area }) do
        surface.create_decoratives {
            check_collision = false,
            decoratives = { { name = decorative.decorative.name, position = { x = decorative.position.x * -1 - 1, y = decorative.position.y }, amount = decorative.amount } }
        }
    end
end

local mirror_functions = {
    [1] = mirror_tiles,
    [2] = mirror_entities,
    [3] = mirror_decoratives
}

function Public.mirror_queue(hatchery)
    local mirror_queue = hatchery.mirror_queue
    local chunk = mirror_queue[1]
    if not chunk then
        return
    end
    mirror_functions[chunk[2]]({ x = chunk[1][1] * -1 - 32, y = chunk[1][2] })
    mirror_queue[1][2] = mirror_queue[1][2] + 1
    if not mirror_functions[mirror_queue[1][2]] then
        table.remove(hatchery.mirror_queue, 1)
    end
end

local function is_out_of_map(p)
    if p.y < 96 and p.y >= -96 then
        return
    end
    if p.x * 0.5 >= math_abs(p.y) then
        return
    end
    if p.x * -0.5 > math_abs(p.y) then
        return
    end
    return true
end

function Public.is_out_of_map_chunk(p)
    if p.y < 96 and p.y >= -96 then
        return
    end
    if p.x * 0.5 + 32 >= math_abs(p.y) then
        return
    end
    if p.x * -0.5 + 32 > math_abs(p.y) then
        return
    end
    return true
end

function Public.out_of_map_area(surface, left_top)
    local tiles = {}
    local i = 1
    for x = -1, 32, 1 do
        for y = -1, 32, 1 do
            local p = { x = left_top.x + x, y = left_top.y + y }
            if is_out_of_map(p) then
                tiles[i] = { name = 'out-of-map', position = { x = left_top.x + x, y = left_top.y + y } }
                i = i + 1
            end
        end
    end
    surface.set_tiles(tiles, true)
end

function Public.out_of_map(surface, left_top)
    local tiles = {}
    local i = 1
    for x = 0, 31, 1 do
        for y = 0, 31, 1 do
            tiles[i] = { name = 'out-of-map', position = { x = left_top.x + x, y = left_top.y + y } }
            i = i + 1
        end
    end
    surface.set_tiles(tiles, true)
end

return Public
