-- Facade Shaper
-- This machine serves the same purpose as the circular saw from moreblocks or the milling
-- maching from mymillwork. Namely, it provides a tool for creating shaped blocks that does
-- not rely on using recipes.


-- Balancing output per 1 input block with respect to apparent volume of output shape.
-- All current shapes are added, but shapes not present in this table will still be produced
-- one at a time — if that is the desired quantity, adding them is not required.
local output_ratios = {
	bannerstone = 1,
	bannerstone_corner = 1,
	centerstone = 1,
	column = 1,
	column_corner = 1,
	corbel = 1,
	corbel_corner = 1,
	corbel_corner_inner = 1,
	carved_stone_a = 1,
	carved_stone_a_corner = 1,
	rgspro = 2,
	rgspro_inner_corner = 1,
	rgspro_outer_corner = 1,
	corner_bricks = 2,
	columnia_mid = 4,
	columnia_bottom = 1,
	columnia_top = 1,
	columnia_crosslink = 1,
	columnia_link = 4,
	columnia_linkdown = 4,
}

-- The material to be used for buttons when no material is actually loaded.
-- It should be a generic material for which all the facade shapes are defined.
local demo_material = "default:stone"

-- Whether the facade should obey area protection for the inventories (as machines in technic mod)
-- or allow anybody to use them, but disallow the removal of machine itself (like circular saw in moreblocks)
local protect_inventories = false



local function prepare_formspec (material_name)

	local output = string.gsub(material_name, "^.*:", "facade:")

	local shaper_formspec =

	"size[8,11;]"..
	"label[0,0;" .. "Choose shape to produce:" .. "]"..

	-- row 1, blocky shapes
	"item_image_button[0,0.5;1,1;" .. output .. "_bannerstone" .. ";bannerstone; ]"..
	"item_image_button[1,0.5;1,1;" .. output .. "_bannerstone_corner" .. ";bannerstone_corner; ]"..
	"item_image_button[2,0.5;1,1;" .. output .. "_centerstone" .. ";centerstone; ]"..
	"item_image_button[3,0.5;1,1;" .. output .. "_carved_stone_a" .. ";carved_stone_a; ]"..
	"item_image_button[4,0.5;1,1;" .. output .. "_carved_stone_a_corner" .. ";carved_stone_a_corner; ]"..
	"item_image_button[5,0.5;1,1;" .. output .. "_column" .. ";column; ]"..
	"item_image_button[6,0.5;1,1;" .. output .. "_column_corner" .. ";column_corner; ]"..

	-- row 2, corbel
	"item_image_button[0,1.5;1,1;" .. output .. "_corbel" .. ";corbel; ]"..
	"item_image_button[1,1.5;1,1;" .. output .. "_corbel_corner_inner" .. ";corbel_corner_inner; ]"..
	"item_image_button[2,1.5;1,1;" .. output .. "_corbel_corner" .. ";corbel_corner; ]"..

	-- row 3, cornice
	"item_image_button[0,2.5;1,1;" .. output .. "_rgspro" .. ";rgspro; ]"..
	"item_image_button[1,2.5;1,1;" .. output .. "_rgspro_inner_corner" .. ";rgspro_inner_corner; ]"..
	"item_image_button[2,2.5;1,1;" .. output .. "_rgspro_outer_corner" .. ";rgspro_outer_corner; ]"

	-- row 4, columnia
	if not minetest.get_modpath("columnia") then
		shaper_formspec = shaper_formspec ..
		"item_image_button[0,3.5;1,1;" .. output .. "_columnia_mid" .. ";columnia_mid; ]"..
		"item_image_button[1,3.5;1,1;" .. output .. "_columnia_bottom" .. ";columnia_bottom; ]"..
		"item_image_button[2,3.5;1,1;" .. output .. "_columnia_crosslink" .. ";columnia_crosslink; ]"..
		"item_image_button[3,3.5;1,1;" .. output .. "_columnia_link" .. ";columnia_link; ]"..
		"item_image_button[4,3.5;1,1;" .. output .. "_columnia_linkdown" .. ";columnia_linkdown; ]"

		-- this code is a provision in case top column pieces enter service
		if minetest.registered_nodes[output .. "_columnia_top"] then
			shaper_formspec = shaper_formspec ..
			"item_image_button[5,3.5;1,1;" .. output .. "_columnia_top" .. ";columnia_top; ]"
		end

	end

	-- row 5 for shapes which are not available for all materials
	-- only one such shape exists so far, but more should be easy to add here

	if minetest.registered_nodes[output .. "_corner_bricks"] then
		shaper_formspec = shaper_formspec ..
		"item_image_button[0,4.5;1,1;" .. output .. "_corner_bricks" .. ";corner_bricks; ]"
	end

	-- inventory part

	shaper_formspec = shaper_formspec ..

	"label[0, 5.5;".."In:".."]"..
	"list[current_name;src;1,5.5;1,1;]"..
	"label[3, 5.5;".."Out:".."]"..
	"list[current_name;dst;4,5.5;4,1;]"..

	"list[current_player;main;0,7;8,4;]"..
	"listring[current_name;dst]"..
	"listring[current_player;main]"..
	"listring[current_name;src]"..
	"listring[current_player;main]"

	return(shaper_formspec)

end


-- a simple check for compatibile materials
local function check_material_applicability (material)
	-- using centerstone node here, since it appears to be both one of the oldest
	-- and defined for all materials as well, making it suitable for a quick check
	if minetest.registered_nodes[string.gsub(material, "^.*:", "facade:") .. "_centerstone"] then
		return true
	else
		return false
	end
end


-- update the buttons to show shapes made from the actual material
local function update_formspec_put (pos, listname, index, stack, player)

	if protect_inventories and minetest.is_protected(pos, player:get_player_name()) then
		return
	end

	if listname ~= "src" then
		return
	end

	local meta = minetest.get_meta(pos)
	local material_name = stack:get_name()

	if check_material_applicability(material_name) then
		meta:set_string("formspec", prepare_formspec(material_name))
	else
		return
	end

end


-- update the buttons to show shapes made from demo material if all material is removed
local function update_formspec_take (pos, listname, index, stack, player)

	if protect_inventories and minetest.is_protected(pos, player:get_player_name()) then
		return
	end

	if listname ~= "src" then
		return
	end

	local meta = minetest.get_meta(pos)
	local inv = meta:get_inventory()

	if inv:is_empty("src") then
		meta:set_string("formspec", prepare_formspec(demo_material))
	end

	return

end


-- disallow putting in materials which are not supported
local function check_inventory_put (pos, listname, index, stack, player)

	if protect_inventories and minetest.is_protected(pos, player:get_player_name()) then
		return 0
	end

	if listname ~= "src" then
		return 0
	end

	local material_name = stack:get_name()

	if check_material_applicability(material_name) then
		return(stack:get_count())
	else
		return 0
	end
end


local function check_inventory_take (pos, listname, index, stack, player)

	if protect_inventories and minetest.is_protected(pos, player:get_player_name()) then
		return 0
	end

	if listname ~= "src" and listname ~= "dst" then
		return 0
	end

	return(stack:get_count())

end

local function check_inventory_move (pos, from_list, from_index, to_list, to_index, count, player)

	if protect_inventories and minetest.is_protected(pos, player:get_player_name()) then
		return 0
	end

	return(stack:get_count())

end

-- process the form fields and convert source material to desired shapes
local function form_handler(pos, formname, fields, sender)

	if protect_inventories and minetest.is_protected(pos, sender:get_player_name()) then
		return
	end

	if fields.quit then
		return
	end

	local meta = minetest.get_meta(pos)
	local inv = meta:get_inventory()

	if inv:is_empty("src") then
		return
	end

	local inputstack = inv:get_stack("src", 1)
	local inputname = inputstack:get_name()

	for shape,_ in pairs(fields) do

		local result = string.gsub(inputname, "^.*:", "facade:") .. "_" .. shape

		-- one can never be overly paranoid, unlike the quick check before, this one is precise
		if not minetest.registered_nodes[result] then
			return
		end

		-- output quantities are adjusted to preserve roughly same mass of resulting products
		if output_ratios[shape] then
			result = result .. " " .. output_ratios[shape]
		end

		if not inv:room_for_item("dst", result) then
			return
		end

		inputstack:take_item(1)
		inv:set_stack("src", 1, inputstack)
		inv:add_item("dst", result)

	end
	return
end

local function check_removability (pos, player)

	local meta = minetest.get_meta(pos)
	local owner = meta:set_string("owner")
	local pname = player:get_player_name()
	local inv = meta:get_inventory()

	-- owner may always remove the device
	if owner and owner ~= "" and pname and pname ~= "" and owner == pname then
		if inv:is_empty("src") and inv:is_empty("dst") then
			return true
		else
			return false
		end
	end

	if minetest.is_protected(pos, player:get_player_name()) then
		return false
	end

	if inv:is_empty("src") and inv:is_empty("dst") then
		return true
	end

	return false

end


minetest.register_node("facade:shaper", {
	description = "Shaper Machine",
	drawtype = "nodebox",
	node_box = {
		type = "fixed",
		fixed = {
			-- base
			{-1/2, -1/2, -1/2, 1/2, -14/32, 1/2},
			-- back
			{-8/32, -1/2, 12/32, 8/32, 1/2, 16/32},
			-- table
			{-8/32, -4/32, -16/32, 8/32, 4/32, 16/32},
			-- rear table sliding support
			{-16/32, -4/32, 12/32, 16/32, 4/32, 16/32},
			-- front table sliding support
			{-8/32, -14/32, -12/32, 8/32, -4/32, -16/32},
			-- top tool beam
			{-4/32, 16/32, -8/32, 4/32, 12/32, 12/32},
			-- cutter holder
			{-2/32, 7/32, -2/32, 2/32, 14/32, 2/32},
			-- cutter
			{-1/128, 6/32, -1/32, 1/128, 7/32, 1/32},
		},
	},
	selection_box = {
			type = "fixed",
			fixed = {
				{-1/2, -1/2, -1/2, 1/2, 1/2, 1/2},
			},
	},
	tiles = {	"facade_shaper_top.png",
			"facade_shaper_bottom.png",
			"facade_shaper_right.png",
			"facade_shaper_left.png",
			"facade_shaper_back.png",
			"facade_shaper_front.png"},
	groups = { oddly_breakable_by_hand=2, cracky=3, dig_immediate=1 },
	paramtype = "light",
	paramtype2  = "facedir",
	legacy_facedir_simple = true,
	on_construct = function(pos)
		local meta = minetest.get_meta(pos)
		meta:set_string("formspec", prepare_formspec(demo_material))
		local inv = meta:get_inventory()
		inv:set_size("src", 1)
		inv:set_size("dst", 4)
	end,
	after_place_node = function(pos, placer)
		local meta = minetest.get_meta(pos)
		local owner = placer and placer:get_player_name() or ""
		meta:set_string("owner", owner)
		if owner then
			meta:set_string("infotext", ("Facade Shaper (owned by %s)"):format(owner))
		else
			meta:set_string("infotext", "Facade Shaper")
		end
	end,
	can_dig = check_removability,
	allow_metadata_inventory_put  = check_inventory_put,
	allow_metadata_inventory_take = check_inventory_take,
	allow_metadata_inventory_move = check_inventory_move,
	on_metadata_inventory_put = update_formspec_put,
	on_metadata_inventory_take = update_formspec_take,
	on_receive_fields = form_handler,
})


minetest.register_craft({
	output = 'facade:shaper',
	recipe = {
		{'', 				'default:diamond',	''				},
		{'default:steel_ingot',	'default:steelblock',	'default:steel_ingot'	},
		{'',				'default:steelblock' , 				''	},

	},
})
