-- LUALOCALS < ---------------------------------------------------------
local math, minetest, nodecore, pairs, unpack
    = math, minetest, nodecore, pairs, unpack
local math_random
    = math.random
-- LUALOCALS > ---------------------------------------------------------

local genlabels = 0

function nodecore.register_limited_abm(def)
	def = nodecore.underride(def, {
			limited_queue = {},
			limited_seen = {},
			limited_qty = 0,
			limited_max = 1000,
			limited_interval = 1,
			limited_jitter = 0.05,
			limited_action = def.action or function() end,
			catch_up = false
		})

	if not def.label then
		def.label = minetest.get_current_modname() .. ":" .. genlabels
		genlabels = genlabels + 1
	end
	def.limited_alert = def.limited_alert or def.limited_max

	def.action = function(pos, ...)
		local hash = minetest.hash_node_position(pos)
		local seen = def.limited_seen
		if seen[hash] then return end
		seen[hash] = true

		local q = def.limited_queue
		local max = def.limited_max
		local nqty = def.limited_qty + 1
		if #q < max then
			q[#q + 1] = {pos, ...}
		else
			local r = math_random(1, nqty)
			if r <= #q then q[r] = {pos, ...} end
		end
		def.limited_qty = nqty
	end

	local function pumpq()
		minetest.after(def.limited_interval
			- def.limited_jitter
			+ def.limited_jitter * math_random() * 2,
			pumpq)

		if def.limited_qty >= def.limited_alert then
			minetest.log("limited abm \"" .. def.label .. "\" filled ("
				.. def.limited_qty .. "/" .. def.limited_max .. ")")
		end

		local act = def.limited_action
		for _, args in pairs(def.limited_queue) do
			local pos = args[1]
			local node = pos and args[2]
			local nn = node and minetest.get_node_or_nil(pos)
			if nn and nn.name == node.name then
				act(unpack(args))
			end
		end

		def.limited_queue = {}
		def.limited_seen = {}
		def.limited_qty = 0
	end
	pumpq()

	return minetest.register_abm(def)
end
