Namespace CalendarData.Common
    ''' <summary>
    ''' Wrapper for <see cref="RichTextBlock"/> that creates as many additional overflow
    ''' columns as needed to fit the available content.
    ''' </summary>
    ''' <example>
    ''' The following creates a collection of 400-pixel wide columns spaced 50 pixels apart
    ''' to contain arbitrary data-bound content:
    ''' <code>
    ''' <RichTextColumns>
    '''     <RichTextColumns.ColumnTemplate>
    '''         <DataTemplate>
    '''             <RichTextBlockOverflow Width="400" Margin="50,0,0,0"/>
    '''         </DataTemplate>
    '''     </RichTextColumns.ColumnTemplate>
    '''     
    '''     <RichTextBlock Width="400">
    '''         <Paragraph>
    '''             <Run Text="{Binding Content}"/>
    '''         </Paragraph>
    '''     </RichTextBlock>
    ''' </RichTextColumns>
    ''' </code>
    ''' </example>
    ''' <remarks>Typically used in a horizontally scrolling region where an unbounded amount of
    ''' space allows for all needed columns to be created.  When used in a vertically scrolling
    ''' space there will never be any additional columns.</remarks>
    <Windows.UI.Xaml.Markup.ContentProperty(Name:="RichTextContent")>
    Public NotInheritable Class RichTextColumns
        Inherits Panel
        ''' <summary>
        ''' Identifies the <see cref="RichTextContent"/> dependency property.
        ''' </summary>
        Public Shared ReadOnly RichTextContentProperty As DependencyProperty = DependencyProperty.Register("RichTextContent", GetType(RichTextBlock), GetType(RichTextColumns), New PropertyMetadata(Nothing, New PropertyChangedCallback(AddressOf ResetOverflowLayout)))

        ''' <summary>
        ''' Identifies the <see cref="ColumnTemplate"/> dependency property.
        ''' </summary>
        Public Shared ReadOnly ColumnTemplateProperty As DependencyProperty = DependencyProperty.Register("ColumnTemplate", GetType(DataTemplate), GetType(RichTextColumns), New PropertyMetadata(Nothing, New PropertyChangedCallback(AddressOf ResetOverflowLayout)))

        ''' <summary>
        ''' Initializes a new instance of the <see cref="RichTextColumns"/> class.
        ''' </summary>
        Public Sub New()
            Me.HorizontalAlignment = HorizontalAlignment.Left
        End Sub

        ''' <summary>
        ''' Gets or sets the initial rich text content to be used as the first column.
        ''' </summary>
        Public Property RichTextContent() As RichTextBlock
            Get
                Return CType(GetValue(RichTextContentProperty), RichTextBlock)
            End Get
            Set
                SetValue(RichTextContentProperty, Value)
            End Set
        End Property

        ''' <summary>
        ''' Gets or sets the template used to create additional
        ''' <see cref="RichTextBlockOverflow"/> instances.
        ''' </summary>
        Public Property ColumnTemplate() As DataTemplate
            Get
                Return CType(GetValue(ColumnTemplateProperty), DataTemplate)
            End Get
            Set
                SetValue(ColumnTemplateProperty, Value)
            End Set
        End Property

        ''' <summary>
        ''' Invoked when the content or overflow template is changed to recreate the column layout.
        ''' </summary>
        ''' <param name="d">Instance of <see cref="RichTextColumns"/> where the change
        ''' occurred.</param>
        ''' <param name="e">Event data describing the specific change.</param>
        Private Shared Sub ResetOverflowLayout(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
            ' When dramatic changes occur, rebuild the column layout from scratch
            Dim target As RichTextColumns = TryCast(d, RichTextColumns)
            If target IsNot Nothing Then
                target._overflowColumns = Nothing
                target.Children.Clear()
                target.InvalidateMeasure()
            End If
        End Sub

        ''' <summary>
        ''' Lists overflow columns already created.  Must maintain a 1:1 relationship with
        ''' instances in the <see cref="Panel.Children"/> collection following the initial
        ''' RichTextBlock child.
        ''' </summary>
        Private _overflowColumns As List(Of RichTextBlockOverflow) = Nothing

        ''' <summary>
        ''' Determines whether additional overflow columns are needed and if existing columns can
        ''' be removed.
        ''' </summary>
        ''' <param name="availableSize">The size of the space available, used to constrain the
        ''' number of additional columns that can be created.</param>
        ''' <returns>The resulting size of the original content plus any extra columns.</returns>
        Protected Overrides Function MeasureOverride(availableSize As Size) As Size
            If Me.RichTextContent Is Nothing Then
                Return New Size(0, 0)
            End If

            ' Make sure the RichTextBlock is a child, using the lack of
            ' a list of additional columns as a sign that this hasn't been
            ' done yet
            If Me._overflowColumns Is Nothing Then
                Children.Add(Me.RichTextContent)
                Me._overflowColumns = New List(Of RichTextBlockOverflow)()
            End If

            ' Start by measuring the original RichTextBlock content
            Me.RichTextContent.Measure(availableSize)
            Dim maxWidth As Double = Me.RichTextContent.DesiredSize.Width
            Dim maxHeight As Double = Me.RichTextContent.DesiredSize.Height
            Dim hasOverflow As Boolean = Me.RichTextContent.HasOverflowContent

            ' Make sure there are enough overflow columns
            Dim overflowIndex As Integer = 0
            While hasOverflow AndAlso maxWidth < availableSize.Width AndAlso Me.ColumnTemplate IsNot Nothing
                ' Use existing overflow columns until we run out, then create
                ' more from the supplied template
                Dim overflow As RichTextBlockOverflow
                If Me._overflowColumns.Count > overflowIndex Then
                    overflow = Me._overflowColumns(overflowIndex)
                Else
                    overflow = CType(Me.ColumnTemplate.LoadContent(), RichTextBlockOverflow)
                    Me._overflowColumns.Add(overflow)
                    Me.Children.Add(overflow)
                    If overflowIndex = 0 Then
                        Me.RichTextContent.OverflowContentTarget = overflow
                    Else
                        Me._overflowColumns(overflowIndex - 1).OverflowContentTarget = overflow
                    End If
                End If

                ' Measure the new column and prepare to repeat as necessary
                overflow.Measure(New Size(availableSize.Width - maxWidth, availableSize.Height))
                maxWidth += overflow.DesiredSize.Width
                maxHeight = Math.Max(maxHeight, overflow.DesiredSize.Height)
                hasOverflow = overflow.HasOverflowContent
                overflowIndex += 1
            End While

            ' Disconnect extra columns from the overflow chain, remove them from our private list
            ' of columns, and remove them as children
            If Me._overflowColumns.Count > overflowIndex Then
                If overflowIndex = 0 Then
                    Me.RichTextContent.OverflowContentTarget = Nothing
                Else
                    Me._overflowColumns(overflowIndex - 1).OverflowContentTarget = Nothing
                End If
                While Me._overflowColumns.Count > overflowIndex
                    Me._overflowColumns.RemoveAt(overflowIndex)
                    Me.Children.RemoveAt(overflowIndex + 1)
                End While
            End If

            ' Report final determined size
            Return New Size(maxWidth, maxHeight)
        End Function

        ''' <summary>
        ''' Arranges the original content and all extra columns.
        ''' </summary>
        ''' <param name="finalSize">Defines the size of the area the children must be arranged
        ''' within.</param>
        ''' <returns>The size of the area the children actually required.</returns>
        Protected Overrides Function ArrangeOverride(finalSize As Size) As Size
            Dim maxWidth As Double = 0
            Dim maxHeight As Double = 0
            For Each child As UIElement In Children
                child.Arrange(New Rect(maxWidth, 0, child.DesiredSize.Width, finalSize.Height))
                maxWidth += child.DesiredSize.Width
                maxHeight = Math.Max(maxHeight, child.DesiredSize.Height)
            Next
            Return New Size(maxWidth, maxHeight)
        End Function
    End Class
End Namespace