
function getDimensions(compound)
	local maxX, maxY, maxZ = nil, nil, nil
	local minX, minY, minZ = nil, nil, nil
	local compIdx, comp
	for compIdx, comp in ipairs(compound.components) do
		local xLow = comp.offsetX + comp.provider.startX
		local yLow = comp.offsetY + comp.provider.startY
		local zLow = comp.offsetZ + comp.provider.startZ
		local xHigh = xLow + comp.provider.sizeX - 1
		local yHigh = yLow + comp.provider.sizeY - 1
		local zHigh = zLow + comp.provider.sizeZ - 1
		if minX == nil or xLow < minX then minX = xLow end
		if minY == nil or yLow < minY then minY = yLow end
		if minZ == nil or zLow < minZ then minZ = zLow end
		if maxX == nil or xHigh > maxX then maxX = xHigh end
		if maxY == nil or yHigh > maxY then maxY = yHigh end
		if maxZ == nil or zHigh > maxZ then maxZ = zHigh end
	end
	if maxX == nil then return nil, nil, nil end
	local sizeX = maxX - minX + 1
	local sizeY = maxY - minY + 1
	local sizeZ = maxZ - minZ + 1
	return sizeX, sizeY, sizeZ, minX, minY, minZ
end

function getNumMaterials(compound)
	local curNum = 1
	local compIdx, comp
	for compIdx, comp in ipairs(compound.components) do
		local maxMaterialNum
		if comp.materialMap == nil then
			maxMaterialNum = comp.provider.numMaterials
		else
			local i
			maxMaterialNum = 0
			for i = 1,comp.provider.numMaterials do
				local cMatNum = i
				if comp.materialMap[i] ~= nil then cMatNum = comp.materialMap[i] end
				if cMatNum > maxMaterialNum then maxMaterialNum = cMatNum end
			end
		end
		if maxMaterialNum > curNum then curNum = maxMaterialNum end
	end
	return curNum
end

function update(compound)
	compound.sizeX, compound.sizeY, compound.sizeZ, compound.startX, compound.startY, compound.startZ = getDimensions(compound)
	compound.numMaterials = getNumMaterials(compound)
end

function compoundGetBlockAt(compound, x, y, z, compoundStartX, compoundStartY, compoundStartZ)
	local compIdx, comp
	for compIdx, comp in ipairs(compound.components) do
		local compX = x - comp.offsetX
		local compY = y - comp.offsetY
		local compZ = z - comp.offsetZ
		if compX >= comp.provider.startX and compX < comp.provider.startX + comp.provider.sizeX
			and compY >= comp.provider.startY and compY < comp.provider.startY + comp.provider.sizeY
			and compZ >= comp.provider.startZ and compZ < comp.provider.startZ + comp.provider.sizeZ
		then
			local compMat = comp.provider.getBlockAt(compX, compY, compZ)
			if compMat ~= nil and compMat ~= 0 then
				if comp.materialMap ~= nil and comp.materialMap[compMat] ~= nil and comp.materialMap[compMap] ~= 0 then
					return comp.materialMap[compMat]
				else
					return compMat
				end
			end
		end
	end
	return nil
end

function create()
	local components = {}
	local obj = {
		["sizeX"] = 0,
		["sizeY"] = 0,
		["sizeZ"] = 0,
		["startX"] = 0,
		["startY"] = 0,
		["startZ"] = 0,
		["numMaterials"] = 0,
		["components"] = components
	}
	obj["add"] = function(provider, offX, offY, offZ, materialMap)
		if provider.startX == nil then provider.startX = 0 end
		if provider.startY == nil then provider.startY = 0 end
		if provider.startZ == nil then provider.startZ = 0 end
		local newComponent = {
			["provider"] = provider,
			["offsetX"] = offX,
			["offsetY"] = offY,
			["offsetZ"] = offZ,
			["materialMap"] = materialMap
		}
		table.insert(components, newComponent)
		update(obj)
		return obj
	end
	obj["getBlockAt"] = function(x, y, z)
		return compoundGetBlockAt(obj, x, y, z, obj.startX, obj.startY, obj.startZ)
	end
	return obj
end


