local cellular = require(script.Parent.CellularModule)
local module = {}

local store = game.ReplicatedStorage:WaitForChild("Structure")

local function put(model,x,y,z)
	model = model:Clone()
	model:PivotTo(CFrame.new(Vector3.new(x,y,z)))
	model.Parent = game.Workspace.Terrain
end

local function block(x,y,z,sx,sy,sz)
	local part = Instance.new("Part")
	part.Material = Enum.Material.Slate
	part.Parent = game.Workspace.Terrain
	part.Size = Vector3.new(sx,sy,sz)
	part.Position = Vector3.new(x,y,z)
	part.Transparency = 0
	part.Anchored = true
	part.CanCollide = false
	return part
end

--[[
-- Create a list based tree for octree access
local function create_octree_heap(s,l)
	local sum = 0
	local s_ = l
	local amount = 1
	repeat
		s_ = s_ * 2
		sum = sum + amount
		amount = amount * 8
	until s_ ~= s
	print(sum)
	local tab = table.create()
end

local function index_octree_heap(x,y,z)
	
end
]] -- probably not actually fast enough than direct hash access?. optimise later...

-- Create a list based tree for 6 bit access
local function create_6way_heap()
	local tab = table.create(64)
	for index,_ in ipairs(tab) do
		tab[index] = {}
	end
	return tab
end

-- Index a 6way heap by wall values
local function index_6way_heap(heap,px,py,pz,nx,ny,nz)
	return heap[1 + px * 1 + py * 2 + pz * 4 + nx * 8 + ny * 16 + nz * 32]
end

local function setindex_6way_heap(heap,px,py,pz,nx,ny,nz,v)
	heap[1 + px * 1 + py * 2 + pz * 4 + nx * 8 + ny * 16 + nz * 32] = v
end

local l = -10
-- Index a 6way heap by wall values from a noise function
local function access_6way_heap(heap,x,y,z,f,n)
	f = f * 0.5
	local px = 0 if n(x+f,y,z)>l then px = 1 end
	local py = 0 if n(x,y+f,z)>l then py = 1 end
	local pz = 0 if n(x,y,z+f)>l then pz = 1 end
	local nx = 0 if n(x-f,y,z)>l then nx = 1 end
	local ny = 0 if n(x,y-f,z)>l then ny = 1 end
	local nz = 0 if n(x,y,z-f)>l then nz = 1 end
	return index_6way_heap(heap,px,py,pz,nx,ny,nz)
end

local function preload_6way_heap(heap,stuff)
	for _,piece in pairs(stuff:GetChildren()) do
		local n = piece.Name
		local x = 0 if n:match("PZ") then x = 1 end
		local y = 0 if n:match("PY") then y = 1 end
		local z = 0 if n:match("PX") then z = 1 end
		local nx = 0 if n:match("NZ") then nx = 1 end
		local ny = 0 if n:match("NY") then ny = 1 end
		local nz = 0 if n:match("NX") then nz = 1 end
		piece.PrimaryPart = piece:FindFirstChild("Root") or piece:FindFirstChildWhichIsA("BasePart")
		piece.PrimaryPart:PivotTo(piece.PrimaryPart.CFrame * CFrame.Angles(0,math.pi * 0.5,0))
		if not piece.PrimaryPart then print(piece) error() end
		local p0 = piece:Clone() p0.Name = p0.Name.."_0"
		--p0.PrimaryPart:PivotTo(p0.PrimaryPart.CFrame * CFrame.Angles(0,-math.pi*0.0,0))
		setindex_6way_heap(heap,x,y,z,nx,ny,nz,p0)
		local p90 = piece:Clone() p90.Name = p90.Name.."_90"
		p90.PrimaryPart:PivotTo(p90.PrimaryPart.CFrame * CFrame.Angles(0,math.pi*0.5,0))
		setindex_6way_heap(heap,nz,y,x,z,ny,nx,p90)
		local p180 = piece:Clone() p180.Name = p180.Name.."_180"
		p180.PrimaryPart:PivotTo(p180.PrimaryPart.CFrame * CFrame.Angles(0,math.pi*1.0,0))
		setindex_6way_heap(heap,nx,y,nz,x,ny,z,p180)
		local p270 = piece:Clone() p270.Name = p270.Name.."_270"
		p270.PrimaryPart:PivotTo(p270.PrimaryPart.CFrame * CFrame.Angles(0,math.pi*1.5,0))
		setindex_6way_heap(heap,z,y,nx,nz,ny,x,p270)
		-- Anchor the whole thing into one assembly
		for _,piece in pairs({p0,p90,p180,p270}) do
			for _,part in pairs(piece:GetChildren()) do
				if part:IsA("BasePart") and part ~= piece.PrimaryPart then
					local weld = Instance.new("WeldConstraint")
					weld.Parent = piece
					weld.Name = "BuildingWeld"
					weld.Part0 = part
					weld.Part1 = piece.PrimaryPart
				end
			end
			piece.PrimaryPart.Anchored = true
		end
	end
end

local heap_6way_32_bank = create_6way_heap()
preload_6way_heap(heap_6way_32_bank,store.Bank)
local heap_6way_64_slums = create_6way_heap()
preload_6way_heap(heap_6way_64_slums,store.Slums)

-- Rule: Any block only needs to support up to half away from any connected wall.
--       Any block is allowed to protrude up to half into empty space from no wall.

local function building_32(x,y,z,sx,sy,sz)
	local b = block(x,y,z,sx,sy,sz)
	b.Color = Color3.new(0.568627, 0.341176, 0.152941)
	b.Material = Enum.Material.WoodPlanks
end

local function support_32(x,y,z,sx,sy,sz)
	local b = block(x,y,z,sx,sy,sz)
	b.Color = Color3.new(0.6, 0.6, 0.6)
	b.Material = Enum.Material.Slate
end

local function cover_128_32(x,y,z,n)
	local s = 128 * 0.5
	local f = 32
	local f2 = f * 0.5
	for ox = -s + f2 + x,s - f2 + x,f do
		for oy = -s + f2 + y,s - f2 + y,f do
			for oz = -s + f2 + z,s - f2 + z,f do
				local v = n(ox,oy,oz)
				if v > l then
					local jigsaw = access_6way_heap(heap_6way_32_bank,ox,oy,oz,32,n)
					if jigsaw then
						local blk = jigsaw:Clone()
						blk.Parent = game.Workspace
						blk:PivotTo(CFrame.new(ox,oy,oz))
					end
				end
			end
		end
	end
end

local function compose_128_32(x,y,z,n)
	local rx = x - 64 + 16
	local ry = y - 64 + 16
	local rz = z - 64 + 16
	local i = 128
	local px = n(x+i,y,z)>l
	local py = n(x,y+i,z)>l
	local pz = n(x,y,z+i)>l
	local nx = n(x-i,y,z)>l
	local ny = n(x,y-i,z)>l
	local nz = n(x,y,z-i)>l
	local function p(x,y,z,...) -- px,py...
		local jigsaw = index_6way_heap(heap_6way_32_bank,...)
		if jigsaw then
			local blk = jigsaw:Clone()
			blk.Parent = game.Workspace
			blk:PivotTo(CFrame.new(x,y,z))
		end
	end
	local s = 128 * 0.5
	local f = 32
	local f2 = f * 0.5
	for dx = -3,3,2 do
		for dy = -3,3,2 do
			for dz = -3,3,2 do
				local _px = 1 if not px and dx ==  1 then _px = 0 end
				local _py = 1 if not py and dy ==  1 then _py = 0 end
				local _pz = 1 if not pz and dz ==  1 then _pz = 0 end
				local _nx = 1 if not nx and dx == -1 then _nx = 0 end
				local _ny = 1 if not ny and dy == -1 then _ny = 0 end
				local _nz = 1 if not nz and dz == -1 then _nz = 0 end
				if 
					(not px and dx ==  3) or
					(not py and dy ==  3) or 
					(not pz and dz ==  3) or 
					(not nx and dx == -3) or 
					(not ny and dy == -3) or
					(not nz and dz == -3)
				then continue end
				local jigsaw = index_6way_heap(heap_6way_32_bank,_px,_py,_pz,_nx,_ny,_nz)
				if jigsaw then
					local blk = jigsaw:Clone()
					blk.Parent = game.Workspace
					blk:PivotTo(CFrame.new(dx * f2 + x,dy * f2 + y,dz * f2 + z))
				end
			end
		end
	end
end

local function cover_1024_128(x,y,z,n)
	local s = 1024 * 0.5
	local f = 64
	local f2 = f * 0.5
	for ox = -s + f2 + x,s - f2 + x,f do
		for oy = -s + f2 + y,s - f2 + y,f do
			for oz = -s + f2 + z,s - f2 + z,f do
				local v = n(ox,oy,oz)
				--local blk = block(ox,oy,oz,128,128,128)
				if v > 0 then
					--put(store.Support1,ox,oy,oz)
				elseif v > l then
					local jigsaw = access_6way_heap(heap_6way_64_slums,ox,oy,oz,64,n)
					if jigsaw then
						local blk = jigsaw:Clone()
						blk.Parent = game.Workspace
						blk:PivotTo(CFrame.new(ox,oy,oz))
					end
					--compose_128_32(ox,oy,oz,n)
				end
			end
		end
	end
end

function module.make_chunk(x,y,z,f,n)
	cover_1024_128(x,y,z,n)
end

return module
