--[[
    Adds environmental spawners to the map. When enabled, the spawners will be added to newly generated Dungeons (Uruk Hai) and Temples (Spider). They are dropping a real mob spawner by change (very small chance).
    Copyright (C) 2016 - 2023 SaKeL <juraj.vajda@gmail.com>

    This library is free software; you can redistribute it and/or
    modify it pos the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to juraj.vajda@gmail.com
--]]

-- main tables
spawners_env = {
    ENABLED_MODS = {},
    MOBS_PROPS = {}
}

spawners_env.mob_tables = {}

function spawners_env.register_spawners()
    -- check if mods exists and build tables
    for k, mob_mod in ipairs(spawners_env.ENABLED_MODS) do
        local modpath = minetest.get_modpath(mob_mod)
        -- list of mobs and their info
        if (modpath) then
            for j, mob in ipairs(spawners_env.MOBS_PROPS[mob_mod]) do
                -- local mob_egg = nil

                -- create only environmental spawners
                if mob.env then
                    table.insert(spawners_env.mob_tables,  { name = mob.name, mod_prefix = mob_mod, egg_name_custom = mob.egg_name_custom, dummy_size = mob.dummy_size, dummy_offset = mob.dummy_offset, dummy_mesh = mob.dummy_mesh, dummy_texture = mob.dummy_texture, night_only = mob.night_only, sound_custom = mob.sound_custom, env = mob.env, boss = mob.boss })

                    -- use custom egg or create a default egg
                    -- if mob.egg_name_custom ~= '' then
                    --     mob_egg = mob.egg_name_custom
                    -- else
                    --     mob_egg = mob_mod  ..  ':'  ..  mob.name
                    -- end
                end

            end
        end
    end
end

-- start spawning mobs
function spawners_env.start_spawning(pos, how_many, mob_name, mod_prefix, sound_custom)

    if not (pos or mob_name) then
        return
    end

    -- remove 'spawners_env:' from the string
    local _mob_name = string.sub(mob_name, 14)
    local sound_name
    -- use custom sounds
    if sound_custom ~= '' then
        sound_name = sound_custom
    else
        sound_name = mod_prefix .. '_' .. _mob_name
    end

    if how_many == nil then
        how_many = math.random(1, 2)
    end

    for i = 1, how_many do
        pos.y = pos.y + 1
        local obj = minetest.add_entity(pos, mod_prefix .. ':' .. _mob_name)

        if obj then
            if sound_name then
                minetest.sound_play(sound_name, {
                    pos = pos,
                    max_hear_distance = 100,
                    gain = 5,
                })
            end
        end
    end
end

function spawners_env.check_around_radius(pos)
    local player_near = false
    local radius = 21

    for _,obj in ipairs(minetest.get_objects_inside_radius(pos, radius)) do
        if obj:is_player() then
            player_near = true
        end
    end

    return player_near
end

function spawners_env.check_node_status(pos, mob, night_only, boss)
    local player_near = spawners_env.check_around_radius(pos)

    if player_near or boss then
        local random_pos
        local min_node_light = 10
        local tod = minetest.get_timeofday() * 24000
        local node_light = minetest.get_node_light(pos)

        if not node_light then
            return false
        end

        local spawn_positions = {}
        local right = minetest.get_node({ x = pos.x + 1, y = pos.y, z = pos.z })
        local front = minetest.get_node({ x = pos.x, y = pos.y, z = pos.z + 1 })
        local left = minetest.get_node({ x = pos.x - 1, y = pos.y, z = pos.z })
        local back = minetest.get_node({ x = pos.x, y = pos.y, z = pos.z - 1 })
        local top = minetest.get_node({ x = pos.x, y = pos.y + 1, z = pos.z })
        local bottom = minetest.get_node({ x = pos.x, y = pos.y - 1, z = pos.z })

        -- make sure that at least one side of the spawner is open
        if right.name == 'air' then
            table.insert(spawn_positions, { x = pos.x + 1.5, y = pos.y, z = pos.z })
        end
        if front.name == 'air' then
            table.insert(spawn_positions, { x = pos.x, y = pos.y, z = pos.z + 1.5 })
        end
        if left.name == 'air' then
            table.insert(spawn_positions, { x = pos.x - 1.5, y = pos.y, z = pos.z })
        end
        if back.name == 'air' then
            table.insert(spawn_positions, { x = pos.x, y = pos.y, z = pos.z - 1.5 })
        end
        if top.name == 'air' then
            table.insert(spawn_positions, { x = pos.x, y = pos.y + 1.5, z = pos.z })
        end
        if bottom.name == 'air' then
            table.insert(spawn_positions, { x = pos.x, y = pos.y - 1.5, z = pos.z })
        end

        -- spawner is closed from all sides
        if #spawn_positions < 1 then
            return false

        else
            -- find random position in all posible places
            local possible_spawn_pos = {}
            local pick_random_key

            -- get a position value from the picked/random key
            for k, v in pairs(spawn_positions) do
                local node_above = minetest.get_node({ x = v.x, y = v.y + 1, z = v.z }).name
                local node_below = minetest.get_node({ x = v.x, y = v.y - 1, z = v.z }).name

                -- make super sure there is enough place to spawn mob and collect all possible spawn points
                if node_above == 'air' or node_below == 'air' then
                    table.insert(possible_spawn_pos, v)
                    -- print('possible pos: ' .. minetest.pos_to_string(v))
                end
            end

            -- no possible spawn points found - not enough place around the spawner
            if #possible_spawn_pos < 1 then
                return false

            elseif #possible_spawn_pos == 1 then
                -- only one possible position ?
                pick_random_key = #possible_spawn_pos

            else
                -- pick random from the possible open sides
                pick_random_key = math.random(1, #possible_spawn_pos)
            end

            random_pos = possible_spawn_pos[pick_random_key]
            -- print(minetest.pos_to_string(random_pos))
        end

        if night_only ~= 'disable' then
            -- spawn only at day
            if not night_only and node_light < min_node_light then
                return false, true
            end

            -- spawn only at night
            if night_only then
                if not (19359 > tod and tod > 5200) or node_light < min_node_light then
                    return random_pos
                else
                    return false, true
                end
            end
        end
        -- random_pos, waiting
        return random_pos, false
    else
        -- random_pos, waiting
        return false, true
    end
end
