-- LUALOCALS < ---------------------------------------------------------
local minetest, nodecore, pairs, ipairs
    = minetest, nodecore, pairs, ipairs
-- LUALOCALS > ---------------------------------------------------------
local get_node = minetest.get_node
local set_node = minetest.swap_node
local water_level = tonumber(minetest.get_mapgen_setting("water_level"))
local all_direction_permutations = {              -- table of all possible permutations of horizontal direction to avoid lots of redundant calculations.
	{{x=0,z=1},{x=0,z=-1},{x=1,z=0},{x=-1,z=0}},
	{{x=0,z=1},{x=0,z=-1},{x=-1,z=0},{x=1,z=0}},
	{{x=0,z=1},{x=1,z=0},{x=0,z=-1},{x=-1,z=0}},
	{{x=0,z=1},{x=1,z=0},{x=-1,z=0},{x=0,z=-1}},
	{{x=0,z=1},{x=-1,z=0},{x=0,z=-1},{x=1,z=0}},
	{{x=0,z=1},{x=-1,z=0},{x=1,z=0},{x=0,z=-1}},
	{{x=0,z=-1},{x=0,z=1},{x=-1,z=0},{x=1,z=0}},
	{{x=0,z=-1},{x=0,z=1},{x=1,z=0},{x=-1,z=0}},
	{{x=0,z=-1},{x=1,z=0},{x=-1,z=0},{x=0,z=1}},
	{{x=0,z=-1},{x=1,z=0},{x=0,z=1},{x=-1,z=0}},
	{{x=0,z=-1},{x=-1,z=0},{x=1,z=0},{x=0,z=1}},
	{{x=0,z=-1},{x=-1,z=0},{x=0,z=1},{x=1,z=0}},
	{{x=1,z=0},{x=0,z=1},{x=0,z=-1},{x=-1,z=0}},
	{{x=1,z=0},{x=0,z=1},{x=-1,z=0},{x=0,z=-1}},
	{{x=1,z=0},{x=0,z=-1},{x=0,z=1},{x=-1,z=0}},
	{{x=1,z=0},{x=0,z=-1},{x=-1,z=0},{x=0,z=1}},
	{{x=1,z=0},{x=-1,z=0},{x=0,z=1},{x=0,z=-1}},
	{{x=1,z=0},{x=-1,z=0},{x=0,z=-1},{x=0,z=1}},
	{{x=-1,z=0},{x=0,z=1},{x=1,z=0},{x=0,z=-1}},
	{{x=-1,z=0},{x=0,z=1},{x=0,z=-1},{x=1,z=0}},
	{{x=-1,z=0},{x=0,z=-1},{x=1,z=0},{x=0,z=1}},
	{{x=-1,z=0},{x=0,z=-1},{x=0,z=1},{x=1,z=0}},
	{{x=-1,z=0},{x=1,z=0},{x=0,z=-1},{x=0,z=1}},
	{{x=-1,z=0},{x=1,z=0},{x=0,z=1},{x=0,z=-1}},
}
-- ================================================== --
---------------------Worldgen Water---------------------
-- ================================================== --
local mapgen_prefill = true
local data = {}
local waternodes

if mapgen_prefill then
	local c_water = minetest.get_content_id("nc_terrain:water_source")
	local c_air = minetest.get_content_id("air")
	waternodes = {}

	local fill_to = function (vi, data, area)
		if area:containsi(vi) and area:position(vi).y <= water_level then
			if data[vi] == c_air then
				data[vi] = c_water
				table.insert(waternodes, vi)
			end
		end
	end

--	local count = 0
	local drop_liquid = function(vi, data, area, min_y)
		if data[vi] ~= c_water then
			-- we only care about water.
			return
		end
		local start = vi -- remember the water node we started from
		local ystride = area.ystride
		vi = vi - ystride
		if data[vi] ~= c_air then
			-- if there's no air below this water node, give up immediately.
			return
		end
		vi = vi - ystride -- There's air below the water, so move down one.
		while data[vi] == c_air and area:position(vi).y > min_y do
			-- the min_y check is here to ensure that we don't put water into the mapgen
			-- border zone below our current map chunk where it might get erased by future mapgen activity.
			-- if there's more air, keep going.
			vi = vi - ystride
		end
		vi = vi + ystride -- Move back up one. vi is now pointing at the last air node above the first non-air node.
		data[vi] = c_water
		data[start] = c_air
--		count = count + 1
--		if count % 100 == 0 then
--			minetest.chat_send_all("dropped water " .. (start-vi)/ystride .. " at " .. minetest.pos_to_string(area:position(vi)))
--		end
	end
	
	minetest.register_on_generated(function(minp, maxp, seed)
		if minp.y > water_level then
			-- we're in the sky.
			return
		end
	
		local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
		local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
		vm:get_data(data)
		local maxp_y = maxp.y
		local minp_y = minp.y
		
		if maxp_y > -70 then
			local top = vector.new(maxp.x, math.min(maxp_y, water_level), maxp.z) -- prevents flood fill from affecting any water above sea level
			for vi in area:iterp(minp, top) do
				if data[vi] == c_water then
					table.insert(waternodes, vi)
				end
			end
			
			while table.getn(waternodes) > 0 do
				local vi = table.remove(waternodes)
				local below = vi - area.ystride
				local left = vi - area.zstride
				local right = vi + area.zstride
				local front = vi - 1
				local back = vi + 1
				
				fill_to(below, data, area)
				fill_to(left, data, area)
				fill_to(right, data, area)
				fill_to(front, data, area)
				fill_to(back, data, area)
			end
		else
			-- Caves sometimes generate with liquid nodes hovering in mid air.
			-- This immediately drops them straight down as far as they can go, reducing the ABM thrashing.
			-- We only iterate down to minp.y+1 because anything at minp.y will never be dropped farther anyway.
			for vi in area:iter(minp.x, minp_y+1, minp.z, maxp.x, maxp_y, maxp.z) do
				-- fortunately, area:iter iterates through y columns going upward. Just what we need!
				-- We could possibly be a bit more efficient by remembering how far we dropped then
				-- last liquid node in a column and moving stuff down that far,
				-- but for now let's keep it simple.
				drop_liquid(vi, data, area, minp_y)
			end
		end
		
		vm:set_data(data)
		vm:write_to_map()
		vm:update_liquids()
	end)
end


