
local random=math.random
local function pick(list) return list[math.random(1, #list)] end
local max=math.max

local materials=
{
	["default:dirt_with_grass"]=
	{
		-- these ruins are either on open terrain or in friendly forests so less buildings and common mats
		walls={"default:silver_sandstone_block", "default:silver_sandstonebrick", "default:brick" },
		glass={"mf_decor:glass"},
		inside={ "mf_decor:ors_cobble", "air", "air", "air" },
	},
	["default:dirt_with_coniferous_litter"]=
	{
		-- these ruins are often in pine forests. More buildings, more interesting mats
		walls={"default:stone_block", "default:stonebrick"},
		glass={"mf_decor:glass"},
		inside={ "mf_decor:slate_cobble", "default:cobble", "air", "air", "air", "air", "air", "air"},
	},
	["default:dirt_with_dry_grass"]=
	{
		-- savanna, semi-open terrain
		walls={"default:stonebrick", "default:stone_block", },
		glass={"mf_decor:glass"},
		inside={ "mf_decor:ors_cobble", "default:cobble", "air", "air", "air", "air", "air", "air", "air" },
	},
	["default:dry_dirt"]=
	{
		-- semi-desert, open terrain
		walls={"default:stonebrick", "default:stone_block", nil, nil},
		glass={"mf_decor:glass"},
		inside={ "mf_decor:ors_cobble", "default:cobble", "air", "air", "air", "air", "air", "air", "air" },
	},
	["default:dirt_with_snow"]=
	{
		-- either snowy pine forests or taiga; hostile environments
		walls={ "default:stone_block", "mf_decor:stone_brick",},
		glass={"mf_decor:glass"},
		inside={ "mf_decor:slate_cobble", "mf_decor:slate_cobble", "air", "air", "air", "air", "air", "air", "air", "air"},
	},
	["default:dirt_with_rainforest_litter"]=
	{
		-- often dense rainforests although ruins can spawn at forest borders too.
		walls={"default:stonebrick", "default:cobble"},
		glass={"default:glass", "mf_decor:glass"},
		inside={"mf_decor:ors_cobble", "mf_decor:ors_cobble", "air", "air", "air" },
	},
	["default:permafrost"]=
	{
		-- open terrain. Decrease building density
		walls={ "default:basalt_brick", "default:stonebrick", nil, nil},
		glass={"mf_decor:glass"},
		inside={ "mf_decor:slate_cobble", "mf_decor:slate_cobble", "air", "air", "air", "air", "air", "air", "air", "air"},
	},
	["default:desert_sand"]=
	{
		-- open terrain. Decrease building density
		walls={"default:desert_stonebrick", "default:desert_stone_block", nil, nil},
		glass={"mf_decor:glass", },
		inside={ "default:desert_cobble", "default:desert_sand", "air", "air", "air", "air", "air"},
	},
	["abyss:rack"]=
	{
		walls={"abyss:abyssite_brick"},
		glass={"default:glass"},
		inside={ "default:glass", "air", "air"},
		loot="default:bookshelf_abyss",
	},
	["default:basalt"]=
	{
		-- underground
		walls={ "default:obsidianbrick", "default:obsidian_block"},
		glass={"default:glass", "mf_decor:glass", "default:obsidian_glass"},
		inside={ "default:basalt_cobble", "air", "air"},
		loot="default:bookshelf_obsidian",
	},
}

local models={{8,4,8}, {6,8,6}, { 6, 4, 10}, {5,12,5}, {8,4,12} }

local newnode={}
local setnode=function(pos, name)
	newnode.name=name
	minetest.swap_node(pos, newnode)
end

local function make_ruin(pos)
	local seed= minetest.get_node(pos).name
	if seed=="ignore" then return end
	-- minetest.chat_send_all("Generating ruins on "..seed)
	local mats=materials[seed]
	if not mats then return end -- trying to build something on top of something

	local seedpos={x=pos.x, y=pos.y+1, z=pos.z}
	local walls=pick(mats.walls)
	if not walls then return end -- lets vary building density
	if not minetest.registered_nodes[walls] then minetest.chat_send_all("INVALID WALL: "..walls) end
	local glass=pick(mats.glass)
	local width,height,length=unpack(pick(models))
	if random(1,2)==1 then local width, length= length, width end -- create rotated variants
	-- center the building on the seed so we don't have "hanging ruins"
	pos.x=pos.x-math.floor(width/2)
	pos.z=pos.z-math.floor(length/2)

	local rn=minetest.registered_nodes
	local p={}
	for yi = 0,height do
		-- Walls
		local prob=2*(height-yi)/height
		for xi = 0, width do
			local xi_edge=(xi==0 or xi==width)
			for zi = 0, length do
				p.x=pos.x+xi
				p.y=pos.y+yi
				p.z=pos.z+zi
				local here=minetest.get_node(p)
				local under=minetest.get_node{x=p.x, y=p.y-1, z=p.z}
				if xi_edge or zi==0 or zi==length then
					if yi~=0 then
						if under.name==walls and here.name=="air" and math.random()<prob then	
							setnode(p, walls)
						end
					elseif here.name==seed then
						setnode(p, walls)
					end
				end
			end
		end
	end
	-- Windows
	for yi=2, height, 4 do
		p.y=pos.y+yi
		for xi=2, width-2 do
			p.x=pos.x+xi
			for zi=0, length, length do
				p.z=pos.z+zi
				if minetest.get_node(p).name==walls and random(1, 5)==1 then
					setnode(p, glass)
				end
			end
		end
		for xi=0, width, width do
			p.x=pos.x+xi
			for zi=2, length-2 do
				p.z=pos.z+zi
				if minetest.get_node(p).name==walls and random(1, 5)==1 then
					setnode(p, glass)
				end
			end
		end
	end

	-- Salvage
	for yi=1, 4 do
		p.y=pos.y+yi
		for xi=1, width-1 do
			p.x=pos.x+xi
			for zi=1, length-1 do
				p.z=pos.z+zi
				local under=minetest.registered_nodes[minetest.get_node({x=p.x, y=p.y-1, z=p.z}).name]
				local solid= under and under.walkable==true
				if solid and minetest.get_node(p).name=="air" then
					setnode(p, pick(mats.inside))
				end
			end
		end
	end

	-- Note: no door anymore. Most buildings are incomplete anymway. For those which are,
	-- well, players can be creative and "fixing" that is the main purpose of the
	-- obsidian shards.
	
	-- finally place a loot crate, maybe
	if minetest.get_node(seedpos).name=="air" and seedpos.y>-5000 then
		minetest.set_node(seedpos, {name=mats.loot or pick(dungeon_crates.loot)})
	end
end

minetest.register_decoration({
	name = "ruins:ruins",
	deco_type = "simple",
	place_on = {"default:desert_sand", "default:dirt_with_grass", "default:dirt_with_dry_grass", "default:dirt_with_snow", "default:dirt_with_rainforest_litter", "default:dirt_with_coniferous_litter", "default:permafrost", "default:dry_dirt", "abyss:rack", "default:basalt"},
	sidelen=20,
	fill_ratio=0.003,
	y_max = 31000,
	y_min = -31000,
	decoration="air",
	flags="all_floors",
})

local decoId=minetest.get_decoration_id("ruins:ruins")
minetest.set_gen_notify("decoration", {decoId})
minetest.register_on_generated(function(minp, maxp, seed)
	math.randomseed(seed)
	if random(1, 6)~=1 then return end
	local surface=minetest.get_mapgen_object("gennotify")
	local pos=surface["decoration#"..decoId] or {}
	if #pos ~=0 then
		for n=1, #pos do
			make_ruin(pos[n])
		end
	end
end)

