
function calcYDimension(func, sizeX, sizeZ, maxY)
	if maxY == nil then
		if sizeX > sizeZ then
			maxY = sizeX * 4
		else
			maxY = sizeZ * 4
		end
	end
	local max = 0
	local i, j
	for i = 1,sizeX do
		for j = 1,sizeZ do
			local y = func(i, j)
			if y ~= nil and y > max then
				if y > maxY then
					max = maxY
				else
					max = y
				end
			end
		end
	end
	return max + 1
end

function checkBetweenFill(x, y, z, calcY, sideX, sideZ, func, fillMode, maxX, maxZ)
	if sideX < 0 or sideZ < 0 or sideX > maxX or sideZ > maxZ then return false end
	local sideY = func(sideX, sideZ)
	if math.floor(sideY) ~= sideY then
		sideY = math.floor(sideY + 0.499)
	end
	if y > sideY and y < calcY then return true end
	if y < sideY and y > calcY then return true end
	return false
end

function surfaceGetBlockAt(x, y, z, func, fillMode, maxX, maxZ)
	if fillMode == nil then fillMode = "between" end
	local pointY, material = func(x, z)
	if pointY == nil then return nil end
	if material == nil then material = 1 end
	if math.floor(pointY) ~= pointY then
		pointY = math.floor(pointY + 0.499)
	end
	if fillMode == "up" then
		if y >= pointY then return material else return nil end
	elseif fillMode == "down" then
		if y <= pointY then return material else return nil end
	elseif fillMode == "none" then
		if y == pointY then return material else return nil end
	else
		if y == pointY
			or checkBetweenFill(x, y, z, pointY, x - 1, z, func, fillMode, maxX, maxZ)
			or checkBetweenFill(x, y, z, pointY, x + 1, z, func, fillMode, maxX, maxZ)
			or checkBetweenFill(x, y, z, pointY, x, z - 1, func, fillMode, maxX, maxZ)
			or checkBetweenFill(x, y, z, pointY, x, z + 1, func, fillMode, maxX, maxZ) then
			return material
		else
			return nil
		end
	end
end

function prov(sizeX, sizeZ, getYFunc, numMaterials, fillMode, maxY)
	if numMaterials == nil then numMaterials = 1 end
	local sizeY = calcYDimension(getYFunc, sizeX, sizeZ, maxY)
	local provGetBlockAt = function(x, y, z)
		local mat = surfaceGetBlockAt(x, y, z, getYFunc, fillMode, sizeX - 1, sizeZ - 1)
		return mat
	end
	local provider = {
		["sizeX"] = sizeX,
		["sizeY"] = sizeY,
		["sizeZ"] = sizeZ,
		["numMaterials"] = numMaterials,
		["getBlockAt"] = provGetBlockAt
	}
	return provider
end





