-- LUALOCALS < ---------------------------------------------------------
local minetest, pairs, piredo_api, piredo_map, piredo_player,
      piredo_schems, string, vector
    = minetest, pairs, piredo_api, piredo_map, piredo_player,
      piredo_schems, string, vector
local string_match
    = string.match
-- LUALOCALS > ---------------------------------------------------------

local api = piredo_api.get_mod_api()
local modname = minetest.get_current_modname()

local holdername = modname .. ":fake_item_holder"
do
	local holderdef = {}
	for k, v in pairs(minetest.registered_nodes["piredo_terrain:ambient"]) do holderdef[k] = v end
	holderdef.name = nil
	holderdef.buildable_to = false
	holderdef.groups = {solid_top = 1}
	minetest.register_node(holdername, holderdef)
end

piredo_api.add_groups("piredo_terrain:luxury_decor__simple_wooden_table", "solid_top_extend")

do
	local param2dirs = {
		{x = 0, y = 0, z = 1},
		{x = 1, y = 0, z = 0},
		{x = 0, y = 0, z =-1},
		[0] = {x =-1, y = 0, z = 0}
	}
	minetest.register_abm({
			nodenames = {"group:solid_top_extend"},
			interval = 1,
			chance = 1,
			action = function(pos, node)
				local dir = param2dirs[node.param2]
				if not dir then return end
				local p = vector.add(pos, dir)
				local n = minetest.get_node(p)
				if n.name ~= holdername then
					minetest.set_node(p, {name = holdername})
				end
			end
		})
end

piredo_api.add_groups("piredo_terrain:xdecor__table", "solid_top")
piredo_api.add_groups("piredo_terrain:luxury_decor__bright_wall_wooden_shelf", "solid_top")
piredo_api.add_groups("piredo_terrain:luxury_decor__dark_wall_wooden_shelf", "solid_top")
piredo_api.add_groups("piredo_terrain:luxury_decor__simple_wooden_table", "solid_top")

local solid_drawtypes = {
	normal = true,
	glasslike = true,
	glasslike_framed = true,
	glasslike_framed_optional = true,
	allfaces = true,
	allfaces_optional = true
}
local function is_solid_top(bnode)
	if minetest.get_item_group(bnode.name, "solid_top") > 0
	then return true end

	local def = minetest.registered_nodes[bnode.name]
	if not def then return end
	if def.walkable and def.pointable
	and solid_drawtypes[def.drawtype] then return true end

	if string_match(def.name, "stairs__slab")
	or string_match(def.name, "stairs__stair") then
		return bnode.param2 >= 20
	end
end

function api.placement(stack, placer, pointed, ...)
	piredo_player.player_hand_movement(placer)

	local node = minetest.get_node(pointed.under)
	local invoked = api.tryinvoke(pointed, node, stack, placer, "on_give_")
	if invoked ~= false then return invoked end
	invoked = api.tryinvoke(pointed, node, stack, placer, "on_apply_")
	if invoked ~= false then return invoked end

	-- Cannot drop items in first couple of rooms (would lose access)
	-- or in adjoining passages (too easy to lose track)
	local room, roompos, roommax = piredo_map.get_current_room()
	if not room
	or (piredo_schems[room] and piredo_schems[room].disallow_changes)
	or pointed.above.x < roompos.x or pointed.above.z < roompos.z
	or pointed.above.x > roommax.x or pointed.above.z > roommax.z
	then return end

	-- Need a valid node placement location.
	if not pointed or pointed.type ~= "node" then return end

	-- Torchlike items handle wall placement fine, but everything
	-- else is assumed to be affected by gravity, so place on
	-- the surface below.
	local idef = stack:get_definition()
	if not (idef and idef.drawtype == "torchlike") then
		pointed.under = vector.offset(pointed.above, 0, -1, 0)
	end

	-- Cannot place items "floating", must be on top of a
	-- walkable item, which also means not another collectable.
	-- This prevents goofy hovering items.
	local bnode = minetest.get_node(pointed.under)
	if not is_solid_top(bnode) then return end

	-- Disallow placing of "signlike" flat items above
	-- the player's eye height because it might make
	-- them too easy to lose.
	if placer and idef and idef.drawtype == "signlike" then
		local pos = placer:get_pos()
		if pointed.above.y > pos.y + placer:get_properties().eye_height
		then return end
	end

	-- Cannot drop an item "through" invisible barriers
	if api.checkbarriers(pointed.above, placer) then
		api.ratelimitskip()
		return
	end

	local before = stack:to_string()
	local after = minetest.item_place(stack, placer, pointed, ...)
	if before ~= after:to_string() then
		minetest.sound_play("default_place_node_hard", {pos = pointed.above}, true)
	end
	return after
end
