-- LUALOCALS < ---------------------------------------------------------
local core, math, nc, pairs, table, tonumber
    = core, math, nc, pairs, table, tonumber
local math_random, table_concat
    = math.random, table.concat
-- LUALOCALS > ---------------------------------------------------------

local modname = core.get_current_modname()

local function growparticles(pos, rate, width)
	nc.soaking_particles(pos, rate, 10, width, modname .. ":leaves_bud")
end

local sproutcost = 2000
nc.register_soaking_abm({
		label = "eggcorn sprout",
		fieldname = "eggcorn",
		nodenames = {modname .. ":eggcorn_planted"},
		interval = 10,
		arealoaded = 1,
		soakrate = nc.tree_growth_rate,
		soakcheck = function(data, pos)
			if data.total >= sproutcost then
				nc.node_sound(pos, "dig")
				nc.set_loud(pos, {name = modname .. ":root"})
				local apos = {x = pos.x, y = pos.y + 1, z = pos.z}
				nc.set_loud(apos,
					{name = modname .. ":tree_bud", param2 = 1})
				nc.witness(apos, "tree growth")
				return nc.soaking_abm_push(apos,
					"treegrow", data.total - sproutcost)
			end
			return growparticles(pos, data.rate, 0.2)
		end
	})

local function leafbud(pos, dx, dy, dz, param2, surplus, rate)
	local npos = {x = pos.x + dx, y = pos.y + dy, z = pos.z + dz}
	if not nc.buildable_to(npos) then
		local node = core.get_node(npos)
		if core.get_item_group(node.name, "canopy") == 0 or param2 < node.param2
		then return end
	end
	if param2 <= 1 then
		if 240 < math_random(0, 255) then return end
		return nc.set_loud(npos, nc.calc_leaves(npos))
	end
	nc.set_loud(npos, {name = modname .. ":leaves_bud", param2 = param2})
	core.get_meta(npos):set_float("growrate", rate)
	return nc.soaking_abm_push(npos, "leafgrow", surplus)
end

local trunkcost = 1000
nc.register_soaking_abm({
		label = "tree trunk grow",
		fieldname = "treegrow",
		nodenames = {modname .. ":tree_bud"},
		interval = 10,
		arealoaded = 1,
		soakrate = function(pos, ...)
			local v = nc.tree_trunk_growth_rate(pos, ...)
			if v == false then
				core.set_node(pos, {name = modname .. ":tree"})
			end
			return v
		end,
		soakcheck = function(data, pos, node)
			if data.total < trunkcost then
				return growparticles(pos, data.rate, 0.45)
			end

			local tp = nc.tree_params[node.param2]
			if not tp then return core.remove_node(pos) end

			core.set_node(pos, {name = modname .. ":tree"})

			local apos = {x = pos.x, y = pos.y + 1, z = pos.z}
			if not nc.buildable_to(apos)
			and core.get_item_group(core.get_node(apos).name, "canopy") == 0
			then return end

			local param2 = node.param2 + 1
			tp = nc.tree_params[param2]
			if not tp then return core.remove_node(pos) end
			while tp.prob and (tp.prob < math_random(0, 255)) do
				param2 = param2 + 1
				tp = nc.tree_params[param2]
				if not tp then return core.remove_node(pos) end
			end

			local surplus = data.total - trunkcost
			if tp.leaves then
				leafbud(apos, 1, 0, 0, tp.leaves + 1, surplus, data.rate)
				leafbud(apos, -1, 0, 0, tp.leaves + 1, surplus, data.rate)
				leafbud(apos, 0, 0, 1, tp.leaves, surplus, data.rate)
				leafbud(apos, 0, 0, -1, tp.leaves, surplus, data.rate)
			end

			if tp.notrunk then
				leafbud(apos, 0, 0, 0, tp.leaves, surplus, data.rate)
			else
				nc.set_loud(apos, {
						name = modname .. ":tree_bud",
						param2 = param2
					})
				nc.witness(apos, "tree growth")
				nc.soaking_abm_push(apos,
					"treegrow", surplus)
			end
		end
	})

local leafcost = trunkcost
nc.register_soaking_abm({
		label = "tree leaves grow",
		nodenames = {modname .. ":leaves_bud"},
		fieldname = "leafgrow",
		interval = 10,
		arealoaded = 1,
		soakrate = function(pos)
			local rate = core.get_meta(pos):get_float("growrate") or 0
			return rate and rate ~= 0 and rate or 10
		end,
		soakcheck = function(data, pos, node)
			if data.total < leafcost then return end

			nc.set_loud(pos, nc.calc_leaves(pos))

			local surplus = data.total - leafcost
			if node.param2 <= 1 then
				return
			elseif node.param2 == 2 then
				leafbud(pos, 1, 0, 0, 1, surplus, data.rate)
				leafbud(pos, -1, 0, 0, 1, surplus, data.rate)
			elseif node.param2 == 3 then
				leafbud(pos, 0, 0, 1, 1, surplus, data.rate)
				leafbud(pos, 0, 0, -1, 1, surplus, data.rate)
			else
				leafbud(pos, 1, 0, 0, 3, surplus, data.rate)
				leafbud(pos, -1, 0, 0, 3, surplus, data.rate)
				leafbud(pos, 0, 0, 1, 2, surplus, data.rate)
				leafbud(pos, 0, 0, -1, 2, surplus, data.rate)
				if node.param2 >= 6 then
					leafbud(pos, 0, 1, 0, node.param2 - 4, surplus, data.rate)
				end
			end
		end
	})

local growtreedata = {
	[modname .. ":eggcorn_planted"] = {
		r = nc.tree_growth_rate,
		f = "eggcorn"
	},
	[modname .. ":tree_bud"] = {
		r = nc.tree_trunk_growth_rate,
		f = "treegrow"
	},
	[modname .. ":leaves_bud"] = {
		r = function() return 1 end,
		f = "leafgrow"
	}
}
core.register_chatcommand("growtrees", {
		description = "Accelerate growth of nearby trees",
		privs = {server = true},
		params = "[radius]",
		func = function(pname, param)
			local player = core.get_player_by_name(pname)
			if not player then return end
			local pos = player:get_pos()
			local spec = {}
			for k in pairs(growtreedata) do spec[#spec + 1] = k end
			local grew = {}
			param = tonumber(param) or 5
			if param < 0 then param = 0 end
			if param > 79 then param = 79 end
			for _, p in pairs(nc.find_nodes_around(pos, spec, param)) do
				local nn = core.get_node(p).name
				local data = growtreedata[nn]
				local r = data.r(p)
				if r and r > 0 then
					nc.soaking_abm_push(p, data.f, 100000)
					grew[#grew + 1] = nn .. " at " .. core.pos_to_string(p)
				end
			end
			return true, table_concat(grew, "\n")
		end
	})

nc.register_craft({
		label = "tickle eggcorn",
		action = "pummel",
		toolgroups = {cuddly = 1},
		normal = {y = 1},
		nodes = {
			{match = {name = modname .. ":eggcorn_planted", stacked = false}}
		},
		after = function(pos)
			nc.soaking_abm_tickle(pos, "eggcorn")
			nc.soaking_particles(pos, 25, 0.5, .45, modname .. ":leaves_bud")
		end
	})

nc.register_craft({
		label = "tickle tree trunk",
		action = "pummel",
		toolgroups = {cuddly = 1},
		normal = {y = 1},
		nodes = {
			{match = {name = modname .. ":tree_bud", stacked = false}}
		},
		after = function(pos)
			nc.soaking_abm_tickle(pos, "treegrow")
			nc.soaking_particles(pos, 25, 0.5, .45, modname .. ":leaves_bud")
		end
	})

nc.register_craft({
		label = "tickle tree leaves",
		action = "pummel",
		toolgroups = {cuddly = 1},
		nodes = {
			{match = {name = modname .. ":leaves_bud", stacked = false}}
		},
		after = function(pos)
			nc.soaking_abm_tickle(pos, "leafgrow")
		end
	})
