-- LUALOCALS < ---------------------------------------------------------
local core, nc
    = core, nc
-- LUALOCALS > ---------------------------------------------------------

nc.amcoremod()

local modname = core.get_current_modname()

function nc.storebox_open_bottom(pos, node, def)
	node = node or core.get_node(pos)
	def = def or core.registered_nodes[node.name] or {}
	return (not def.storebox_access) or def.storebox_access(
		{type = "node", above = {x = pos.x, y = pos.y - 1, z = pos.z},
			under = pos}, pos, node)
end

local function doplace(stack, clicker, pointed_thing, ...)
	local function helper(left, ok, ...)
		if ok then nc.node_sound(pointed_thing.above, "place") end
		return left, ok, ...
	end
	return helper(nc.item_place_node_or_stack(stack, clicker, pointed_thing, ...))
end

function nc.storebox_on_rightclick(pos, node, clicker, stack, pointed_thing)
	if not nc.interact(clicker) then return end
	if (not stack) or stack:is_empty() then return end
	node = node or core.get_node(pos)
	local def = core.registered_items[node.name]
	if not def then return end
	if nc.protection_test(pos, clicker) then return stack end
	if def.storebox_access and (not def.storebox_access(
			pointed_thing, pos, node)) then
		return doplace(stack, clicker, pointed_thing)
	end
	if clicker and clicker.get_wielded_item
	and nc.craft_check(pos, core.get_node(pos), {
			action = "stackapply",
			crafter = clicker,
			pointed = pointed_thing
		}) then
		return clicker:get_wielded_item()
	end
	if def.stack_allow and def.stack_allow(pos, node, stack) == false then
		return doplace(stack, clicker, pointed_thing)
	end
	return nc.stack_add(pos, stack, clicker)
end

function nc.storebox_on_punch(pos, node, puncher, pointed_thing, ...)
	core.node_punch(pos, node, puncher, pointed_thing, ...)
	if not nc.interact(puncher) then return end
	if puncher:get_player_control().sneak then return end
	node = node or core.get_node(pos)
	local def = core.registered_items[node.name]
	if nc.protection_test(pos, puncher) then return end
	if def.storebox_access and (not def.storebox_access(
			pointed_thing, pos, node)) then return end
	if pointed_thing.above.y < pointed_thing.under.y then return end
	return nc.stack_giveto(pos, puncher)
end

function nc.storebox_stack_allow(pos, node, stack)
	node = node or core.get_node(pos)
	local def = core.registered_items[node.name]
	local idef = core.registered_items[stack:get_name()] or {}
	if idef.groups and idef.groups.container
	and idef.groups.container >= def.groups.storebox then return false end
end

function nc.storebox_on_settle_item(pos, node, stack, inside)
	if inside and nc.storebox_open_bottom(pos, node)
	and nc.stack_can_fall_in({
			x = pos.x,
			y = pos.y - 1,
			z = pos.z
		}, stack) then
		return stack
	end
	local def = node and core.registered_items[node.name] or {}
	if def.storebox_access and (not def.storebox_access(
			{type = "node", above = {x = pos.x, y = pos.y + 1, z = pos.z},
				under = pos}, pos, node)) then return stack end
	return nc.stack_add(pos, stack)
end

function nc.storebox_can_item_fall_in(pos, node, stack)
	if not (nc.stack_get(pos):is_empty() or stack:is_empty()) then return end
	local def = node and core.registered_items[node.name] or {}
	if def.storebox_access and ((not def.storebox_access(
				{type = "node", above = {x = pos.x, y = pos.y + 1, z = pos.z},
					under = pos}, pos, node))
		or (not def.storebox_access(
				{type = "node", above = {x = pos.x, y = pos.y - 1, z = pos.z},
					under = pos}, pos, node))) then return end
	return true
end

function nc.storebox_check_item_fall_out(pos, node, stack)
	if not nc.storebox_open_bottom(pos, node) then return end
	if stack:is_empty() then return false end

	local below = {x = pos.x, y = pos.y - 1, z = pos.z}
	if not nc.stack_can_fall_in(below, stack) then
		if not nc.stack_can_fall_in(below, "") then return false end
		nc.stack_set(pos, nc.stack_add(below, stack))
		return false
	end

	nc.stack_set(pos, "")
	nc.item_eject(pos, stack)
	return true
end

nc.storebox_on_stack_change = function(...)
	return nc.storebox_check_item_fall_out(...)
end

nc.storebox_on_falling_check = function(pos)
	return nc.storebox_check_item_fall_out(pos,
		core.get_node(pos), nc.stack_get(pos))
end

nc.register_on_register_item(function(_, def)
		if def.type ~= "node" or (not def.groups) or (not def.groups.storebox) then return end

		if not def.drawtype then
			def.drawtype = "mesh"
			def.visual_scale = nc.z_fight_ratio
			def.mesh = def.mesh or modname .. "_box.obj"
			def.backface_culling = true
			def.use_texture_alpha = def.use_texture_alpha or "clip"
		end

		def.groups.visinv = def.groups.visinv or 1
		def.groups.always_scalable = def.groups.always_scalable or 1
		def.groups.container = def.groups.container or def.groups.storebox

		def.on_construct = def.on_construct or nc.visinv_on_construct
		def.after_destruct = def.after_destruct or nc.visinv_after_destruct

		def.on_rightclick = def.on_rightclick or nc.storebox_on_rightclick
		def.on_punch = def.on_punch or nc.storebox_on_punch

		def.stack_allow = def.stack_allow or nc.storebox_stack_allow

		def.on_settle_item = def.on_settle_item or nc.storebox_on_settle_item
		def.can_item_fall_in = def.can_item_fall_in or nc.storebox_can_item_fall_in

		def.on_stack_change = def.on_stack_change or nc.storebox_on_stack_change
		def.on_falling_check = def.on_falling_check or nc.storebox_on_falling_check
	end)
