local box = {
	type = 'fixed';
	fixed = {
		-0.4, -0.5, -0.5;
		 0.4,  0.5,  0.5;
	};
}

local function
findextract(herb)
	for name, e in pairs(sorcery.data.extracts) do
		local found = false
		if sorcery.lib.item.groupmatch(e[1], herb) then
			return name, {
				color = e[2];
				reqamt = e[3] or 3;
				reqtime = e[4] or 75;
			}
		end
	end
end

local function
distiller_setformspec(pos,running)
	local m = minetest.get_meta(pos)
	local inv = m:get_inventory()

	local arrow = 'gui_furnace_arrow_bg.png'
	if running then
		local reqtime, timeleft = m:get_float 'reqtime', m:get_float 'timeleft'
		local pct = 100*((reqtime-timeleft) / reqtime)
		arrow = arrow .. string.format('^[lowpart:%u%%:gui_furnace_arrow_fg.png', pct)
	end
	arrow = arrow .. "^[transformR270"

	local form = string.format([[
		formspec_version[4] size[10.25,8]
		list[context;herb;2.75,0.25;1,1]
		list[context;bottle;2.75,1.50;1,1]
		list[context;output;5.25,0.25;2,2]
		image[4,0.75;1,1;%s]
		list[current_player;main;0.25,3;8,4]

		listring[current_player;main] listring[context;herb]
		listring[current_player;main] listring[context;bottle]
		listring[current_player;main] listring[context;output]
	]], arrow)

	m:set_string('formspec', form)
end

minetest.register_node('sorcery:distiller', {
	description = 'Distiller';
	drawtype = 'mesh';
	mesh = 'sorcery-distiller.obj';
	sunlight_propagates = true;
	paramtype = 'light';
	paramtype2 = 'facedir';
	after_dig_node = sorcery.lib.node.purge_container;
	tiles = {
		'default_water.png';
		'default_wood.png';
		"default_steel_block.png";
		"default_copper_block.png";
		'default_glass.png';
	};
	groups = {
		cracky = 2, heavy = 1;
		sorcery_alchemy = 1;
		sorcery_tech = 1;
		attached_node = 1;
	};
	_sorcery = {
		recipe = {
			note = 'Use grain alcohols to make extracts';
		};
	};
	selection_box = box, collision_box = box;
	on_construct = function(pos)
		local meta = minetest.get_meta(pos)
		local inv = meta:get_inventory()
		inv:set_size('herb', 1)
		inv:set_size('bottle', 1)
		inv:set_size('output', 4)
		distiller_setformspec(pos)
	end;

	on_timer = function(pos,elapsed)
		local cont = true
		local function stop()
			minetest.get_node_timer(pos):stop()
			cont = false
		end

		local m = minetest.get_meta(pos)
		local inv = m:get_inventory()
		local herb = inv:get_stack('herb',1)
		local bottle = inv:get_stack('bottle',1)
		if herb:get_count() == 0 or bottle:get_count() == 0 then
			stop()
		else
			local timeleft, reqtime = m:get_float 'timeleft', m:get_float 'reqtime'
			if timeleft > elapsed then
				timeleft = timeleft - elapsed
			else
				local iters = 1 + ((elapsed-timeleft) / reqtime)
				iters = math.min(bottle:get_count(), iters)

				if iters == 0 then stop() else
					local ename, e = findextract(herb:get_name())
					timeleft = reqtime
					bottle:take_item(iters)
					for i = 1, iters do
						herb:take_item(e.reqamt)
						sorcery.lib.node.insert(ItemStack('sorcery:extract_'..ename),
												'output', nil, nil, inv)
						if herb:get_count() < e.reqamt then
							stop()
							break
						end
					end
					inv:set_stack('herb',1,herb)
					inv:set_stack('bottle',1,bottle)
				end
			end

			if cont then m:set_float('timeleft', timeleft) end
		end

		distiller_setformspec(pos, cont)
		return cont
	end;

	allow_metadata_inventory_move = function(pos, flst, fidx, tlst, tidx, q, who)
		return 0
	end;

	allow_metadata_inventory_put = function(pos, list, idx, stack, who)
		if list == 'herb' then
			if findextract(stack:get_name()) then return stack:get_count() end
		elseif list == 'bottle' then
			local alc = sorcery.itemclass.get(stack, 'alcohol')
			if not alc then return 0 end
			if alc.kind == 'pure' then return stack:get_count() end
		end
		return 0
	end;

	on_metadata_inventory_put = function(pos, list, idx, stack, who)
		local m = minetest.get_meta(pos)
		local timer = minetest.get_node_timer(pos)
		local inv = m:get_inventory()
		if inv:is_empty 'herb' then return end
		local herb = inv:get_stack('herb',1)

		local ename, e = findextract(herb:get_name())
		if herb:get_count() < e.reqamt then return end

		if not timer:is_started() then
			m:set_float('reqtime', e.reqtime)
			m:set_float('timeleft', e.reqtime)
			timer:start(5)
			distiller_setformspec(pos,true)
		end
	end;
	on_metadata_inventory_take = function(pos, list, idx, stack, who)
		local m = minetest.get_meta(pos)
		local inv = m:get_inventory()
		local function stop()
			minetest.get_node_timer(pos):stop()
			distiller_setformspec(pos,false)
		end
		if inv:is_empty 'herb' or inv:is_empty 'bottle' then
			stop()
		else
			local herb = m:get_inventory():get_stack('herb',1)

			local ename, e = findextract(herb:get_name())
			if herb:get_count() < e.reqamt then stop() end
		end
	end;
})

minetest.register_craft {
	output = 'sorcery:distiller';
	recipe = {
		{'group:sorcery_screw','sorcery:herb_crusher',   'group:sorcery_screw'};
		{'default:stick',      'stairs:slab_copperblock','default:stick'};
		{'default:stick',      'default:steel_ingot',    'default:stick'};
	};
}