/// <summary>
        /// Pack items into boxes using the principle of largest volume item first
        /// </summary>
        /// <returns></returns>
        public PackedBoxList PackByVolume()
        {
            var packedBoxes = new PackedBoxList();

            while (Items.GetCount() > 0)
            {
                var boxesToEvaluate      = Boxes.ShallowCopy();
                var boxListToEvaluate    = Boxes.ShallowCopy().GetContent().Cast <Box>().ToList();
                var packedBoxesIteration = new PackedBoxList();

                if (!StartWithLargerBoxes)
                {
                    while (!boxesToEvaluate.IsEmpty())
                    {
                        var box       = boxesToEvaluate.ExtractMin();
                        var packedBox = PackIntoBox(box, Items.ShallowCopy());

                        if (packedBox.GetItems().GetCount() > 0)
                        {
                            packedBoxesIteration.Insert(packedBox);
                            if (packedBox.GetItems().GetCount() == Items.GetCount())
                            {
                                break;
                            }
                        }
                    }
                }
                else
                {
                    boxListToEvaluate.Sort(boxesToEvaluate.ReverseCompareTo);
                    foreach (var box in boxListToEvaluate)
                    {
                        var packedBox = PackIntoBox(box, Items.ShallowCopy());
                        if (packedBox.GetItems().GetCount() > 0)
                        {
                            packedBoxesIteration.Insert(packedBox);
                        }
                    }
                }

                // Check iteration was productive
                if (packedBoxesIteration.IsEmpty() && !CreateBoxesForOversizedItems)
                {
                    var oversizedItem = Items.GetMax();
                    throw new ItemTooLargeException($"Item {oversizedItem.Description} (id: {oversizedItem.Id}) is too large to fit into any box.");
                }

                // 1. Create box, add to boxes list
                // 2. Run through loop to add that product to that box
                if (packedBoxesIteration.IsEmpty() && CreateBoxesForOversizedItems)
                {
                    // TODO: What should the empty box weight be?
                    var oversizedItem = Items.GetMax();
                    var box           = new Box()
                    {
                        Description = String.Format("Custom box for {0}", oversizedItem.Description),
                        GeneratedId = Guid.NewGuid().ToString(),
                        EmptyWeight = 0,
                        InnerDepth  = oversizedItem.Depth,
                        InnerLength = oversizedItem.Length,
                        InnerWidth  = oversizedItem.Width,
                        MaxWeight   = oversizedItem.Weight,
                        OuterDepth  = oversizedItem.Depth,
                        OuterLength = oversizedItem.Length,
                        OuterWidth  = oversizedItem.Width
                    };
                    Boxes.Insert(box);
                    _logger.Log(LogLevel.Debug, "Item {0} (id: {1}) is too large to fit into any box, creating custom box for it.",
                                oversizedItem.Description, oversizedItem.Id);
                }
                else
                {
                    PackedBox bestBox;

                    if (!StartWithLargerBoxes)
                    {
                        bestBox = packedBoxesIteration.GetMin();
                    }
                    else
                    {
                        var packedBoxListToEvaluate = packedBoxesIteration.GetContent().Cast <PackedBox>().ToList();
                        packedBoxListToEvaluate.Sort(packedBoxesIteration.ReverseCompareTo);
                        bestBox = packedBoxListToEvaluate.FirstOrDefault();
                    }

                    var bestBoxItems  = bestBox.GetItems().ShallowCopy();
                    var unpackedItems = Items.GetContent().Cast <Item>().ToList();

                    foreach (var packedItem in bestBoxItems.GetContent())
                    {
                        foreach (var unpackedItem in unpackedItems)
                        {
                            if (packedItem == unpackedItem)
                            {
                                unpackedItems.Remove(unpackedItem);
                                break;
                            }
                        }
                    }

                    var unpackedItemList = new ItemList();
                    foreach (var unpackedItem in unpackedItems)
                    {
                        unpackedItemList.Insert(unpackedItem);
                    }

                    Items = unpackedItemList;
                    packedBoxes.Insert(bestBox);
                }
            }

            return(packedBoxes);
        }