-- the math basically needs to be rewritten from scratch by someone who isn't
-- dyscalculic but 
sorcery.lathe = {
	techs = {
		cut = {dmg = true};
		intaglio = {consume = true};
	};
	tools = {
		sword = 'cut', knife = 'cut', blade = 'cut';
	};
	recipes = {};
	register = function(def)
		local recipes = sorcery.lathe.recipes
		if not recipes[def.input] then recipes[def.input] = {} end
		local rs = recipes[def.input][def.tech]
		if not rs
			then recipes[def.input][def.tech] = { def }
			else rs[#rs+1] = def
		end
	end;
	tooltech = function(tool)
		if type(tool) ~= 'string' then tool = tool:get_name() end
		for g,t in pairs(sorcery.lathe.tools) do
			if minetest.get_item_group(tool, g) ~= 0 then
				return t
			end
		end
	end;
}

local R = sorcery.lathe.recipes
sorcery.lathe.get = function(pos,idx,howmany)
	local inv = minetest.get_meta(pos):get_inventory()
	local tool = inv:get_stack('tool',1)
	local tech = sorcery.lathe.tooltech(tool)
	if not tech then return nil end
	local wkpc = inv:get_stack('workpiece',1)
	local rec = R[wkpc:get_name()][tech][idx]
	local outn = ItemStack(rec.output):get_count()
	howmany = howmany or math.floor(wkpc:get_count()/(rec.mass or 1))*outn
	local ntimes = math.floor(howmany / outn)

	local tmat = sorcery.itemclass.get(tool,'material')
	local wmat = sorcery.itemclass.get(wkpc,'material')
	local dur = 100
	local lfac = 1
	if tmat then
		local dur = tmat.data.durability or dur
		lfac = (wmat and wmat.data.level or 1) /
			(tmat.data.maxlevel or tmat.data.level or 1)
	end
	local ch = 65535 / dur
	local wear = 2 * (ch * rec.cost * ntimes * lfac);
	return {
		tool = tool, wkpc = wkpc;
		cost = rec.cost * ntimes;
		wear = wear;
		ntimes = ntimes;
		tqty = ntimes * (rec.mass or 1), outn = outn;
		tech = tech;
		rec = rec;
		inv = inv;
	}
end

sorcery.lathe.update = function(pos)
	local inv = minetest.get_meta(pos):get_inventory()
	local tool = inv:get_stack('tool',1)
	local wkpc = inv:get_stack('workpiece',1)
	if tool:is_empty() or wkpc:is_empty() then
		if not inv:is_empty('preview') then
			for i=1, inv:get_size('preview') do
				inv:set_stack('preview',i,ItemStack())
			end
		end
		return
	end

	local tmat = sorcery.itemclass.get(tool:get_name(),'material')
	local wmat = sorcery.itemclass.get(wkpc:get_name(),'material')
	-- obey level restrictions. TODO honor Rend
	if (wmat and wmat.data.level or 0) > (tmat and (tmat.data.maxlevel or tmat.data.level) or 0) then
		return
	end
	
	local tech = sorcery.lathe.tooltech(tool)
	local rec = R[wkpc:get_name()][tech]
	tech = sorcery.lathe.techs[tech]

	-- fill in the preview slots
	local j = 1
	for i=1, inv:get_size 'preview'  do
		local stk = ItemStack()
		local os = rec[i] and ItemStack(rec[i].output)
		if rec[i] and minetest.registered_items[os:get_name()] and (rec[i].mass == nil or rec[i].mass <= wkpc:get_count()) then
			local l = sorcery.lathe.get(pos, i)
			local max = l.ntimes
			--math.floor(wkpc:get_count() / (rec[i].mass or 1))
			if tech.dmg then
				local lw = l.wear
				while lw + tool:get_wear() > 65535 do
					max = max - 1
					if max == 0 then break end
					lw = sorcery.lathe.get(pos, i, max).wear
				end
			elseif tech.consume then
				max = math.min(max, tool:get_count())
			end
			if max > 0 then
				stk = ItemStack(rec[i].output)
				local ct = math.min(stk:get_count() * max, stk:get_stack_max())
				ct = ct - (ct % os:get_count())
				stk:set_count(ct)
			end
		end
		inv:set_stack('preview',i,stk)
		j = j + 1
	end

	-- make sure remaining slots are clear
	for i = j, inv:get_size('preview') do
		inv:set_stack('preview',i,ItemStack())
	end

end

local box = {
	type='fixed';
	fixed = {
		-0.7, -0.5, -0.3;
		 0.57,  0.2,  0.3;
	}
}
minetest.register_node('sorcery:lathe', {
	description = 'Lathe';
	drawtype = 'mesh';
	mesh = 'sorcery-lathe.obj';
	sunlight_propagates = true;
	paramtype = 'light';
	paramtype2 = 'facedir';
	selection_box = box;
	collision_box = box;
	groups = { cracky = 2; sorcery_tech = 1; attached_node = 1 }; -- 2=liquid
	after_dig_node = sorcery.lib.node.purge_only { 'workpiece', 'tool' };
	tiles = {
		'default_wood.png';
		'default_steel_block.png';
		'default_bronze_block.png';
	};
	on_construct = function(pos)
		local m = minetest.get_meta(pos)
		local i = m:get_inventory()
		i:set_size('workpiece', 1);
		i:set_size('tool', 1);
		i:set_size('preview', 8);
		m:set_string('formspec', [[
			formspec_version[3] size[10.25,8]
			list[context;tool;1.25,1;1,1]
			list[context;workpiece;2.75,1;1,1]
			list[context;preview;5.25,0.25;4,2]
			list[current_player;main;0.25,3;8,4]

			listring[current_player;main] listring[context;workpiece]
			listring[current_player;main] listring[context;tool]
			listring[current_player;main] listring[context;preview]
			listring[current_player;main]
		]])
	end;

	allow_metadata_inventory_move = function() return 0 end;
	allow_metadata_inventory_put = function(pos, list, idx, stack, user)
		local inv = minetest.get_meta(pos):get_inventory()
		if list == 'tool' then
			local s_wkpc = inv:get_stack('workpiece', 1)
			local tech = sorcery.lathe.tooltech(stack)
			if tech and (s_wkpc:is_empty()
			or (R[s_wkpc:get_name()]       ~= nil and
				R[s_wkpc:get_name()][tech] ~= nil))
					then return stack:get_count() end
		elseif list == 'workpiece' then
			local s_tool = inv:get_stack('tool', 1)
			if R[stack:get_name()] then
				if s_tool:is_empty()
				or R[stack:get_name()][sorcery.lathe.tooltech(s_tool)]
					then return stack:get_count() end
			end
		end

		return 0
	end;
	allow_metadata_inventory_take = function(pos, list, idx, stack, user)
		if list == 'preview' then
			local l = sorcery.lathe.get(pos,idx,stack:get_count())
			if stack:get_count() % l.outn == 0 then
				return stack:get_count()
			else return 0 end
		else return stack:get_count() end
	end;
	on_metadata_inventory_put = sorcery.lathe.update;
	on_metadata_inventory_take = function(pos, list, idx, stack, user)
		if list == 'preview' then
			local l = sorcery.lathe.get(pos,idx,stack:get_count())
			if sorcery.lathe.techs[l.tech].consume then
				l.tool:take_item(l.cost)
			elseif sorcery.lathe.techs[l.tech].dmg then
				l.tool:add_wear(l.wear)
			end
			l.wkpc:take_item(l.tqty)
			l.inv:set_stack('tool', 1, l.tool)
			l.inv:set_stack('workpiece', 1, l.wkpc)
			if l.rec.leftover then
				sorcery.lib.node.insert(ItemStack(l.rec.leftover), 'workpiece', pos, user, l.inv)
			end
			minetest.sound_play('sorcery_clank', { pos = pos, gain = 0.9 })
		end
		sorcery.lathe.update(pos)
	end;

})

minetest.register_craft {
	output = 'sorcery:lathe';
	recipe = {
		{'default:stick','basic_materials:gear_steel','default:steel_ingot'};
		{'default:bronze_ingot','basic_materials:steel_bar','default:bronze_ingot'};
		{'group:wood','','group:wood'};
	};
}
