--
-- Grow grass
--

core.register_abm({
	label = "Grass spread on dry dirt",
	nodenames = {"default:dry_dirt"},
	neighbors = {
		"group:spreading_dry_dirt_type",
		"group:dry_grass",
	},
	interval = 12,
	chance = 100,
	catch_up = false,
	action = function(pos, node)
		-- Check for darkness: night, shadow or under a light-blocking node
		-- Returns if ignore above
		local above = {x = pos.x, y = pos.y + 1, z = pos.z}
		if (core.get_node_light(above) or 0) < 10 then
			return
		end
		local name = core.get_node(above).name
		if core.get_item_group(name, "liquid") > 0 then
			return
		end

		-- Look for spreading dry_dirt-type neighbours
		local p2 = core.find_node_near(pos, 1, "group:spreading_dry_dirt_type")
		if p2 then
			local n2 = core.get_node(p2)
			local d2 = core.registered_nodes[n2.name]
			core.swap_node(pos, {name = d2 and d2._footsteps and d2._footsteps_name or n2.name})
			return
		end
		-- Else, any seeding nodes on top?
		if core.get_item_group(name, "dry_grass") ~= 0 then
			core.swap_node(pos, {name = "default:dry_dirt_with_dry_grass"})
		end
	end
})

core.register_abm({
	label = "Grass spread",
	nodenames = {"default:dirt"},
	neighbors = {
		"group:spreading_dirt_type",
		"group:grass",
		"group:dry_grass",
		"default:snow",
		"default:snowblock",
	},
	interval = 7,
	chance = 50,
	catch_up = false,
	action = function(pos, node)
		-- Check for darkness: night, shadow or under a light-blocking node
		-- Returns if ignore above
		local above = {x = pos.x, y = pos.y + 1, z = pos.z}
		if (core.get_node_light(above) or 0) < 10 then
			return
		end
		local name = core.get_node(above).name
		if core.get_item_group(name, "liquid") > 0 then
			return
		end

		-- Look for spreading dirt-type neighbours
		local p2 = core.find_node_near(pos, 1, "group:spreading_dirt_type")
		if p2 then
			local n2 = core.get_node(p2)
			local d2 = core.registered_nodes[n2.name]
			core.swap_node(pos, {name = d2 and d2._footsteps and d2._footsteps_name or n2.name})
			return
		end

		-- Else, any seeding nodes on top?
		-- Snow check is cheapest, so comes first
		if name == "default:snow"  or name == "default:snowblock" then
			core.swap_node(pos, {name = "default:dirt_with_snow"})
		elseif core.get_item_group(name, "dry_grass") ~= 0 then
			core.swap_node(pos, {name = "default:dirt_with_dry_grass"})
		elseif core.get_item_group(name, "grass") ~= 0 then
			core.swap_node(pos, {name = "default:dirt_with_grass"})
		end
	end
})

core.register_abm({
	label = "Grass covered",
	nodenames = {
		"group:spreading_dirt_type",
		"group:spreading_dry_dirt_type",
	},
	interval = 8,
	chance = 100,
	catch_up = false,
	action = function(pos, node)
		local above = {x = pos.x, y = pos.y + 1, z = pos.z}
		local name = core.get_node(above).name
		local nodedef = core.registered_nodes[name]
		if name ~= "ignore" and nodedef and not ((nodedef.sunlight_propagates or
				nodedef.paramtype == "light") and
			nodedef.liquidtype == "none") then
			if core.get_item_group(node.name, "spreading_dry_dirt_type") > 0 then
				core.swap_node(pos, {name = "default:dry_dirt"})
			else
				core.swap_node(pos, {name = "default:dirt"})
			end
		end
	end
})

--
-- Lavacooling
--

function default.cool_lava(pos, node)
	if node.name == "default:lava_source" then
		core.set_node(pos, {name = "default:obsidian"})
	else -- Lava flowing
		core.set_node(pos, {name = "default:molten_rock"})
	end
	core.sound_play("default_cool_lava",
		{pos = pos, max_hear_distance = 16, gain = 0.25})
end

core.register_abm({
	label = "Lava cooling",
	nodenames = {"default:lava_source", "default:lava_flowing"},
	neighbors = {"group:cools_lava", "group:water"},
	interval = 1,
	chance = 2,
	catch_up = false,
	action = function(pos, node)
		default.cool_lava(pos, node)
	end,
})

core.register_abm({
	label = "Molten rock cooling",
	nodenames = {"default:molten_rock"},
	neighbors = {"group:cools_lava", "group:water"},
	interval = 11,
	chance = 50,
	catch_up = false,
	action = function(pos, node)
		core.set_node(pos, {name = "default:hardened_rock"})
	end,
})


--
-- Optimized helper to put all items in an inventory into a drops list
--

function default.get_inventory_drops(pos, inventory, drops)
	local inv = core.get_meta(pos):get_inventory()
	local n = #drops
	for i = 1, inv:get_size(inventory) do
		local stack = inv:get_stack(inventory, i)
		if stack:get_count() > 0 then
			table.insert(drops, stack:to_table())
			n = n + 1
		end
	end
end


--
-- Papyrus and cactus growing
--

function default.grow_cactus(pos)
	pos.y = pos.y - 1
	local node = core.get_node(pos)
	if core.get_item_group(node.name, "sand") == 0 then
		return
	end
	pos.y = pos.y + 1
	local height = 0
	node = core.get_node(pos)
	while node.name == "default:cactus" and height < 5 do
		height = height + 1
		pos.y = pos.y + 1
		node = core.get_node(pos)
	end
	if node.name ~= "air" then return end
	-- Increased chance for figs to grow the taller the cactus is.
	if height < math.random(2, 5) then
		core.set_node(pos, {name="default:cactus"})
	else
		core.set_node(pos, {name="default:cactus_fig"})
	end
	return true
end

function default.grow_papyrus(pos)
	pos.y = pos.y - 1
	local node = core.get_node(pos)
	if core.get_item_group(node.name, "soil") == 0 then
		return
	end
	if not core.find_node_near(pos, 3, {"group:water"}) then
		return
	end
	pos.y = pos.y + 1
	node = core.get_node(pos)
	local height = 0
	while node.name == "default:papyrus" and height < 5 do
		height = height + 1
		pos.y = pos.y + 1
		node = core.get_node(pos)
	end
	if height < math.random(0, 5) and node.name == "air" then
		core.set_node(pos, {name="default:papyrus"})
		return true
	end
end

core.register_abm({
	nodenames = {"default:coral_brown", "default:coral_orange", "default:coral_purple"},
	neighbors = {"air"},
	interval = 17,
	chance = 5,
	catch_up = false,
	action = function(pos, node)
		if not core.find_node_near(pos, 1, "group:water") then
			core.set_node(pos, {name = "default:coral_skeleton"})
		end
	end,
})

-- Wrapping the functions in abm action is necessary to make overriding them possible.
core.register_abm({
	label = "Grow cactus",
	nodenames = {"default:cactus"},
	neighbors = {"group:sand"},
	interval = 70,
	chance = 30,
	action = function(pos)
		default.grow_cactus(pos)
	end
})

core.register_abm({
	label = "Grow cactus from fig",
	nodenames = {"default:cactus_fig"},
	neighbors = {"group:sand"},
	interval = 7,
	chance = 3,
	action = function(pos)
		local node_under = core.get_node_or_nil(vector.offset(pos, 0, -1, 0))
		if core.get_item_group(node_under.name, "sand") ~= 0 then
			core.set_node(pos, {name = "default:cactus"})
		end
	end,
})

core.register_abm({
	label = "Grow papyrus",
	nodenames = {"default:papyrus"},
	neighbors = {"default:dirt", "default:dirt_with_grass", "default:papyrus_roots"},
	interval = 40,
	chance = 30,
	action = function(pos)
		default.grow_papyrus(pos)
	end,
})

function default.place_kelp(itemstack, placer, pos)
	if core.get_node(pos).name ~= "default:sand" then
		return itemstack
	end

	local height = math.random(4, 6)
	local pos_top = vector.offset(pos, 0, height, 0)
	local node_top = core.get_node(pos_top)
	local def_top = core.registered_nodes[node_top.name]

	if placer then
		local player_name = placer:get_player_name()
		if core.is_protected(pos, player_name) and core.is_protected(pos_top, player_name) then
			core.chat_send_player(player_name, "Node is protected")
			core.record_protection_violation(pos, player_name)
			return itemstack
		end
	end

	if def_top and def_top.liquidtype == "source" and
			core.get_item_group(node_top.name, "water") > 0 then
		core.set_node(pos, {name = "default:sand_with_kelp", param2 = height * 16})
		if itemstack and not core.is_creative_enabled(placer) then
			itemstack:take_item()
		end
	end

	return itemstack
end

core.register_abm({
	label = "Grow Kelp",
	nodenames = {"default:sand_with_small_kelp"},
	interval = 11,
	chance = 50,
	action = function(pos, node)
		core.set_node(pos, {name = "default:sand"})
		default.place_kelp(nil, nil, pos)
	end,
})
