local slate_color = Color3.fromRGB(124, 124, 124)
local grass_color = Color3.fromRGB(58, 125, 21) -- Color3.fromRGB(58, 125, 21)

local part = Instance.new("Part")
part.Anchored = true
part.CanCollide = true
part.Parent = game.Workspace
part.Material = Enum.Material.Slate
part.Shape = Enum.PartType.Block
part.Color = slate_color
local boom = 10
local math_abs = math.abs
local math_max = math.max
local math_min = math.min
local math_noise = math.noise
local table_create = table.create
local chunks = {}
local function _n(x,y,z) -- Legacy noise
	local h = (math_noise(x*0.00015,y*0.00015,z*0.00015)*2)^3
	local m = math_noise(x*0.00005,y*0.00005,z*0.00005)
	local s = math_noise(x*0.03,y*0.03,z*0.03)
	local n = math_noise(x*0.0015,y*0.0015*h,z*0.0015)
	y = y - 512 + h * 512
	return ((n)*h-y*0.0005)*boom
	--[[local h = math_noise(x*0.0005,0.123,z*0.0005)*4
	return (math_noise(x*0.0015,y*0.0015*h,z*0.0015)*2*h+math_noise(x*0.005,y*0.005*h,z*0.005)*h-(y+h*10)*0.005)*boom]]
end
local function n_cloud(x,y,z)
	return math_noise(x*0.0002,y*0.0002,z*0.0002)
end
local function n_imaginarium(x,y,z) -- Current noise terrain profile
	x = x * 2
	y = y * 2 + 1024
	z = z * 2
	local conduit = (1-math_abs(math_noise(x*0.00005,y*0.00005,z*0.00005)))^4 + 0.05
	local hillsong = math_noise(x*0.0015,y*0.0015,z*0.0015)*2
	local titans = math_noise(x*0.0002,y*0.0002,z*0.0002)*16
	return ((titans + hillsong) * conduit - y * 0.00075) * boom
end
local function n_tranquility(x,y,z) -- Fields and hills
	y = y - 512
	local tranquility = math_noise(x*0.0001,y*0.0001,z*0.0001)
	local turbulence = math_noise(x*0.0008,y*0.0008,z*0.0008)
	local atmosphere = math_noise(x*0.0005,y*0.0005,z*0.0005)
	return ((atmosphere + turbulence) * tranquility - y * 0.0001) * boom
end
local function n_satisfaction(x,y,z) -- 
	return 0
end
--[[local function fol(x,y,z)
	local density = math_noise(x*0.0001,y*0.0001,z*0.0001)
	local b = math_noise(x*0.001,y*0.001,z*0.001) > 0.5
	--local t = math_noise(x*0.001,y*0.001,z*0.001) > 0.3
	return b
end]]
local function p(x,y,z,f,parent) -- Create a part at a size
	local p = part:Clone()
	p.Parent = parent
	p.Size = Vector3.one*f
	p.Position = Vector3.new(x,y,z)
	return p
end
local compensate = 4 -- Make terrain puffy so the octree system can see it clearly
local function m(f)
	return boom*compensate
end
-- Preinitialise a data structure to hold game.Workspace.Terrain form of terrain data
local base = 4
local mat
local occ
do
	local _otx = table_create(8)
	for _x=1,8 do
		local _oty = table_create(8)  
		for _y=1,8 do
			_oty[_y] = table_create(8)
		end
		_otx[_x] = _oty
	end
	local _mtx = table_create(8)
	for _x=1,8 do
		local _mty = table_create(8)  
		for _y=1,8 do
			_mty[_y] = table_create(8)
		end
		_mtx[_x] = _mty
	end
	occ = _otx
	mat = _mtx
end
-- Use the preinitialised data structure from above to render in a chunk of roblox style terrain
local function ter(x,y,z,f)
	for ix=1,8 do
		for iy=1,8 do
			for iz=1,8 do
				occ[ix][iy][iz] = n(x+(ix-4)*4,y+(iy-4)*4,z+(iz-4)*4)
				mat[ix][iy][iz] = Enum.Material.Grass
			end
		end
	end
	workspace.Terrain:WriteVoxels(Region3.new(Vector3.new(x,y,z)-Vector3.one*f*0.5,Vector3.new(x,y,z)+Vector3.one*f*0.5),4,mat,occ)
end
local voro = require(script.Parent.CellularModule) -- Voronoi noise library
local man = voro.man
local mag = voro.mag
local che = voro.che
local cheman = voro.cheman
local vor = voro.vor
local squir = voro.squircle
local function fol(x,y,z)
	x = x - x % 32
	y = y - y % 32
	z = z - z % 32
	return math_noise(x*0.00025,y*0.00025-2,z*0.00025)
end
local function act(x)
	if x > -5 then return 0.8 end
	--if x > -0.1 then return 0.4 end
	return 0
end
--local pieces = require(script.Parent.TownPieceModule)
--local block = pieces.block
road = function(x,y,z)
	local n1,c1 = vor(x*0.01,y*0.01,z*0.01,che)
	local n2,c2 = vor(x*0.002,y*0.002,z*0.002,che) -- 1 = crowded, 0 = empty
	local c1x = c1.X*100
	local c1y = c1.Y*100
	local c1z = c1.Z*100
	local d = n(c1x,c1y,c1z)*0.025 -- 0 = surface, -1 = air
	if n1 > -d and d > -0.3 --[[and n2 < 0.5]] then
		-- Is centre near ground, is building (thinning towards top), is not road, is centre within bounds
		return c1x,c1y,c1z
	end
	--return vor(x*0.002,y*0.002,z*0.002,cheman) + d + 0.3
end

local surf = require(script.Parent.SurfaceNetModule) -- Surface terrain library
local vox = surf.voxel
local rand = Random.new()
local na = Vector3.new(-1,-1,-1) -- Vectors for cardinal directions (corners of a cube)
local nb = Vector3.new(-1,1,1)
local nc = Vector3.new(1,-1,1)
local nd = Vector3.new(1,1,-1)
local ne = Vector3.new(-1,-1,1)
local nf = Vector3.new(-1,1,-1)
local ng = Vector3.new(1,-1,-1)
local nh = Vector3.new(-1,-1,-1)
local trees = game.ReplicatedStorage.Foliage:GetChildren()
local no_trees = #trees
local rand = Random.new()
local struct = require(script.Parent.StructureModule)
@native
local function chunk(x,y,z,f,b,stuff,v,n)
	struct.make_chunk(x,y,z,f,n)
	local vf = 1/4096
	local parent = stuff.parent
	local n1,c2 = vor(x*vf,y*vf,z*vf,man)
	local val = n(x,y,z)
	local grass = stuff.grass.make_quad -- Quad making function from the surface factory for each material
	local slate = stuff.slate.make_quad
	local bd
	local function s(x,y,z,f)
		if not b then b = 32 end
		local fr = f * 0.25
		-- Shift of positions (XYZ axes)
		local lx = x+fr
		local ly = y+fr
		local lz = z+fr
		local hx = x-fr
		local hy = y-fr
		local hz = z-fr
		-- Noise values (corners)
		local _a,_b,_c,_d,_e,_f,_g,_h = n(lx,ly,lz),n(hx,ly,lz),n(lx,hy,lz),n(lx,ly,hz),n(hx,hy,lz),n(hx,ly,hz),n(lx,hy,hz),n(hx,hy,hz)
		local o,dx,dy,dz = n(x,y,z),n(x+1,y,z),n(x,y+1,z),n(x,y,z+1) -- Gradient of surface noises
		local norm = Vector3.new(o-dx,o-dy,o-dz).Unit---((_a)*(na)+(_b)*(nb)+(_c)*(nc)+(_d)*(nd)+(_e)*(ne)+(_f)*(nf)+(_g)*(ng)+(_h)*(nh)).Unit --GRAD
		local sum = (_a+_b+_c+_d+_e+_f+_g+_h)*0.125 -- Average noise from sample
		--[[if f == b and fol(x,y,z) > 0.62 and sum < 0 then
			local fs = fr * 2 - 16
			for bx = x-fs,x+fs,32 do
				for by = y-fs,y+fs,32 do
					for bz = z-fs,z+fs,32 do
						local cx,cy,cz = road(bx,by,bz)
						if cx then pieces.block(bx,by,bz,32,road,cx,cy,cz) end
					end
				end
			end
			--vox(x,y,z,b,brick,road)
		end]]
		if math_max(_a,_b,_c,_d,_e,_f,_g,_h) < -m(f) then -- Nothing to do
			return
		elseif math_min(_a,_b,_c,_d,_e,_f,_g,_h) > m(f) then -- 
			--[[if b == 4 and f == 32 then
				workspace.Terrain:FillRegion(Region3.new(Vector3.new(x,y,z)-Vector3.one*f*0.5,Vector3.new(x,y,z)+Vector3.one*f*0.5),4,Enum.Material.Grass)
			else]]
			--if b ~= 4 then p(x,y,z,f) end
			--end
		else -- We are near the surface
			--[[if math_abs(norm.Y) < 0.4 and math_abs(sum) < 0.1 and f <= b then
				local part = p(x,y,z,f)
				part.CFrame = CFrame.lookAlong(part.Position,norm)
				part.Size = Vector3.new(part.Size.X*(rand:NextNumber()+2),part.Size.Y*(rand:NextNumber()+2),part.Size.Z*(rand:NextNumber()+1))*2
				part.Position = part.Position + norm * f * 0.5
				return
			end]]
			if f == v then
				if norm.Y < 0.5 then 
					vox(x,y,z,v,slate,n,parent)
				else
					vox(x,y,z,v,grass,n,parent)
				end
			end
			if f == b then -- We are at the rendering level for this chunk
				-- Broad building check
				if math_abs(sum) < 0.5 and f >= 256 then -- If 
					if norm.Y < 0.7 then -- Rock
						local part = p(x,y,z,math.clamp(f*2.5,0,512),parent)
						part.CFrame = CFrame.lookAlong(part.Position-norm*f,norm)
						part.Size = Vector3.one * f * 2
					else -- Foliage (why does this never run?)
						if f > 256 then
							local part = p(x,y,z,f*2.5,parent)
							part.Material = Enum.Material.Grass
							part.Color = grass_color
							part.Shape = Enum.PartType.Ball
						end
					end
				elseif f == 512 and rand:NextInteger(1,5) == 5 and n_cloud(x,y,z) > 0 then
					local part = p(x,y,z,2048*(rand:NextNumber()+1),parent)
					part.Material = Enum.Material.SmoothPlastic
					part.Transparency = 0.7
					part.Shape = Enum.PartType.Ball
					part.Size = part.Size * rand:NextInteger(1,4) * 0.5
				end
			else -- We are too large, recurse into lower octrees
				f = f * 0.5
				s(lx,ly,lz,f) s(hx,ly,lz,f) s(lx,hy,lz,f) s(lx,ly,hz,f) s(hx,hy,lz,f) s(hx,ly,hz,f) s(lx,hy,hz,f) s(hx,hy,hz,f)
			end
		end
	end
	s(x,y,z,f)
end

local grass = surf.make_wedges(nil,{Material = Enum.Material.Grass, Color = grass_color}) -- Terrain constructors??? WedgePart?
local slate = surf.make_wedges(nil,{Material = Enum.Material.Slate, Color = slate_color})
local function write_chunk(x,y,z,r)
	local town_folder = Instance.new("Folder")
	town_folder.Parent = game.Workspace.Terrain
	local folder = Instance.new("Folder")
	folder.Parent = game.Workspace.Terrain
	local root = Vector3.new(x,y,z)*512

	local b = 512
	r = math.floor(r)
	if r == 1 then b = 512
	elseif r == 2 then b = 256
	elseif r >= 3 then b = 128
	--elseif r >= 4 then b = 64
	end
	--task.desynchronize() -- enter parallel and generate the chunk
	chunk(x*1024,y*1024,z*1024,1024,b,{grass = grass, slate = slate, parent = folder, town_parent = town_folder},b,n_imaginarium)
	--task.synchronize() -- come back to us
	
	return folder,town_folder
end

return {write_chunk = write_chunk}