local wea = worldeditadditions
local wea_c = worldeditadditions_core

--- Parses a comma-separated side numbers list out into a list of numbers.
-- @param	list	string	The command separated list to parse.
-- @returns	number[]		A list of side numbers.
local function parse_sides_list(list)
	list = list:gsub("%s", "")	-- Spaces are not permitted
	return wea_c.table.unique(wea_c.table.map(
		wea_c.split(list, ","),
		function(value) return tonumber(value) end
	))
end

function worldeditadditions.erode.river(heightmap_initial, heightmap, heightmap_size, region_height, params_custom)
	local params = {
		steps = 1,				-- Number of rounds/passes of the algorithm to run
		lower_sides = "4,3",	-- Cells with this many adjacent horizontal neighbours that are lower than the current pixel will be removed
		raise_sides = "4,3",		-- Cells with this many adjacect horizontal neighbours that are higher than the current pixel will be filled in
		doraise = true,			-- Whether to do raise operations or not
		dolower = true			-- Whether to do lower operations or not
	}
	-- Apply the custom settings
	wea_c.table.apply(params_custom, params)
	
	params.lower_sides = parse_sides_list(params.lower_sides)
	params.raise_sides = parse_sides_list(params.raise_sides)
	
	local timings = {}
	local filled = 0
	local removed = 0
	for i=1,params.steps do
		-- print("[DEBUG:river] step ", i)
		-- wea_c.format.array_2d(heightmap, heightmap_size.x)
		local time_start = wea_c.get_ms_time()
		
		-- Store up changes to make and make them at the end of the step
		-- This is important, because decisions 
		local fill = { }	-- Indexes to add 1 to
		local remove = { }	-- Indexes to take 1 away from
		
		for z = heightmap_size.z - 1, 0, -1 do
			for x = heightmap_size.x - 1, 0, -1 do
				local hi = z*heightmap_size.x + x
				local thisheight = heightmap[hi]
				-- print("[DEBUG:river] z", z, "x", x, "thisheight", thisheight)
				
				local height_up = heightmap[hi]
				local height_down = heightmap[hi]
				local height_left = heightmap[hi]
				local height_right = heightmap[hi]
				
				if x > 0 then height_left = heightmap[z*heightmap_size.x + x-1] end
				if x < heightmap_size.x - 1 then height_right = heightmap[z*heightmap_size.x + x+1] end
				if z > 0 then height_up = heightmap[(z-1)*heightmap_size.x + x] end
				if z < heightmap_size.z - 1 then height_down = heightmap[(z+1)*heightmap_size.x + x] end
				
				-- Whether this pixel is on the edge
				local isedge = x <= 0
					or z <= 0
					or x >= heightmap_size.x - 1
					or z >= heightmap_size.z - 1
				
				local sides_higher = 0	-- Number of sides higher than this pixel
				local sides_lower = 0	-- Number of sides lower than this pixel
				if not isedge then
					if height_down > thisheight then sides_higher = sides_higher + 1 end
					if height_up > thisheight then sides_higher = sides_higher + 1 end
					if height_left > thisheight then sides_higher = sides_higher + 1 end
					if height_right > thisheight then sides_higher = sides_higher + 1 end
					
					if height_down < thisheight then sides_lower = sides_lower + 1 end
					if height_up < thisheight then sides_lower = sides_lower + 1 end
					if height_left < thisheight then sides_lower = sides_lower + 1 end
					if height_right < thisheight then sides_lower = sides_lower + 1 end
				end
				
				-- Perform an action, but only if we're not on the edge
				-- This is important, as we can't accurately tell how many
				-- adjacent neighbours a pixel on the edge has.
				local action = "none"
				if not isedge then 
					if sides_higher > sides_lower then
						for _,sidecount in ipairs(params.raise_sides) do
							if sidecount == sides_higher then
								action = "fill"
								break
							end
						end
					else
						for _i,sidecount in ipairs(params.lower_sides) do
							if sidecount == sides_lower then
								action = "remove"
								break
							end
						end
					end
				end
				
				
				if action == "fill" and params.doraise then
					table.insert(fill, hi)
					filled = filled + 1
				elseif action == "remove" and params.dolower then
					table.insert(remove, hi)
					removed = removed + 1
				end
				-- print("[DEBUG:river] sides_higher", sides_higher, "sides_lower", sides_lower, "action", action)
				-- wea_c.format.array_2d(heightmap, heightmap_size.x)
			end
		end
		
		for _i,hi in ipairs(fill) do
			heightmap[hi] = heightmap[hi] + 1
		end
		for _i,hi in ipairs(remove) do
			heightmap[hi] = heightmap[hi] - 1
		end
		
		table.insert(timings, wea_c.get_ms_time() - time_start)
	end
	
	return true, params.steps.." steps made, raising "..filled.." and lowering "..removed.." columns in "..wea_c.format.human_time(wea_c.sum(timings)).." (~"..wea_c.format.human_time(wea_c.average(timings)).." per step)"
end
