-- LUALOCALS < ---------------------------------------------------------
local ItemStack, math, minetest, nodecore, pairs, string
    = ItemStack, math, minetest, nodecore, pairs, string
local math_random, string_format
    = math.random, string.format
-- LUALOCALS > ---------------------------------------------------------

-- Active ItemStack Modifiers

-- Definition:
--- itemnames: {"mod:itemname", "group:name"}
--- interval: integer,
--- chance: integer,
--- func: function(stack, data) end
-- Data:
--- {pos, node}
--- {player, inv, list, slot}

nodecore.register_aism,
nodecore.registered_aisms
= nodecore.mkreg()

local aismidx = {}
local function defadd(key, def)
	aismidx[key] = aismidx[key] or {}
	aismidx[key][def] = true
end
minetest.after(0, function()
		for _, def in pairs(nodecore.registered_aisms) do
			for _, name in pairs(def.itemnames) do
				if name:sub(1, 6) == "group:" then
					for k, v in pairs(minetest.registered_items) do
						if v and v.groups and v.groups[name:sub(7)] then
							defadd(k, def)
						end
					end
				else
					defadd(name, def)
				end
			end
		end
		local keys = 0
		local defs = 0
		local peak = 0
		for _, v in pairs(aismidx) do
			keys = keys + 1
			local n = 0
			for _ in pairs(v) do n = n + 1 end
			defs = defs + n
			if n > peak then peak = n end
		end
		nodecore.log("info", string_format("register_aism: %d keys, %d defs, %d peak", keys, defs, peak))
	end)

local function checkrun(def, stack, data)
	if nodecore.stasis and not def.ignore_stasis then return end
	if def.chance and def.chance > 1 and math_random(1, def.chance) ~= 1 then return end
	if def.interval and def.interval > 1 and (minetest.get_gametime() % def.interval) ~= 0 then return end
	stack = def.action(stack, data)
	if stack and data.set then data.set(ItemStack(stack)) end
end

local function checkstack(stack, data)
	local defs = aismidx[stack:get_name()]
	if not defs then return end
	for def in pairs(defs) do
		checkrun(def, stack, data)
	end
end
nodecore.aism_check_stack = checkstack

nodecore.register_limited_abm({
		label = "aism schedule",
		nodenames = {"group:visinv"},
		interval = 1,
		chance = 1,
		ignore_stasis = true,
		action = function(pos, node)
			return checkstack(nodecore.stack_get(pos), {
					pos = pos,
					node = node,
					set = function(s)
						return nodecore.stack_set(pos, s)
					end
				})
		end
	})

nodecore.interval(1, function()
		for _, player in pairs(minetest.get_connected_players()) do
			local pos = player:get_pos()
			pos.y = pos.y + player:get_properties().eye_height
			local inv = player:get_inventory()
			for lname, list in pairs(inv:get_lists()) do
				for slot, stack in pairs(list) do
					checkstack(stack, {
							pos = pos,
							player = player,
							inv = inv,
							list = lname,
							slot = slot,
							set = function(s)
								return inv:set_stack(lname, slot, s)
							end
						})
				end
			end
		end
	end)

nodecore.register_item_entity_step(function(self, dtime)
		local t = (self.aismtimer or 0) + dtime
		while t >= 1 do
			t = t - 1
			local pos = self.object:get_pos()
			if not pos then return end
			local setstack
			checkstack(ItemStack(self.itemstring), {
					pos = pos,
					obj = self.object,
					ent = self,
					set = function(s) setstack = s end
				})
			if setstack then
				if setstack:is_empty() then
					return self.object:remove()
				end
				self.itemstring = setstack:to_string()
			end
		end
		self.aismtimer = t
	end)
