local FAUCET_PER_SECOND = 122

-- Node funtions

local function update_timer (pos)
	local t = minetest.get_node_timer(pos)
	if not t:is_started() then
		t:start(1.0)
	end
end

local function controller_timer (pos, elapsed)
	local refresh = false
	local meta = minetest.get_meta(pos)

	return refresh
end

local function faucet_flow (pos, meta)
	local angled = minetest.get_node(pos)
	local back = vector.add(pos, minetest.facedir_to_dir(angled.param2))
	local backnode = minetest.get_node(back)
	local backreg = minetest.registered_nodes[backnode.name]

	if not backreg.node_io_can_take_liquid or not backreg.node_io_can_take_liquid(back, backnode, "front")
		or not backreg.node_io_take_liquid then
		return false
	end

	local stack
	if backreg.node_io_get_liquid_stack then
		stack = backreg.node_io_get_liquid_stack(back, backnode, "front", 1)
	end

	if not stack or stack:is_empty() then
		return false
	end

	return stack:get_name(), stack:get_count(), back
end

local function faucet_activate (pos)
	local meta = minetest.get_meta(pos)
	local source, amount, bhind = faucet_flow(pos, meta)

	if meta:get_string("flowing") ~= "" then return false end
	if not source or amount == 0 then return end

	local e = minetest.add_entity(pos, "multifurnace:faucet_flow")
	e:get_luaentity():set_faucet(pos)
	e:get_luaentity():set_source(source)

	meta:set_string("flowing", source)
	meta:set_int("flowing_capacity", amount)
	update_timer(pos)
end

local function faucet_timer (pos, elapsed)
	local refresh = false
	local meta = minetest.get_meta(pos)
	local liquid = meta:get_string("flowing")
	local source, amount, bhind = faucet_flow(pos, meta)

	while true do
		if liquid == "" then break end
		if source ~= liquid or amount <= 0 then
			liquid = ""
			break
		end

		local bhindnode = minetest.get_node(bhind)
		local bhindreg = minetest.registered_nodes[bhindnode.name]

		local target = vector.subtract(pos, {x=0,y=1,z=0})
		local tnode = minetest.get_node(target)
		local treg = minetest.registered_nodes[tnode.name]
		if not treg.node_io_can_put_liquid or not treg.node_io_can_put_liquid(target, tnode, "top")
			or not treg.node_io_room_for_liquid then
			liquid = ""
			break
		end

		local flowcap = amount
		if flowcap > FAUCET_PER_SECOND then
			flowcap = FAUCET_PER_SECOND
		end

		local room = treg.node_io_room_for_liquid(target, tnode, "top", liquid, flowcap)
		if room > 0 and treg.node_io_put_liquid then
			local over = treg.node_io_put_liquid(target, tnode, "top", nil, liquid, room)

			if treg.on_timer then
				update_timer(target)
			end
		else
			liquid = ""
			break
		end

		bhindreg.node_io_take_liquid(bhind, bhindnode, "front", nil, source, room)
		if bhindreg.on_timer then
			update_timer(bhind)
		end

		refresh = true
		break
	end

	meta:set_string("flowing", liquid)

	return refresh
end

-- Node definitions

minetest.override_item("metal_melter:heated_bricks", {
	groups = {cracky = 3, multifurnace = 1},
})

minetest.register_node("multifurnace:controller", {
	description = "Multifurnace Controller",
	tiles = {
		"metal_melter_heatbrick.png", "metal_melter_heatbrick.png", "metal_melter_heatbrick.png",
		"metal_melter_heatbrick.png", "metal_melter_heatbrick.png", "metal_melter_heatbrick.png^multifurnace_controller_face.png",
	},
	groups = {cracky = 3, multifurnace = 1},
	paramtype2 = "facedir",
	is_ground_content = false,
	on_timer = controller_timer,
})

minetest.register_node("multifurnace:port", {
	description = "Multifurnace Port",
	tiles = {
		"metal_melter_heatbrick.png", "metal_melter_heatbrick.png", "metal_melter_heatbrick.png",
		"metal_melter_heatbrick.png", "metal_melter_heatbrick.png^multifurnace_intake_back.png",
		"metal_melter_heatbrick.png^multifurnace_intake_face.png",
	},
	groups = {cracky = 3, multifurnace = 1},
	paramtype2 = "facedir",
	is_ground_content = false,
})

minetest.register_node("multifurnace:faucet", {
	description = "Multifurnace Faucet",
	tiles = {"metal_melter_heatbrick.png"},
	groups = {cracky = 3, multifurnace_accessory = 1},
	drawtype = "nodebox",
	node_box = {
		type = "fixed",
		fixed = {
			{-0.1875, -0.1875, 0.1250, 0.1875, -0.1250, 0.5000},
			{-0.1875, -0.1250, 0.1250, -0.1250, 0.06250, 0.5000},
			{0.1250, -0.1250, 0.1250, 0.1875, 0.06250, 0.5000}
		}
	},
	paramtype = "light",
	paramtype2 = "facedir",
	is_ground_content = false,
	on_rightclick = faucet_activate,
	on_timer = faucet_timer,
})

-- Entity definitions

minetest.register_entity("multifurnace:faucet_flow", {
	initial_properties = {
		physical = false,
		collide_with_objects = false,
		visual = "mesh",
		mesh = "multifurnace_faucet_flow.obj",
		visual_size = {x = 5, y = 5},
		textures = {},
		backface_culling = true,
		pointable = false,
		static_save = false,
	},
	faucet = nil,
	source = "",
	timer = 0,
	set_faucet = function (self, faucet)
		self.faucet = faucet

		-- Set correct orientation in regards to the faucet
		local node = minetest.get_node(faucet)
		local orient = minetest.facedir_to_dir(node.param2)
		orient = vector.multiply(orient, -1)
		local angle = minetest.dir_to_yaw(orient)
		self.object:set_yaw(angle)
	end,
	set_source = function (self, source)
		self.source = source

		-- Set appropriate fluid texture
		local tiles = minetest.registered_nodes[source]
		if not tiles.liquid_alternative_flowing then return end
		local flowing = minetest.registered_nodes[tiles.liquid_alternative_flowing]
		if not flowing.tiles or type(flowing.tiles[1]) ~= "string" then return end

		self.object:set_properties({textures = {
			flowing.tiles[1]
		}})
	end,
	on_step = function (self, dt)
		self.timer = self.timer + 1

		-- Remove self when the faucet is destroyed or is no longer marked as flowing
		if self.timer >= 10 then
			local node = minetest.get_node(self.faucet)
			if not node or node.name ~= "multifurnace:faucet" then
				self.object:remove()
				return
			end

			local meta = minetest.get_meta(self.faucet)
			if meta:get_string("flowing") ~= self.source then
				self.object:remove()
				return
			end

			self.timer = 0
		end
	end
})
