private void Generate(
            GenerateDirection direction,
            int anchorIndex,
            Size availableSize,
            double minItemSpacing,
            double lineSpacing,
            uint maxItemsPerLine,
            bool disableVirtualization,
            string layoutId)
        {
            if (anchorIndex != -1)
            {
                int step = (direction == GenerateDirection.Forward) ? 1 : -1;

                int    previousIndex       = anchorIndex;
                int    currentIndex        = anchorIndex + step;
                var    anchorBounds        = m_elementManager.GetLayoutBoundsForDataIndex(anchorIndex);
                double lineOffset          = OM.MajorStart(anchorBounds);
                double lineMajorSize       = OM.MajorSize(anchorBounds);
                uint   countInLine         = 1;
                bool   lineNeedsReposition = false;

                while (m_elementManager.IsIndexValidInData(currentIndex) &&
                       (disableVirtualization || ShouldContinueFillingUpSpace(previousIndex, direction)))
                {
                    // Ensure layout element.
                    m_elementManager.EnsureElementRealized(direction == GenerateDirection.Forward, currentIndex, layoutId);
                    var currentElement = m_elementManager.GetRealizedElement(currentIndex);
                    var desiredSize    = MeasureElement(currentElement, currentIndex, availableSize, m_context);

                    // Lay it out.
                    var  previousElement       = m_elementManager.GetRealizedElement(previousIndex);
                    Rect currentBounds         = new Rect(0, 0, desiredSize.Width, desiredSize.Height);
                    var  previousElementBounds = m_elementManager.GetLayoutBoundsForDataIndex(previousIndex);

                    if (direction == GenerateDirection.Forward)
                    {
                        double remainingSpace = OM.Minor(availableSize) - (OM.MinorStart(previousElementBounds) + OM.MinorSize(previousElementBounds) + minItemSpacing + OM.Minor(desiredSize));
                        if (countInLine >= maxItemsPerLine || m_algorithmCallbacks.Algorithm_ShouldBreakLine(currentIndex, remainingSpace))
                        {
                            // No more space in this row. wrap to next row.
                            OM.SetMinorStart(ref currentBounds, 0);
                            OM.SetMajorStart(ref currentBounds, OM.MajorStart(previousElementBounds) + lineMajorSize + lineSpacing);

                            if (lineNeedsReposition)
                            {
                                // reposition the previous line (countInLine items)
                                for (uint i = 0; i < countInLine; i++)
                                {
                                    var dataIndex = currentIndex - 1 - i;
                                    var bounds    = m_elementManager.GetLayoutBoundsForDataIndex((int)dataIndex);
                                    OM.SetMajorSize(ref bounds, lineMajorSize);
                                    m_elementManager.SetLayoutBoundsForDataIndex((int)dataIndex, bounds);
                                }
                            }

                            // Setup for next line.
                            lineMajorSize       = OM.MajorSize(currentBounds);
                            lineOffset          = OM.MajorStart(currentBounds);
                            lineNeedsReposition = false;
                            countInLine         = 1;
                        }
                        else
                        {
                            // More space is available in this row.
                            OM.SetMinorStart(ref currentBounds, OM.MinorStart(previousElementBounds) + OM.MinorSize(previousElementBounds) + (minItemSpacing));
                            OM.SetMajorStart(ref currentBounds, lineOffset);
                            lineMajorSize       = Math.Max(lineMajorSize, OM.MajorSize(currentBounds));
                            lineNeedsReposition = OM.MajorSize(previousElementBounds) != OM.MajorSize(currentBounds);
                            countInLine++;
                        }
                    }
                    else
                    {
                        // Backward
                        double remainingSpace = OM.MinorStart(previousElementBounds) - (OM.Minor(desiredSize) + minItemSpacing);
                        if (countInLine >= maxItemsPerLine || m_algorithmCallbacks.Algorithm_ShouldBreakLine(currentIndex, remainingSpace))
                        {
                            // Does not fit, wrap to the previous row
                            var availableSizeMinor = OM.Minor(availableSize);
                            OM.SetMinorStart(ref currentBounds, !double.IsInfinity(availableSizeMinor) ? availableSizeMinor - OM.Minor(desiredSize) : 0.0);
                            OM.SetMajorStart(ref currentBounds, lineOffset - OM.Major(desiredSize) - lineSpacing);

                            if (lineNeedsReposition)
                            {
                                var previousLineOffset = OM.MajorStart(m_elementManager.GetLayoutBoundsForDataIndex((int)(currentIndex + countInLine + 1)));
                                // reposition the previous line (countInLine items)
                                for (uint i = 0; i < countInLine; i++)
                                {
                                    var dataIndex = currentIndex + 1 + (int)i;
                                    if (dataIndex != anchorIndex)
                                    {
                                        var bounds = m_elementManager.GetLayoutBoundsForDataIndex(dataIndex);
                                        OM.SetMajorStart(ref bounds, previousLineOffset - lineMajorSize - lineSpacing);
                                        OM.SetMajorSize(ref bounds, lineMajorSize);
                                        m_elementManager.SetLayoutBoundsForDataIndex(dataIndex, bounds);
                                    }
                                }
                            }

                            // Setup for next line.
                            lineMajorSize       = OM.MajorSize(currentBounds);
                            lineOffset          = OM.MajorStart(currentBounds);
                            lineNeedsReposition = false;
                            countInLine         = 1;
                        }
                        else
                        {
                            // Fits in this row. put it in the previous position
                            OM.SetMinorStart(ref currentBounds, OM.MinorStart(previousElementBounds) - OM.Minor(desiredSize) - minItemSpacing);
                            OM.SetMajorStart(ref currentBounds, lineOffset);
                            lineMajorSize       = Math.Max(lineMajorSize, OM.MajorSize(currentBounds));
                            lineNeedsReposition = OM.MajorSize(previousElementBounds) != OM.MajorSize(currentBounds);
                            countInLine++;
                        }
                    }

                    m_elementManager.SetLayoutBoundsForDataIndex(currentIndex, currentBounds);

                    previousIndex = currentIndex;
                    currentIndex += step;
                }

                // If we did not reach the top or bottom of the extent, we realized one
                // extra item before we knew we were outside the realization window. Do not
                // account for that element in the indicies inside the realization window.
                if (direction == GenerateDirection.Forward)
                {
                    int dataCount = m_context.ItemCount;
                    m_lastRealizedDataIndexInsideRealizationWindow = previousIndex == dataCount - 1 ? dataCount - 1 : previousIndex - 1;
                    m_lastRealizedDataIndexInsideRealizationWindow = Math.Max(0, m_lastRealizedDataIndexInsideRealizationWindow);
                }
                else
                {
                    int dataCount = m_context.ItemCount;
                    m_firstRealizedDataIndexInsideRealizationWindow = previousIndex == 0 ? 0 : previousIndex + 1;
                    m_firstRealizedDataIndexInsideRealizationWindow = Math.Min(dataCount - 1, m_firstRealizedDataIndexInsideRealizationWindow);
                }

                m_elementManager.DiscardElementsOutsideWindow(direction == GenerateDirection.Forward, currentIndex);
            }
        }