
aom_mob_spawn.mob = {}
aom_mob_spawn.mob_index = {}


-- Registers a mob into the default "spawn from random solid blocks under air" method.
--[[

    aom_mob_spawn.cap.aom_kelar = {max=10, cur=0}
    aom_mob_spawn.register_mob({
        name = "aom_kelar:kelar",
        groups = {"hostile", "aom_kelar"},
        biomes = {},
        on_spawn = function(self, pos) end,
        can_spawn = function(pos, spawn_info)
            if aom_mob_spawn.has_head_room(pos, 2)
            and aom_mob_spawn.has_light(pos, 0, 7) then
                return true
            end
            return false
        end,
        can_despawn = function(self)
            local pos = self.object:get_pos()
            if not pos then
                return true
            end
            if aom_mob_spawn.has_player_in_range(pos, 0, 40) then
                return false
            end
            return true
        end,
    })
]]
function aom_mob_spawn.register_mob(param)
    local name = param.name
    aom_mob_spawn.mob[name] = param
    aom_mob_spawn.mob_index[#aom_mob_spawn.mob_index+1] = name

    for _, groupname in pairs(param.groups) do
        if not aom_mob_spawn.cap[groupname] then
            aom_mob_spawn.cap[groupname] = {max = 40, cur = 0}
        end
    end
end

function aom_mob_spawn.default_register_mob(name)
    aom_mob_spawn.register_mob({
        name = name,
        groups = {"passive"},
        biomes = {},
        on_spawn = function(self, pos) end,
        can_spawn = function(pos, spawn_info) return true end,
        can_despawn = function(pos) return true end,
    })
end

core.register_on_mods_loaded(function()
    for name, def in pairs(core.registered_entities) do
        if def._mob_spawn and not aom_mob_spawn.mob[name] then
            aom_mob_spawn.default_register_mob(name)
        end
    end
end)

LISTEN("can_mob_spawn_by_spawnnode", function(pos, mob_name)
    local edef = core.registered_entities[mob_name]
    if not edef then return false end
    local groups = edef._mobcap_groups
    if not groups then return false end
    for k, v in pairs(groups) do
        local cap = aom_mob_spawn.cap[v]
        if cap and (cap.cur >= cap.max) then return false end
    end
end)

LISTEN("on_mob_spawn", function(pos, ent)
end)

function aom_mob_spawn.get_spawnnode_mob(def, pos)
    if def.get_mob then
        return def.get_mob(def, pos)
    end
    if def.mobs then
        return def.mobs[math.random(1, #def.mobs)]
    end
end

---Causes this node name to spawn these mobs. By default, only spawn inhibitors are respected.
---@param name string
---@param def table
---## Example:
--[[

    aom_mob_spawn.register_spawnnode("aom_spawnnodes:concerning_clover", {
        mobs = {"aom_goblins:goblin"},
        get_mob = function(self, pos)
            return "aom_goblins:goblin"
        end,
        neighbors = {"group:solid"},
        interval = 10,
        chance = 100,
        can_spawn = function(self, pos)
            return not aom_entity_api.has_mobs_in_radius(pos, 40, {"aom_goblins:goblin"}, 0, 3)
        end,
        find_ground_pos = function(pos)
            return aom_mob_spawn.find_ground_pos(pos)
        end,
        on_spawn = function(self, spawn_pos, mob_name)
            local obj = core.add_entity(spawn_pos, mob_name)
            local ent = obj and obj:get_luaentity()
            if not ent then return end
            SIGNAL("on_mob_spawn", spawn_pos, ent)
        end,
    })
]]
function aom_mob_spawn.register_spawnnode(name, def)
    def.node_name = name
    core.register_abm({
        label = "aom_mob_spawn:" .. name,
        nodenames = {name},
        neighbors = def.neighbors,
        interval = def.interval or 10,
        chance = def.chance or 100,
        action = function(pos, node, active_object_count, active_object_count_wider)
            aom_util.did_abm("mob spawn")
            core.after(math.random() * 10, function()
                if not aom_mob_spawn.enabled then return false end
                local find_ground_pos = def.find_ground_pos or aom_mob_spawn.find_ground_pos
                local spawn_pos = find_ground_pos(pos)
                if not spawn_pos then return false end
                if def.can_spawn and not def.can_spawn(def, spawn_pos) then return end
                local mob_name = aom_mob_spawn.get_spawnnode_mob(def, spawn_pos)
                if not core.registered_entities[mob_name] then return end
                if CONDITIONAL("can_mob_spawn_by_spawnnode", spawn_pos, mob_name) == false then return end
                if def.on_spawn then
                    def.on_spawn(def, spawn_pos, mob_name)
                    -- it's the callback's responsibility to signal `on_mob_spawn`
                else
                    local obj = core.add_entity(spawn_pos, mob_name)
                    local ent = obj and obj:get_luaentity()
                    if not ent then return end
                    SIGNAL("on_mob_spawn", spawn_pos, ent)
                end
            end)
        end
    })
end
