asuna.decor = {}

--[[
	Flowers
]]

-- Group flowers by color
local flower_colors = {
	black = {
		"flowers:tulip_black",
	},
	white = {
		"beautiflowers:carla",
		"beautiflowers:cloe",
		"beautiflowers:genesis",
		"beautiflowers:gloria",
		"beautiflowers:hadassa",
		"beautiflowers:ingrid",
		"beautiflowers:irene",
		"beautiflowers:iris",
		"beautiflowers:ivette",
		"beautiflowers:michelle",
		"beautiflowers:suri",
		"flowers:dandelion_white",
		"herbs:achillea_white",
		"herbs:leontopodium_white",
		"herbs:leucanthemum_white",
		"herbs:trifolium_white",
	},
	blue = {
		"beautiflowers:beatriz",
		"beautiflowers:berta",
		"flowers:geranium",
		"herbs:centaurea",
		"herbs:campanula_blue",
		"herbs:digitalis_blue",
		"herbs:iris",
		"bakedclay:delphinium",
	},
	cyan = {
		"beautiflowers:thais",
		"beautiflowers:valentina",
		"beautiflowers:valeria",
		"beautiflowers:vera",
		"beautiflowers:victoria",
		"beautiflowers:virginia",
		"beautiflowers:xenia",
		"beautiflowers:zaida",
	},
	orange = {
		"beautiflowers:dafne",
		"beautiflowers:dana",
		"beautiflowers:delia",
		"beautiflowers:elena",
		"beautiflowers:erica",
		"beautiflowers:estela",
		"beautiflowers:eva",
		"beautiflowers:fabiola",
		"beautiflowers:fiona",
		"beautiflowers:gala",
		"flowers:tulip",
	},
	yellow = {
		"beautiflowers:ada",
		"beautiflowers:agnes",
		"beautiflowers:alicia",
		"beautiflowers:alma",
		"beautiflowers:amaia",
		"beautiflowers:anastasia",
		"beautiflowers:any",
		"flowers:dandelion_yellow",
		"farming:sunflower_8",
		"herbs:digitalis_yellow",
		"herbs:plantago",
	},
	purple = {
		"beautiflowers:arleth",
		"beautiflowers:astrid",
		"beautiflowers:belen",
		"beautiflowers:blanca",
		"beautiflowers:casandra",
		"beautiflowers:clara",
		"beautiflowers:claudia",
		"beautiflowers:minerva",
		"beautiflowers:miriam",
		"beautiflowers:nazareth",
		"beautiflowers:noemi",
		"beautiflowers:olga",
		"beautiflowers:paula",
		"beautiflowers:regina",
		"beautiflowers:rocio",
		"beautiflowers:sabrina",
		"beautiflowers:vanesa",
		"flowers:viola",
		"beautiflowers:xena",
	},
	red = {
		"beautiflowers:arcoiris",
		"beautiflowers:jennifer",
		"beautiflowers:lara",
		"beautiflowers:laura",
		"beautiflowers:lidia",
		"beautiflowers:lucia",
		"beautiflowers:mara",
		"beautiflowers:martina",
		"beautiflowers:melania",
		"beautiflowers:mireia",
		"beautiflowers:nadia",
		"beautiflowers:nerea",
		"beautiflowers:noelia",
		"flowers:rose",
		"herbs:dosera",
		"herbs:papaver_red",
	},
	pink = {
		"beautiflowers:caroline",
		"beautiflowers:cristina",
		"beautiflowers:diana",
		"beautiflowers:gisela",
		"beautiflowers:olimpia",
		"beautiflowers:oriana",
		"beautiflowers:pia",
		"beautiflowers:raquel",
		"beautiflowers:ruth",
		"beautiflowers:sandra",
		"beautiflowers:sara",
		"beautiflowers:silvia",
		"beautiflowers:sofia",
		"beautiflowers:sonia",
		"beautiflowers:talia",
		"herbs:antirrhinum",
		"herbs:trifolium_red",
		"bakedclay:thistle",
		"bakedclay:lazarus",
		"ethereal:lilac",
	},
	green = {
		"flowers:chrysanthemum_green",
		"beautiflowers:pasto_1",
		"beautiflowers:pasto_2",
		"beautiflowers:pasto_3",
		"beautiflowers:pasto_4",
		"beautiflowers:pasto_5",
		"beautiflowers:pasto_6",
		"beautiflowers:pasto_7",
		"beautiflowers:pasto_8",
		"beautiflowers:pasto_9",
		"beautiflowers:pasto_10",
		"bakedclay:mannagrass",
	},
	red_mushroom = {
		"flowers:mushroom_red",
	},
	brown_mushroom = {
		"flowers:mushroom_brown",
		"herbs:mushroom_boletus",
		"herbs:mushroom_cantharellus",
		"herbs:mushroom_macrolepiota",
	},
	odd_mushroom = {
		"herbs:mushroom_amanita_green",
		"herbs:mushroom_gyromitra",
		"herbs:mushroom_galerina",
	}
}

-- Register flower decorations per biome
local function cf(biome)
	local decor = {
		flowers = {},
		mushrooms = {},
	}

	-- Generate list of flowers
	for _,flower in ipairs(biome.flowers or {}) do
		for _,flower in ipairs(flower_colors[flower]) do
			table.insert(decor.flowers,flower)
		end
	end

	-- Generate list of mushrooms
	for _,mushroom in ipairs(biome.mushrooms or {}) do
		for _,mushroom in ipairs(flower_colors[mushroom .. "_mushroom"]) do
			table.insert(decor.mushrooms,mushroom)
		end
	end

	return decor
end

-- Do actual decoration registration after other mods are finished
minetest.register_on_mods_loaded(function()
	-- Register shore grass
  local sandy_biomes = {}
	local desert_biomes = {
		desert = true,
		sandstone_desert = true,
		desert_shore = true,
		sandstone_desert_shore = true,
		desert_below = true,
		sandstone_desert_below = true,
	}
	for biome,def in pairs(asuna.biomes) do
		if def.shore == "default:sand" and
			(def.ocean == "temperate" or def.ocean == "tropical" or def.ocean == "cold") and
			not desert_biomes[biome]
		then
			table.insert(sandy_biomes,biome)
		end
	end

	minetest.register_decoration(asuna.biome_groups.shore.inject_decoration({
		deco_type = "simple",
		place_on = "default:sand",
		decoration = {"default:marram_grass_1","default:marram_grass_2","default:marram_grass_3"},
		y_min = 2,
		y_max = 2,
		sidelen = 16,
		noise_params = {
			offset = 0.004,
			scale = 0.0195,
			spread = {x = 11, y = 11, z = 11},
			seed = 69420,
			octaves = 1,
		},
		biomes = sandy_biomes,
	}))

	minetest.register_decoration(asuna.biome_groups.shore.inject_decoration({
		deco_type = "simple",
		place_on = "default:sand",
		decoration = {"default:marram_grass_1","default:marram_grass_2","default:marram_grass_3"},
		y_min = 3,
		y_max = 4,
		sidelen = 16,
		noise_params = {
			offset = 0.075,
			scale = 0.175,
			spread = {x = 10, y = 10, z = 10},
			seed = 42069,
			octaves = 1,
		},
		biomes = sandy_biomes,
	}))

	-- Register flowers and mushrooms
	for name,biome in pairs(asuna.biomes) do
		-- Get node groups for biome
		local decor = cf(biome)

		-- Register flowers
		if #biome.flowers > 0 then
			minetest.register_decoration({
				name = "asuna_core:flowers_" .. name,
				deco_type = "simple",
				sidelen = 80,
				place_on = biome.nodes.extra and {biome.nodes[1],unpack(biome.nodes.extra)} or biome.nodes[1],
				noise_params = {
					offset = 0.006786,
					scale = 0.004175,
					spread = {x = 8, y = 8, z = 8},
					seed = 1999,
					octaves = 2,
					persist = 0.44,
					lacunarity = 0.75,
				},
				biomes = {name},
				y_max = 31000,
				y_min = 1,
				decoration = decor.flowers,
			})
		end

		-- Register mushrooms
		if #biome.mushrooms > 0 then
			minetest.register_decoration({
				name = "asuna_core:mushrooms_" .. name,
				deco_type = "simple",
				sidelen = 8,
				place_on = biome.nodes.extra and {biome.nodes[1],unpack(biome.nodes.extra)} or biome.nodes[1],
				noise_params = {
					offset = -0.0069,
					scale = 0.027525,
					spread = {x = 8, y = 8, z = 8},
					seed = 60659,
					octaves = 2,
					persist = 0.7625,
					lacunarity = 0.6,
				},
				biomes = {name},
				y_max = 31000,
				y_min = 1,
				decoration = decor.mushrooms,
			})
		end
	end

	-- Special sunflower decor for the Plains biome
	minetest.register_decoration({
		name = "asuna_core:plains_special_sunflower",
		deco_type = "simple",
		place_on = {"default:dry_dirt_with_dry_grass"},
		sidelen = 80,
		fill_ratio = 0.075,
		biomes = {"plains"},
		y_max = 31000,
		y_min = 1,
		decoration = "farming:sunflower_8",
	})

	-- Special dense flower decor for the Prairie biome
	minetest.register_decoration({
		name = "asuna_core:prairie_special_flowers",
		deco_type = "simple",
		place_on = "prairie:prairie_dirt_with_grass",
		sidelen = 80,
		fill_ratio = 0.265,
		biomes = {"prairie"},
		y_max = 31000,
		y_min = 1,
		decoration = cf({ flowers = {"blue","cyan","white","orange","yellow"} }).flowers,
	})

	-- Special mushroom decor for the Mushroom biome
	minetest.register_decoration({
		name = "asuna_core:mushroom_special_mushrooms",
		deco_type = "simple",
		place_on = "ethereal:mushroom_dirt",
		sidelen = 80,
		fill_ratio = 0.075,
		biomes = {"mushroom"},
		y_max = 31000,
		y_min = 1,
		decoration = cf({ mushrooms = {"odd","brown"} }).mushrooms,
	})

	-- Bushes for certain grassy biomes
	minetest.register_decoration({
		name = "bushes:default_replacement",
		deco_type = "schematic",
		place_on = "default:dirt_with_grass",
		sidelen = 80,
		fill_ratio = 0.00025,
		y_min = 2,
		y_max = 31000,
		biomes = {
				"grassland",
				"deciduous_forest",
				"grassytwo",
				"jumble",
				"marsh",
		},
		schematic = minetest.get_modpath("default") .. "/schematics/bush.mts",
		flags = "place_center_x,place_center_z",
	})

	--[[
		Butterflies
	]]

	if minetest.get_modpath("butterflies") then

		minetest.register_decoration({
			name = "butterflies:butterfly",
			deco_type = "simple",
			place_on = "group:soil",
			place_offset_y = 1,
			sidelen = 80,
			fill_ratio = 0.005,
			biomes = {
				"grassland",
				"deciduous_forest",
				"grassytwo",
				"prairie",
				"dorwinion",
				"jumble",
				"bamboo",
				"naturalbiomes:heath",
				"naturalbiomes:alpine",
				"everness:bamboo_forest",
			},
			y_max = 31000,
			y_min = 1,
			decoration = {
				"butterflies:butterfly_white",
				"butterflies:butterfly_red",
				"butterflies:butterfly_violet",
			},
			spawn_by = "group:flower",
			num_spawn_by = 1,
		})
	end

	--[[
		Fireflies
	]]

	if minetest.get_modpath("fireflies") then
		minetest.register_decoration({
			name = "fireflies:firefly_low",
			deco_type = "simple",
			place_on = "group:soil",
			place_offset_y = 2,
			sidelen = 16,
			noise_params = {
				offset = -0.005,
				scale = 0.015,
				spread = {x = 60, y = 20, z = 60},
				seed = 2112,
				octaves = 1,
				persistence = 0.75,
				flags = "eased"
			},
			biomes = {
				"deciduous_forest",
				"grassland",
				"grassytwo",
				"prairie",
				"dorwinion",
				"jumble",
				"swamp",
				"marsh",
				"naturalbiomes:alderswamp",
				"naturalbiomes:alpine",
				"naturalbiomes:bushland",
				"everness:bamboo_forest",
			},
			y_max = 31000,
			y_min = -1,
			place_offset_y = 2,
			decoration = "fireflies:hidden_firefly",
		})
	
		minetest.register_decoration({
			name = "fireflies:firefly_low_bamboo_cave",
			deco_type = "simple",
			place_on = "everness:moss_block",
			place_offset_y = 2,
			sidelen = 16,
			fill_ratio = 0.0065,
			biomes = asuna.features.cave.bamboo,
			y_max = 0,
			y_min = -31000,
			place_offset_y = 1,
			decoration = "fireflies:hidden_firefly",
		})

		minetest.register_decoration({
			name = "fireflies:firefly_low_dorwinion_cave",
			deco_type = "simple",
			place_on = "dorwinion:dorwinion_grass",
			place_offset_y = 2,
			sidelen = 16,
			fill_ratio = 0.0065,
			biomes = asuna.features.cave.dorwinion,
			y_max = 0,
			y_min = -31000,
			place_offset_y = 1,
			decoration = "fireflies:hidden_firefly",
		})
	end

	--[[
		Large jungle trees
	]]

	local chunksize = tonumber(minetest.get_mapgen_setting("chunksize"))
	if chunksize >= 5 then
		minetest.register_decoration({
			name = "default:emergent_jungle_tree",
			deco_type = "schematic",
			place_on = {
				"default:dirt_with_rainforest_litter",
			},
			sidelen = 80,
			noise_params = {
				offset = 0.0,
				scale = 0.0025,
				spread = {x = 200, y = 200, z = 200},
				seed = 2685,
				octaves = 3,
				persist = 0.7
			},
			biomes = {"rainforest"},
			y_max = 30000,
			y_min = 1,
			schematic = minetest.get_modpath("default") .. "/schematics/emergent_jungle_tree.mts",
			flags = "place_center_x, place_center_z",
			rotation = "random",
			place_offset_y = -4,
		})
	end

	--[[
		Hanging vines
	]]

	local soil_nodes = {}
	for _,soil_node in ipairs({
		"default:dirt",
		"default:dirt_with_grass",
		"default:dirt_with_rainforest_litter",
		"livingjungle:jungleground",
		"livingjungle:leafyjungleground",
		"naturalbiomes:alderswamp_litter",
		"naturalbiomes:alderswamp_dirt",
		"default:tree",
		"default:jungletree",
		"default:jungleleaves",
	}) do
		soil_nodes[minetest.get_content_id(soil_node)] = true
	end

	local cids = {
		air = minetest.get_content_id("air"),
		vine = minetest.get_content_id("ethereal:vine"),
		water = minetest.get_content_id("default:water_source"), -- used for waterfalls below; unrelated to vines
		water_flowing = minetest.get_content_id("default:water_flowing"), -- used for cave liquids below; unrelated to vines
		lava = minetest.get_content_id("default:lava_source"), -- used for cave liquids below; unrelated to vines
		lava_flowing = minetest.get_content_id("default:lava_flowing"), -- used for cave liquids below; unrelated to vines
		stone = minetest.get_content_id("default:stone"),
		jungleleaves = minetest.get_content_id("default:jungleleaves"),
	}

	local asuna_hanging_vines_fn = function(mapgen)
		-- Get provided values
		local pos = mapgen.pos
		local va = mapgen.voxelarea
		local vdata = mapgen.data
		local vparam2 = mapgen.param2

		-- Get stride values and set position
		local ystride = va.ystride
		local zstride = va.zstride
		pos = va:index(pos.x,pos.y,pos.z)

		-- Scan for dirt or surface nodes for a short distance above the position
		for above = 1, 2 do
			above = pos + above * ystride
			if soil_nodes[vdata[above]] then
				pos = above
				break
			end
		end

		-- List of cardinal directions relative to the current position
		local cardinal = {
			pos - 1,
			pos + 1,
			pos - zstride,
			pos + zstride,
		}

		-- Iterate over cardinal positions and place vines at and below those positions
		for i = 1, 4 do
			local dir = cardinal[i]
			if vdata[dir] == cids.air then
				for below = 0, ((dir ^ 2 + (dir + pos) % 3) % 4 + 2) do
					below = dir - below * ystride
					if vdata[below] == cids.air then
						vdata[below] = cids.vine
						vparam2[below] = i + 1
					else
						break
					end
				end
			end
		end
	end

	abdecor.register_advanced_decoration("asuna_hanging_vines_jungletree",{
		target = {
			place_on = {
				"default:jungletree",
			},
			sidelen = 80,
			fill_ratio = 0.3,
			biomes = {
				"rainforest",
			},
			y_max = 31000,
			y_min = 1,
			flags = "all_ceilings",
		},
		fn = function(mapgen)
		-- Get provided values
			local pos = mapgen.pos
			local va = mapgen.voxelarea
			local vdata = mapgen.data
			local vparam2 = mapgen.param2

			-- Get stride values and set position
			local ystride = va.ystride
			local zstride = va.zstride
			pos = va:index(pos.x,pos.y,pos.z)

			-- Check surrounding nodes for jungle leaves
			for x = -1, 1 do
				for z = -zstride, zstride, zstride do
					local lpos = pos + x + z
					if vdata[lpos] == cids.jungleleaves and (pos * x + z) % 3 > 0 then
						mapgen.pos = va:position(lpos)
						asuna_hanging_vines_fn(mapgen)
					end
				end
			end
		end,
		flags = {
			param2 = true,
		}
	})

	abdecor.register_advanced_decoration("asuna_hanging_vines",{
		target = {
			place_on = {
				"group:stone",
				"default:dirt",
				"default:jungletree",
			},
			spawn_by = "air",
			num_spawn_by = 5,
			sidelen = 80,
			fill_ratio = 0.3,
			biomes = {
				"swamp",
				"naturalbiomes:alderswamp",
				"marsh",
				"rainforest",
				"jumble",
				"livingjungle:jungle",
			},
			y_max = 31000,
			y_min = 1,
			flags = "all_ceilings",
		},
	  fn = asuna_hanging_vines_fn,
		flags = {
			param2 = true,
		},
	})

	--[[
		Ocean waterfalls
	]]

	local wall_stones = {
		"default:stone_with_coal",
		"default:stone_with_iron",
		"default:stone_with_tin",
		"default:stone_with_copper",
		"default:stone_with_gold",
		"default:stone_with_diamond",
		"default:stone_with_mese",
	}

	for node,def in pairs(minetest.registered_nodes) do
		if def.groups and def.groups.stone and def.groups.stone > 0 then
			table.insert(wall_stones,node)
		end
	end

	local valid_wall_stones = {}
	for _,node in ipairs(wall_stones) do
		valid_wall_stones[minetest.get_content_id(node)] = true
	end

	abdecor.register_advanced_decoration("asuna_waterfalls",{
		target = {
			place_on = "default:water_source",
			spawn_by = wall_stones,
			num_spawn_by = 3,
			sidelen = 80,
			fill_ratio = 0.11,
			biomes = asuna.biome_groups.shore,
			y_max = 1,
			y_min = 1,
			flags = "liquid_surface",
		},
		fn = function(mapgen)
			-- Get provided values
			local pos = mapgen.pos
			local va = mapgen.voxelarea
			local vdata = mapgen.data

			-- Get stride values and adjust position
			local ystride = va.ystride
			local zstride = va.zstride
			pos = va:index(pos.x,pos.y + 1,pos.z)
			local too_low_pos = 0

			-- Get stone wall direction
			local cardinal = {
				-1,
				-zstride,
				1,
				zstride,
			}

			local found_stone = false
			local check_wall = nil
			for i = 1, 4 do
				local wallpos = pos + cardinal[i]
				local wallleft = cardinal[i % 4 + 1]
				local wallright = cardinal[(i + 2) % 4 + 1]
				check_wall = function(pos) -- is the entire row of wall nodes made of stone?
					return valid_wall_stones[vdata[pos]] and valid_wall_stones[vdata[pos + wallleft]] and valid_wall_stones[vdata[pos + wallright]] and true or false
				end
				if check_wall(wallpos) then
					pos = wallpos
					too_low_pos = pos
					found_stone = true
					break
				end
			end

			-- Do nothing if no stone wall found
			if not found_stone then
				return
			end

			-- Iterate above 'rows' of stone wall until we find something that isn't stone
			repeat
				pos = pos + ystride
			until not check_wall(pos)

			-- Set two below stone position to water if higher than one node
			local placepos = pos - 2 * ystride
			if placepos > too_low_pos then
				vdata[placepos] = cids.water
			end
		end,
		flags = {
			liquid = true,
		},
	})

	--[[
		Cave ceiling liquids
	]]

	local enclosing_nodes = valid_wall_stones

	abdecor.register_advanced_decoration("asuna_cave_liquids",{
		target = {
			place_on = {
				"group:stone",
			},
			sidelen = 80,
			spawn_by = {
				"group:stone",
			},
			num_spawn_by = 8,
			fill_ratio = 0.000015,
			y_max = -30,
			y_min = -31000,
			flags = "all_ceilings",
		},
		fn = function(mapgen)
			-- Get provided values
			local va = mapgen.voxelarea
			local vdata = mapgen.data
			local vparam2 = mapgen.param2
			local pos = mapgen.pos

			-- Get stride values and set position
			local ystride = va.ystride
			local zstride = va.zstride
			local pos = va:index(pos.x,pos.y,pos.z)

			-- Liquid must be enclosed to its sides and above
			for _,adjacent in ipairs({
				ystride,
				1,
				-1,
				zstride,
				-zstride,
			}) do
				if not enclosing_nodes[vdata[pos + adjacent]] then
					return -- liquid is not fully enclosed
				end
			end

			-- Liquid must have sufficient clearance below
			-- Scanning from bottom up should typically fail faster than top down
			for below = pos - ystride * 8, pos - ystride, ystride do
				if vdata[below] ~= minetest.CONTENT_AIR then
					return -- not enough space between ceiling and ground
				end
			end

			-- Fill the position and all air below with liquid based on climate + bias
			-- Dry/hot climates are more likely to be lava, vice-versa with water
			local liquid = (function()
				local heatmap = minetest.get_mapgen_object("heatmap") or {}
				local humiditymap = minetest.get_mapgen_object("humiditymap") or {}
				local pos2d = mapgen.index2d(mapgen.pos)
				local heat = heatmap[pos2d] or 50
				local humidity = humiditymap[pos2d] or 50
				local climate = 50 + (heat / 2 - 25) - (humidity / 2 - 25)
				local pos_random = (pos ^ 2 + pos) % 38 * (pos % 2 == 0 and 1 or -1) + climate -- not actually random but good enough
				return pos_random > 56 and {cids.lava,cids.lava_flowing} or {cids.water,cids.water_flowing} -- bias in favor of water
			end)()
			vdata[pos] = liquid[1]
			pos = pos - ystride
			while vdata[pos] == minetest.CONTENT_AIR do
				vdata[pos] = liquid[2]
				vparam2[pos] = 15
				pos = pos - ystride
			end
		end,
		flags = {
			liquid = true,
			param2 = true,
		},
	})
end)