-- Create shortcuts to avoid intensive lookups

local CFrame_new = CFrame.new;
local table_insert = table.insert;
local CFrame_toWorldSpace = CFrame.new().toWorldSpace;
local math_min = math.min;
local math_max = math.max;
local unpack = unpack;

function BoundingBoxModule.CalculateExtents(Items, StaticExtents, ExtentsOnly)
	-- Returns the size and position of a box covering all items in `Items`

	-- Ensure there are items
	if #Items == 0 then
		return;
	end;

	-- Get initial extents data for comparison
	local ComparisonBaseMin = StaticExtents and StaticExtents.Min or Items[1].Position;
	local ComparisonBaseMax = StaticExtents and StaticExtents.Max or Items[1].Position;
	local MinX, MinY, MinZ = ComparisonBaseMin.X, ComparisonBaseMin.Y, ComparisonBaseMin.Z;
	local MaxX, MaxY, MaxZ = ComparisonBaseMax.X, ComparisonBaseMax.Y, ComparisonBaseMax.Z;

	-- Go through each part in `Items`
	for _, Part in pairs(Items) do

		-- Avoid re-calculating for static parts
		if not ((IsPhysicsStatic() or Part.Anchored) and StaticExtents) then

			-- Get shortcuts to part data
			local PartCFrame = Part.CFrame;
			local PartSize = Part.Size / 2;
			local SizeX, SizeY, SizeZ = PartSize.X, PartSize.Y, PartSize.Z;

			local Corner;
			local XPoints, YPoints, ZPoints = {}, {}, {};

			Corner = PartCFrame * CFrame_new(SizeX, SizeY, SizeZ);
			table_insert(XPoints, Corner.x);
			table_insert(YPoints, Corner.y);
			table_insert(ZPoints, Corner.z);

			Corner = PartCFrame * CFrame_new(-SizeX, SizeY, SizeZ);
			table_insert(XPoints, Corner.x);
			table_insert(YPoints, Corner.y);
			table_insert(ZPoints, Corner.z);

			Corner = PartCFrame * CFrame_new(SizeX, -SizeY, SizeZ);
			table_insert(XPoints, Corner.x);
			table_insert(YPoints, Corner.y);
			table_insert(ZPoints, Corner.z);

			Corner = PartCFrame * CFrame_new(SizeX, SizeY, -SizeZ);
			table_insert(XPoints, Corner.x);
			table_insert(YPoints, Corner.y);
			table_insert(ZPoints, Corner.z);

			Corner = PartCFrame * CFrame_new(-SizeX, SizeY, -SizeZ);
			table_insert(XPoints, Corner.x);
			table_insert(YPoints, Corner.y);
			table_insert(ZPoints, Corner.z);

			Corner = PartCFrame * CFrame_new(-SizeX, -SizeY, SizeZ);
			table_insert(XPoints, Corner.x);
			table_insert(YPoints, Corner.y);
			table_insert(ZPoints, Corner.z);

			Corner = PartCFrame * CFrame_new(SizeX, -SizeY, -SizeZ);
			table_insert(XPoints, Corner.x);
			table_insert(YPoints, Corner.y);
			table_insert(ZPoints, Corner.z);

			Corner = PartCFrame * CFrame_new(-SizeX, -SizeY, -SizeZ);
			table_insert(XPoints, Corner.x);
			table_insert(YPoints, Corner.y);
			table_insert(ZPoints, Corner.z);

			-- Reduce gathered points to min/max extents
			MinX = math_min(MinX, unpack(XPoints));
			MinY = math_min(MinY, unpack(YPoints));
			MinZ = math_min(MinZ, unpack(ZPoints));
			MaxX = math_max(MaxX, unpack(XPoints));
			MaxY = math_max(MaxY, unpack(YPoints));
			MaxZ = math_max(MaxZ, unpack(ZPoints));

		end;

	end;

	-- Calculate the extents
	local Extents = {
		Min = Vector3.new(MinX, MinY, MinZ),
		Max = Vector3.new(MaxX, MaxY, MaxZ);
	};

	-- Only return extents if requested
	if ExtentsOnly then
		return Extents;
	end;

	-- Calculate the bounding box size
	local Size = Vector3.new(
		MaxX - MinX,
		MaxY - MinY,
		MaxZ - MinZ
	);

	-- Calculate the bounding box center
	local Position = CFrame.new(
		MinX + (MaxX - MinX) / 2,
		MinY + (MaxY - MinY) / 2,
		MinZ + (MaxZ - MinZ) / 2
	);

	-- Return the size and position
	return Size, Position;

end;

return BoundingBoxModule;
