local log = sorcery.logger('tap')
minetest.register_node('sorcery:tap',{
	description = 'Tree Tap';
	drawtype = 'mesh';
	mesh = 'sorcery-tap.obj';
	tiles = {
		'default_copper_block.png';
		'default_steel_block.png';
	};
	groups = {
		dig_immediate = 2;
		attached_node = 1;
	};
	sunlight_propagates = true;
	paramtype = 'light', paramtype2 = 'wallmounted';
	selection_box = { type='fixed', fixed = {-0.2,-0.5,-0.35; 0.3,0.1,0.4} };
	collision_box = { type='fixed', fixed = {-0.2,-0.5,-0.35; 0.3,0.1,0.4} };
	node_placement_prediction = '';
	on_place = function(stack,who,where)
		if where.type ~= 'node' then return end
		local bl = minetest.get_node(where.under)
		-- FIXME prevent tapping 'dead' non-tree wood blocks

		local tree = sorcery.tree.get(where.under)
		if not tree or tree.sap == false then return end;

		-- disallow vertical attachment
		if vector.subtract(where.under,where.above).y ~= 0 then return end

		minetest.set_node(where.above, {
			name = 'sorcery:tap';
			param2 = minetest.dir_to_wallmounted(vector.subtract(where.under,where.above))
		})

		stack:take_item(1)
		return stack
	end;
	_sorcery = {
		recipe = {
			note = 'Extract syrups and oils from trees';
		};
	};
})

minetest.register_craft {
	output = 'sorcery:tap';
	recipe = {
		{'','sorcery:screw_steel','basic_materials:steel_bar'};
		{'sorcery:pipe','sorcery:valve','sorcery:screw_steel'};
		{'','sorcery:pipe',''};
	};
}

local sap_interval = 60;
local abm_cache
local abm_cache_time
minetest.register_abm {
	label = 'Sap drip';
	nodenames = {'sorcery:tap'};
	neighbors = {'group:tree'};
	interval = sap_interval;
	chance = 7;
	catch_up = true;
	action = function(pos, node)
		local now = os.time()
		if abm_cache_time == nil or now > abm_cache_time + (sap_interval-1) then
			abm_cache = { treehash = {} }
			abm_cache_time = now
		end
		local tpos = vector.add(pos,minetest.wallmounted_to_dir(node.param2))
		local tnode = minetest.get_node(tpos)
		if tnode.name == 'air' then return end --pathological case
		local tree
		for id,t in pairs(sorcery.data.trees) do
			if t.node == tnode.name then
				tree = t
				goto found
			end
		end do
			return
		end ::found::

		local tposhash = vector.to_string(tpos)
		local live, should_cache
		local mass_leaves, mass_trunk, topnode, prevalidate
		if abm_cache.treehash[tposhash] then
			live = true
			local c = abm_cache.treehash[tposhash]
			mass_leaves = c.mass_leaves
			mass_trunk = c.mass_trunk
			prevalidate = true
		else
			local tbody
			live, tbody = sorcery.lib.node.tree_is_live(tpos)
			if live then
				should_cache = tbody.nodes[tbody.trunk]
				mass_leaves = #(tbody.nodes[tbody.leaves])
				mass_trunk = #(tbody.nodes[tbody.trunk]) * 12
				topnode = tbody.topnode
			end
		end

		if (not live)
			or tree.sap == false
			or not tree.sapliq then return end
		if mass_trunk < 12*3 then return end -- too small

		local mass = mass_leaves + mass_trunk
		local max_mass = 400
		local ltratio = mass_leaves / mass_trunk
		local mratio = mass / max_mass
		local outof = 15 / mratio
		local chance = math.max(1, math.floor(outof - (25 * ltratio))) / 3
		local diceroll = math.random(1,chance)
		-- log.act('rolling dice: ', chance,diceroll, '(lt ratio =', ltratio, '; mass ratio = ', mratio, '; tree mass =', mass, '; outof =', outof)
		if diceroll ~= 1 then return end -- failed roll

		if not prevalidate then
			if minetest.get_natural_light(vector.offset(topnode,0,1,0), 0.5) < 13
				then return false end
		end

		for i=1,8 do
			local at = vector.offset(pos, 0,-i,0)
			if sorcery.lib.node.is_air(at) then goto skip end

			local trough = minetest.get_node(at)
			if minetest.get_item_group(trough.name, 'sorcery_trough') ~= 0 then
				local n = minetest.registered_nodes[trough.name]
				local l = sorcery.register.liquid.db[tree.sapliq]
				local C = sorcery.liquid.constants
				if n._sorcery and n._sorcery.container then
					local ctr = n._sorcery.container
					if not ctr.has or (ctr.has == tree.sapliq and ctr.charge < ctr.max) then
						if ctr.set_node_liq then
							ctr.set_node_liq(at, l, (ctr.charge or 0) + C.glasses_per_bottle)
						else --legacy code, kept for pathological cases
							local nct = (l.containers[trough.name] or l.containers[ctr.empty]) -- oof
							if not nct then
								log.err('no container of type',trough.name,'for sap',n.sapliq,'available')
								return
							end
							if type(nct) == 'string' then -- pathological case
								node.name = nct 
							else
								node.name = nct.make((ctr.charge or 0) + nct.res,1):get_name()
							end
							minetest.swap_node(at, node)
						end
						if should_cache then
							for _,v in pairs(should_cache) do
								abm_cache.treehash[v:to_string()] = {
									mass_leaves = mass_leaves;
									mass_trunk = mass_trunk;
								}
							end
						end
					end
				else
					log.err('item',trough.name,'is marked as a trough but lacks a _sorcery.container property table')
				end
			end
			do return end
		::skip::end
	end;
}
