﻿''' <summary>
''' Represents a collection of styled Unicode characters. This class cannot be inherited.
''' </summary>
<DebuggerDisplay("{ToString()}")> _
Public NotInheritable Class StyledString
    Implements IDisposable
    Implements IEnumerable
    Implements ICloneable

    '//operators
    ''' <summary>
    ''' Converts a StyledString to a System.String using a narrowing conversion.
    ''' </summary>
    ''' <param name="value">The StyledString to convert.</param>
    Public Shared Narrowing Operator CType(ByVal value As StyledString) As String
        Return value.ToString()
    End Operator

    ''' <summary>
    ''' Converts a System.String to a StyledString using a widening conversion.
    ''' </summary>
    ''' <param name="value">The System.String to convert.</param>
    Public Shared Widening Operator CType(ByVal value As String) As StyledString
        Return New StyledString(value)
    End Operator

    '//constants
    ''' <summary>
    ''' Represents the default flags that will be used to render the string to the device context.
    ''' </summary>
    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
    Public Shared ReadOnly DefaultFlags As TextFormatFlags

    ''' <summary>
    ''' Represents the default color that will be used to render the string to the device context.
    ''' </summary>
    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
    Public Shared ReadOnly DefaultColor As Color

    '//fields
    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
    Private disposedValue As Boolean

    '//properties
    ''' <summary>
    ''' Gets or sets the element at the specified index.
    ''' </summary>
    ''' <param name="index">The zero-based index of the element to get or set.</param>
    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
    Default Public Property Item(ByVal index As Integer) As StyledChar
        Get
            Return Me.List(index)
        End Get
        Set(ByVal value As StyledChar)
            Me.List(index) = value
        End Set
    End Property

    ''' <summary>
    ''' Gets the number of elements actually contained in the collection.
    ''' </summary>
    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
    Public ReadOnly Property Count() As Integer
        Get
            Return Me.List.Count
        End Get
    End Property

    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
    Private _list As List(Of StyledChar)
    ''' <summary>
    ''' Gets the internal list that contains the styled characters.
    ''' </summary>
    <DebuggerBrowsable(DebuggerBrowsableState.Collapsed)> _
    Protected ReadOnly Property List() As List(Of StyledChar)
        Get
            If Me._list Is Nothing Then
                Me._list = New List(Of StyledChar)()
            End If
            Return Me._list
        End Get
    End Property

    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
    Private _font As Font
    ''' <summary>
    ''' Gets the default font to assign to a newly created StyledChar.
    ''' </summary>
    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
    Protected ReadOnly Property Font() As Font
        Get
            Return Me._font
        End Get
    End Property

    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
    Private _color As Color = Color.Empty
    ''' <summary>
    ''' Gets the overridden default color to assign to a newly created StyledChar.
    ''' </summary>
    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
    Protected ReadOnly Property Color() As Color
        Get
            If Me._color.Equals(Color.Empty) Then
                Return StyledString.DefaultColor
            End If
            Return Me._color
        End Get
    End Property

    '//constructors
    ''' <summary>
    ''' Initializes the static variables of the StyledString class.
    ''' </summary>
    Shared Sub New()
        StyledString.DefaultFlags = TextFormatFlags.NoPadding
        StyledString.DefaultColor = SystemColors.ControlText
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the StyledString class.
    ''' </summary>
    Public Sub New()
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the StyledString class with the specified default font.
    ''' </summary>
    ''' <param name="defaultFont">The default font applied to any new StyledChar objects.</param>
    Public Sub New(ByVal defaultFont As Font)
        Me._font = defaultFont
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the StyledString class with the specified default color.
    ''' </summary>
    ''' <param name="defaultColor">The default color applied to any new StyledChar objects.</param>
    Public Sub New(ByVal defaultColor As Color)
        Me._color = defaultColor
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the StyledString class with the specified default font and color.
    ''' </summary>
    ''' <param name="defaultFont">The default font applied to any new StyledChar objects.</param>
    ''' <param name="defaultColor">The default color applied to any new StyledChar objects.</param>
    Public Sub New(ByVal defaultFont As Font, ByVal defaultColor As Color)
        Me._font = defaultFont
        Me._color = defaultColor
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the StyledString class with the specified System.String.
    ''' </summary>
    ''' <param name="value">The initial string used to build the current instance.</param>
    Public Sub New(ByVal value As String)
        Me.New(value, Nothing)
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the StyledString class with the specified System.String and System.Drawing.Font.
    ''' </summary>
    ''' <param name="value">The initial string used to build the current instance.</param>
    ''' <param name="defaultFont">The default font applied to any new StyledChar objects.</param>
    Public Sub New(ByVal value As String, ByVal defaultFont As Font)
        Me.New(value, defaultFont, StyledString.DefaultColor)
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the StyledString class with the specified parameters.
    ''' </summary>
    ''' <param name="value">The initial string used to build the current instance.</param>
    ''' <param name="defaultFont">The default font applied to any new StyledChar objects.</param>
    ''' <param name="defaultColor">The default color applied to any new StyledChar objects.</param>
    Public Sub New(ByVal value As String, ByVal defaultFont As Font, ByVal defaultColor As Color)
        Me.New(StyledChar.FromString(value), defaultFont, defaultColor)
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the StyledString class with the specified StyledChar objects.
    ''' </summary>
    ''' <param name="collection">The collection that will be added to the end of the current instance.</param>
    Public Sub New(ByVal collection As IEnumerable(Of StyledChar))
        Me.List.AddRange(collection)
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the StyledString class with the specified StyledChar objects and specified System.Drawing.Font.
    ''' </summary>
    ''' <param name="collection">The collection that will be added to the end of the current instance.</param>
    ''' <param name="defaultFont">The default font applied to any new StyledChar objects.</param>
    Public Sub New(ByVal collection As IEnumerable(Of StyledChar), ByVal defaultFont As Font)
        Me.New(collection)
        Me.ApplyFont(defaultFont)
        Me._font = defaultFont
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the StyledString class with the specified parameters.
    ''' </summary>
    ''' <param name="collection">The collection that will be added to the end of the current instance.</param>
    ''' <param name="defaultFont">The default font applied to any new StyledChar objects.</param>
    ''' <param name="defaultColor">The default color applied to any new StyledChar objects.</param>
    Public Sub New(ByVal collection As IEnumerable(Of StyledChar), ByVal defaultFont As Font, ByVal defaultColor As Color)
        Me.New(collection, defaultFont)
        Me.ApplyColor(defaultColor)
        Me._color = defaultColor
    End Sub

    '//methods
    ''' <summary>
    ''' Applies the specified font to the entire collection.
    ''' </summary>
    ''' <param name="font">The font to apply.</param>
    Public Sub ApplyFont(ByVal font As Font)
        For Each c In Me.List
            c.Font = font
        Next
    End Sub

    ''' <summary>
    ''' Applies the specified font to the collection starting at the specified index.
    ''' </summary>
    ''' <param name="font">The font to apply.</param>
    ''' <param name="startIndex">The index to start applying the font to.</param>
    Public Sub ApplyFont(ByVal font As Font, ByVal startIndex As Integer)
        For i = startIndex To Me.Count - 1
            Me(i).Font = font
        Next
    End Sub

    ''' <summary>
    ''' Applies the specified font to the collection starting and ending at the specified indices.
    ''' </summary>
    ''' <param name="font">The font to apply.</param>
    ''' <param name="startIndex">The index to start applying the font to.</param>
    ''' <param name="endIndex">The index to end applying the font to.</param>
    Public Sub ApplyFont(ByVal font As Font, ByVal startIndex As Integer, ByVal endIndex As Integer)
        For i = startIndex To endIndex
            Me(i).Font = font
        Next
    End Sub

    ''' <summary>
    ''' Applies the specified color to the entire collection.
    ''' </summary>
    ''' <param name="color">The color to apply.</param>
    Public Sub ApplyColor(ByVal color As Color)
        For Each c In Me.List
            c.Color = color
        Next
    End Sub

    ''' <summary>
    ''' Applies the specified color to the collection starting at the specified index.
    ''' </summary>
    ''' <param name="color">The color to apply.</param>
    ''' <param name="startIndex">The index to start applying the font to.</param>
    Public Sub ApplyColor(ByVal color As Color, ByVal startIndex As Integer)
        If startIndex > Me.Count Then
            Return
        End If
        For i = startIndex To Me.Count - 1
            Me(i).Color = color
        Next
    End Sub

    ''' <summary>
    ''' Applies the specified color to the collection starting and ending at the specified indices.
    ''' </summary>
    ''' <param name="color">The color to apply.</param>
    ''' <param name="startIndex">The index to start applying the font to.</param>
    ''' <param name="endIndex">The index to end applying the font to.</param>
    Public Sub ApplyColor(ByVal color As Color, ByVal startIndex As Integer, ByVal endIndex As Integer)
        If (startIndex > Me.Count) OrElse (endIndex > Me.Count) Then
            Return
        End If
        For i = startIndex To endIndex
            Me(i).Color = color
        Next
    End Sub

    ''' <summary>
    ''' Applies the specified colors to the entire collection.
    ''' </summary>
    ''' <param name="colors">The colors to apply.</param>
    Public Sub ApplyColors(ByVal colors As Color())
        Me.ApplyColors(colors, -1)
    End Sub

    ''' <summary>
    ''' Applies the specified colors to the entire collection and optionally skips white space.
    ''' </summary>
    ''' <param name="colors">The colors to apply.</param>
    ''' <param name="skipWhiteSpace">Indicates whether the specified colors should be applied to white space.</param>
    Public Sub ApplyColors(ByVal colors As Color(), ByVal skipWhiteSpace As Boolean)
        Me.ApplyColors(colors, If(skipWhiteSpace, -2, -1))
    End Sub

    ''' <summary>
    ''' Applies the specified colors to the collection starting at the specified index.
    ''' </summary>
    ''' <param name="colors">The colors to apply.</param>
    ''' <param name="startIndex">The index to start applying the colors to.</param>
    Public Sub ApplyColors(ByVal colors As Color(), ByVal startIndex As Integer)
        If colors Is Nothing Then
            Throw New ArgumentNullException("colors")
        End If
        If colors.Length = 0 Then
            Return
        End If
        If startIndex <= -1 Then
            Dim current = 0
            For i = 0 To Me.Count - 1
                Dim currentChar = Me(i)
                If startIndex.Equals(-2) AndAlso Char.IsWhiteSpace(currentChar.Character) Then
                    Continue For
                End If
                currentChar.Color = colors(current)
                current += 1
                If current = colors.Length Then
                    current = 0
                End If
            Next
        Else
            For i = startIndex To (startIndex + colors.Length) - 1
                Me(i).Color = colors(i - startIndex)
            Next
        End If
    End Sub

    ''' <summary>
    ''' Applies the specified color and font to the entire collection.
    ''' </summary>
    ''' <param name="color">The color to apply.</param>
    ''' <param name="font">The font to apply.</param>
    Public Sub ApplyStyle(ByVal color As Color, ByVal font As Font)
        Me.ApplyColor(color)
        Me.ApplyFont(font)
    End Sub

    ''' <summary>
    ''' Applies the specified color and font to the collection starting at the specified index.
    ''' </summary>
    ''' <param name="color">The color to apply.</param>
    ''' <param name="font">The font to apply.</param>
    ''' <param name="startIndex">The index to start applying the font to.</param>
    Public Sub ApplyStyle(ByVal color As Color, ByVal font As Font, ByVal startIndex As Integer)
        Me.ApplyColor(color, startIndex)
        Me.ApplyFont(font, startIndex)
    End Sub

    ''' <summary>
    ''' Applies the specified color and font to the collection starting and ending at the specified indices.
    ''' </summary>
    ''' <param name="color">The color to apply.</param>
    ''' <param name="font">The font to apply.</param>
    ''' <param name="startIndex">The index to start applying the font to.</param>
    ''' <param name="endIndex">The index to end applying the font to.</param>
    Public Sub ApplyStyle(ByVal color As Color, ByVal font As Font, ByVal startIndex As Integer, ByVal endIndex As Integer)
        Me.ApplyColor(color, startIndex, endIndex)
        Me.ApplyFont(font, startIndex, endIndex)
    End Sub

    ''' <summary>
    ''' Creates and assigns a new dark color to each element in the collection.
    ''' </summary>
    Public Sub Darken()
        For Each c In Me.List
            c.Color = ControlPaint.Dark(c.Color)
        Next
    End Sub

    ''' <summary>
    ''' Draws the current instance to the specified device context at the specified x- and y-coordinates.
    ''' </summary>
    ''' <param name="dc">The device context in which to draw the text.</param>
    ''' <param name="x">The x-coordinate of the upper-left corner of the drawn text.</param>
    ''' <param name="y">The y-coordinate of the upper-left corner of the drawn text.</param>
    Public Sub Draw(ByVal dc As IDeviceContext, ByVal x As Integer, ByVal y As Integer)
        Me.Draw(dc, New Point(x, y))
    End Sub

    ''' <summary>
    ''' Draws the current instance to the specified device context at the specified location.
    ''' </summary>
    ''' <param name="dc">The device context in which to draw the text.</param>
    ''' <param name="pt">The location of the upper-left corner of the drawn text.</param>
    Public Sub Draw(ByVal dc As IDeviceContext, ByVal pt As Point)
        Dim initialPoint = pt
        Dim ranges = Me.Split()
        For Each range As StyledString In ranges
            Using range
                Dim s = range.Measure(dc)
                For Each c In range.List
                    Dim charSize = TextRenderer.MeasureText(dc, c.Character, c.Font, Size.Empty, StyledString.DefaultFlags)
                    TextRenderer.DrawText(dc, c.Character, c.Font, pt, c.Color, StyledString.DefaultFlags)
                    pt.Offset(charSize.Width, 0)
                Next
                pt.Y += (s.Height)
            End Using
            pt.X = initialPoint.X
        Next
    End Sub

    ''' <summary>
    ''' Releases all resources used by the current instance.
    ''' </summary>
    Public Sub Dispose() Implements IDisposable.Dispose
        Me.Dispose(True)
        System.GC.SuppressFinalize(Me)
    End Sub

    ''' <summary>
    ''' Creates and assigns a new light color to each element in the collection.
    ''' </summary>
    Public Sub Lighten()
        For Each c In Me.List
            c.Color = ControlPaint.Light(c.Color)
        Next
    End Sub

    ''' <summary>
    ''' Swaps the colors of the StyledChar objects at the specified indices.
    ''' </summary>
    ''' <param name="index0">The first index.</param>
    ''' <param name="index1">The second index.</param>
    Public Sub SwapColor(ByVal index0 As Integer, ByVal index1 As Integer)
        StyledString.SwapColor(Me(index0), Me(index1))
    End Sub

    ''' <summary>
    ''' Swaps the colors of the specified StyledChar objects.
    ''' </summary>
    ''' <param name="first">The first StyledChar to swap.</param>
    ''' <param name="second">The second StyledChar to swap.</param>
    Public Shared Sub SwapColor(ByVal first As StyledChar, ByVal second As StyledChar)
        Dim firstColor = first.Color
        first.Color = second.Color
        second.Color = firstColor
    End Sub

    ''' <summary>
    ''' Swaps the fonts of the StyledChar objects at the specified indices.
    ''' </summary>
    ''' <param name="index0">The first index.</param>
    ''' <param name="index1">The second index.</param>
    Public Sub SwapFont(ByVal index0 As Integer, ByVal index1 As Integer)
        StyledString.SwapFont(Me(index0), Me(index1))
    End Sub

    ''' <summary>
    ''' Swaps the fonts of the specified StyledChar objects.
    ''' </summary>
    ''' <param name="first">The first StyledChar to swap.</param>
    ''' <param name="second">The second StyledChar to swap.</param>
    Public Shared Sub SwapFont(ByVal first As StyledChar, ByVal second As StyledChar)
        Dim firstFont = first.Font
        first.Font = second.Font
        second.Font = firstFont
    End Sub

    ''' <summary>
    ''' Replaces the specified Unicode character for each element with the replacement.
    ''' </summary>
    ''' <param name="ch">The Unicode character to seek.</param>
    ''' <param name="replacement">The replacement Unicode character.</param>
    Public Sub Replace(ByVal ch As Char, ByVal replacement As Char)
        Me.Replace(ch, replacement, -1)
    End Sub

    ''' <summary>
    ''' Replaces the specified Unicode character for each element with the replacement.
    ''' </summary>
    ''' <param name="ch">The Unicode character to seek.</param>
    ''' <param name="replacement">The replacement Unicode character.</param>
    ''' <param name="startIndex">The index to start the replacing</param>
    Public Sub Replace(ByVal ch As Char, ByVal replacement As Char, ByVal startIndex As Integer)
        Me.Replace(ch, replacement, startIndex, -1)
    End Sub

    ''' <summary>
    ''' Replaces the specified Unicode character for each element with the replacement.
    ''' </summary>
    ''' <param name="ch">The Unicode character to seek.</param>
    ''' <param name="replacement">The replacement Unicode character.</param>
    ''' <param name="startIndex">The index to start the replacing</param>
    ''' <param name="endIndex">The index to stop the replacing</param>
    Public Sub Replace(ByVal ch As Char, ByVal replacement As Char, ByVal startIndex As Integer, ByVal endIndex As Integer)
        Select Case True
            Case startIndex = -1 AndAlso endIndex = -1
                For Each c In Me.List
                    If c.Character.Equals(ch) Then
                        c.Character = replacement
                    End If
                Next
            Case startIndex > -1 AndAlso endIndex = -1
                Dim current As StyledChar
                For i = startIndex To Me.Count - 1
                    current = Me(i)
                    If current.Character.Equals(ch) Then
                        current.Character = replacement
                    End If
                Next
            Case startIndex > -1 AndAlso endIndex > -1
                Dim current As StyledChar
                For i = startIndex To endIndex
                    current = Me(i)
                    If current.Character.Equals(ch) Then
                        current.Character = replacement
                    End If
                Next
            Case Else
                Return
        End Select
    End Sub

    ''' <summary>
    ''' Appends the specified Unicode character to the end of the collection and returns the styled version of it.
    ''' </summary>
    ''' <param name="ch">The Unicode character to append.</param>
    Public Function Append(ByVal ch As Char) As StyledChar
        Return Me.Append(ch, Me.Font, Me.Color)
    End Function

    ''' <summary>
    ''' Appends the specified Unicode character to the end of the collection and returns the styled version of it.
    ''' </summary>
    ''' <param name="ch">The Unicode character to append.</param>
    ''' <param name="color">The color to apply to the Unicode character.</param>
    Public Function Append(ByVal ch As Char, ByVal color As Color) As StyledChar
        Return Me.Append(ch, Me.Font, color)
    End Function

    ''' <summary>
    ''' Appends the specified Unicode character to the end of the collection and returns the styled version of it.
    ''' </summary>
    ''' <param name="ch">The Unicode character to append.</param>
    ''' <param name="font">The font to apply to the Unicode character.</param>
    Public Function Append(ByVal ch As Char, ByVal font As Font) As StyledChar
        Return Me.Append(ch, font, Me.Color)
    End Function

    ''' <summary>
    ''' Appends the specified Unicode character to the end of the collection and returns the styled version of it.
    ''' </summary>
    ''' <param name="ch">The Unicode character to append.</param>
    ''' <param name="font">The font to apply to the Unicode character.</param>
    ''' <param name="color">The color to apply to the Unicode character.</param>
    Public Function Append(ByVal ch As Char, ByVal font As Font, ByVal color As Color) As StyledChar
        Dim result = StyledChar.FromChar(ch)
        result.Font = font
        result.Color = color
        Me.List.Add(result)
        Return result
    End Function

    ''' <summary>
    ''' Appends the specified System.String to the end of the collection and returns an array of the styled characters.
    ''' </summary>
    ''' <param name="value">The System.String to append.</param>
    Public Function Append(ByVal value As String) As StyledChar()
        Return Me.Append(value, Me.Font, Me.Color)
    End Function

    ''' <summary>
    ''' Appends the specified System.String to the end of the collection and returns an array of the styled characters.
    ''' </summary>
    ''' <param name="value">The System.String to append.</param>
    ''' <param name="color">The color to apply to all of the characters.</param>
    Public Function Append(ByVal value As String, ByVal color As Color) As StyledChar()
        Return Me.Append(value, Me.Font, color)
    End Function

    ''' <summary>
    ''' Appends the specified System.String to the end of the collection and returns an array of the styled characters.
    ''' </summary>
    ''' <param name="value">The System.String to append.</param>
    ''' <param name="font">The font to apply to all of the characters.</param>
    Public Function Append(ByVal value As String, ByVal font As Font) As StyledChar()
        Return Me.Append(value, font, Me.Color)
    End Function

    ''' <summary>
    ''' Appends the specified System.String to the end of the collection and returns an array of the styled characters.
    ''' </summary>
    ''' <param name="value">The System.String to append.</param>
    ''' <param name="font">The font to apply to all of the characters.</param>
    ''' <param name="color">The color to apply to all of the characters.</param>
    Public Function Append(ByVal value As String, ByVal font As Font, ByVal color As Color) As StyledChar()
        Dim result = StyledChar.FromString(value)
        For Each c In result
            c.Font = font
            c.Color = color
        Next
        Me.List.AddRange(result)
        Return result
    End Function

    ''' <summary>
    ''' Appends the specified StyledChar to the end of the collection and returns the index it was added at.
    ''' </summary>
    ''' <param name="ch">The StyledChar to append.</param>
    Public Function Append(ByVal ch As StyledChar) As Integer
        Me.List.Add(ch)
        Return Me.Count - 1
    End Function

    ''' <summary>
    ''' Appends the default line terminator to the end of the collection and returns an array of the styled characters.
    ''' </summary>
    Public Function AppendLine() As StyledChar()
        Dim result = StyledChar.FromString(Environment.NewLine)
        Me.List.AddRange(result)
        Return result.ToArray()
    End Function

    ''' <summary>
    ''' Appends the specified string followed by the default line terminator to the end of the collection and returns an array of the styled characters.
    ''' </summary>
    ''' <param name="value">The System.String to append.</param>
    Public Function AppendLine(ByVal value As String) As StyledChar()
        Return Me.AppendLine(value, Me.Font, Me.Color)
    End Function

    ''' <summary>
    ''' Appends the specified string followed by the default line terminator to the end of the collection and returns an array of the styled characters.
    ''' </summary>
    ''' <param name="value">The System.String to append.</param>
    ''' <param name="color">The color to apply to all of the characters.</param>
    Public Function AppendLine(ByVal value As String, ByVal color As Color) As StyledChar()
        Return Me.AppendLine(value, Me.Font, color)
    End Function

    ''' <summary>
    ''' Appends the specified string followed by the default line terminator to the end of the collection and returns an array of the styled characters.
    ''' </summary>
    ''' <param name="value">The System.String to append.</param>
    ''' <param name="font">The font to apply to all of the characters.</param>
    Public Function AppendLine(ByVal value As String, ByVal font As Font) As StyledChar()
        Return Me.AppendLine(value, font, Me.Color)
    End Function

    ''' <summary>
    ''' Appends the specified string followed by the default line terminator to the end of the collection and returns an array of the styled characters.
    ''' </summary>
    ''' <param name="value">The System.String to append.</param>
    ''' <param name="font">The font to apply to all of the characters.</param>
    ''' <param name="color">The color to apply to all of the characters.</param>
    Public Function AppendLine(ByVal value As String, ByVal font As Font, ByVal color As Color) As StyledChar()
        Dim result = StyledChar.FromString(String.Concat(value, Environment.NewLine))
        For Each c In result
            c.Font = font
            c.Color = color
        Next
        Me.List.AddRange(result)
        Return result.ToArray()
    End Function

    ''' <summary>
    ''' Appends the specified formatted text to the end of the collection and returns an array of the styled characters.
    ''' </summary>
    ''' <param name="format">A composite format string.</param>
    ''' <param name="args">An System.Object array containing zero or more objects to format.</param>
    Public Function AppendFormat(ByVal format As String, ByVal ParamArray args() As Object) As StyledChar()
        Dim result = StyledChar.FromString(String.Format(format, args))
        Me.List.AddRange(result)
        Return result.ToArray()
    End Function

    ''' <summary>
    ''' Creates and returns a copy of the current instance.
    ''' </summary>
    Public Function Clone() As Object Implements System.ICloneable.Clone
        Dim result = New StyledString(Me.List.Select(Function(c) DirectCast(c.Clone(), StyledChar)))
        result._color = Me.Color
        result._font = Me.Font
        Return result
    End Function

    ''' <summary>
    ''' Returns an enumerator that iterates through the collection.
    ''' </summary>
    Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
        Return Me.List.GetEnumerator()
    End Function

    ''' <summary>
    ''' Returns a value indicating whether the specified Unicode character is contained in the current instance.
    ''' </summary>
    ''' <param name="ch">The Unicode character to seek.</param>
    Public Function Contains(ByVal ch As Char) As Boolean
        Return Me.IndexOf(ch) > 0
    End Function

    ''' <summary>
    ''' Returns the index of the first occurrance of the specified Unicode character.
    ''' </summary>
    ''' <param name="ch">The Unicode character to seek.</param>
    Public Function IndexOf(ByVal ch As Char) As Integer
        Return Me.ToString().IndexOf(ch)
    End Function

    ''' <summary>
    ''' Returns the index of the first occurrance of the specified Unicode character from the specified index.
    ''' </summary>
    ''' <param name="ch">The Unicode character to seek.</param>
    ''' <param name="startIndex">The search starting position.</param>
    Public Function IndexOf(ByVal ch As Char, ByVal startIndex As Integer) As Integer
        Return Me.ToString().IndexOf(ch, startIndex)
    End Function

    ''' <summary>
    ''' Returns the index of the first occurrance of the specified string.
    ''' </summary>
    ''' <param name="value">A System.String to seek.</param>
    Public Function IndexOf(ByVal value As String) As Integer
        Return Me.ToString().IndexOf(value)
    End Function

    ''' <summary>
    ''' Returns the index of the first occurrance of the specified string from the specified index.
    ''' </summary>
    ''' <param name="value">A System.String to seek.</param>
    ''' <param name="startIndex">The search starting position.</param>
    Public Function IndexOf(ByVal value As String, ByVal startIndex As Integer) As Integer
        Return Me.ToString().IndexOf(value, startIndex)
    End Function

    ''' <summary>
    ''' Measures the current instance and returns the size relative to the specified device context.
    ''' </summary>
    ''' <param name="dc">The device context in which to measure the text.</param>
    Public Function Measure(ByVal dc As IDeviceContext) As Size
        Return StyledString.Measure(Me, dc)
    End Function

    ''' <summary>
    ''' Measures the specified instance and returns the size relative to the specified device context.
    ''' </summary>
    ''' <param name="styledString">The StyledString to measure.</param>
    ''' <param name="dc">The device context in which to measure the text.</param>
    Public Shared Function Measure(ByVal styledString As StyledString, ByVal dc As IDeviceContext) As Size
        Dim result = Size.Empty
        Dim lineFeed = Convert.ToChar(&HA And &HFFFF)
        Dim carriageReturn = Convert.ToChar(&HD And &HFFFF)
        Dim capturedFirst = False
        For Each c In styledString.List
            Dim s = TextRenderer.MeasureText(dc, c.Character, c.Font, Size.Empty, styledString.DefaultFlags)
            If Not c.Character.Equals(lineFeed) AndAlso Not c.Character.Equals(carriageReturn) Then
                If s.Height > result.Height Then
                    result.Height = s.Height
                End If
                result.Width += s.Width
            Else
                If styledString.Count = 1 Then
                    If s.Height > result.Height Then
                        result.Height = s.Height
                    End If
                End If
            End If
        Next
        Return result
    End Function

    ''' <summary>
    ''' Gets a StyledString that represents the specified range of StyledChar objects.
    ''' </summary>
    ''' <param name="start">The starting index of the range.</param>
    ''' <param name="count">The amount of elements to retrieve.</param>
    Public Function Range(ByVal start As Integer, ByVal count As Integer) As StyledString
        Return StyledString.Range(Me, start, count)
    End Function

    ''' <summary>
    ''' Gets a StyledString that represents the specified range of StyledChar objects.
    ''' </summary>
    ''' <param name="start">The starting index of the range.</param>
    Public Function Range(ByVal start As Integer) As StyledString
        Return StyledString.Range(Me, start)
    End Function

    ''' <summary>
    ''' Gets a StyledString that represents the specified range of StyledChar objects.
    ''' </summary>
    ''' <param name="styledString">The StyledString to retrieve the range from.</param>
    ''' <param name="start">The starting index of the range.</param>
    Public Shared Function Range(ByVal styledString As StyledString, ByVal start As Integer) As StyledString
        Return New StyledString(styledString.List.Skip(start).Select(Function(c) DirectCast(c.Clone(), StyledChar)))
    End Function

    ''' <summary>
    ''' Gets a StyledString that represents the specified range of StyledChar objects.
    ''' </summary>
    ''' <param name="styledString">The StyledString to retrieve the range from.</param>
    ''' <param name="start">The starting index of the range.</param>
    ''' <param name="count">The amount of elements to retrieve.</param>
    Public Shared Function Range(ByVal styledString As StyledString, ByVal start As Integer, ByVal count As Integer) As StyledString
        Return New StyledString(styledString.List.Skip(start).Take(count) _
                                                 .Select(Function(c) DirectCast(c.Clone(), StyledChar)))
    End Function

    ''' <summary>
    ''' Returns the current instance split into ranges by the line feed character.
    ''' </summary>
    Public Function Split() As StyledString()
        Return Me.Split(Convert.ToChar(&HA And &HFFFF))
    End Function

    ''' <summary>
    ''' Returns the current instance split into ranges by the specified character.
    ''' </summary>
    ''' <param name="ch">The Unicode character to split the instance by.</param>
    Public Function Split(ByVal ch As Char) As StyledString()
        Return StyledString.Split(Me, ch)
    End Function

    ''' <summary>
    ''' Returns the specified StyledString split into ranges by the specified character.
    ''' </summary>
    ''' <param name="styledString">The StyledString to split.</param>
    ''' <param name="ch">The Unicode character to split the instance by.</param>
    Public Shared Function Split(ByVal styledString As StyledString, ByVal ch As Char) As StyledString()
        Dim strings = styledString.ToString().Split(ch)
        Dim ranges(strings.Length - 1) As StyledString
        Dim start = 0

        For i = 0 To strings.Length - 1
            Dim [end] = start + strings(i).Length
            ranges(i) = styledString.Range(start, strings(i).Length)
            start = [end] + 1
        Next

        Return ranges
    End Function

    ''' <summary>
    ''' Gets a string representation of the current instance.
    ''' </summary>
    Public Overrides Function ToString() As String
        Return New String(Me.List.Select(Function(c) c.Character).ToArray())
    End Function

    ''' <summary>
    ''' Releases all unmanaged resources and optionally releases managed resources.
    ''' </summary>
    ''' <param name="disposing">True to release both unmanaged and managed resources; false to release only unmanaged resources.</param>
    Protected Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                For Each c In Me.List
                    c.Dispose()
                Next
            End If
            If Me._font IsNot Nothing Then
                Me._font.Dispose()
            End If

            Me._list = Nothing
            Me._font = Nothing
        End If
        Me.disposedValue = True
    End Sub

End Class

''' <summary>
''' Represents a styled Unicode character. This class cannot be inherited.
''' </summary>
<DebuggerDisplay("{Character}")> _
<Serializable()> _
Public NotInheritable Class StyledChar
    Implements IDisposable
    Implements ICloneable

    '//operators
    ''' <summary>
    ''' Converts a StyledChar to a System.Char using a narrowing conversion.
    ''' </summary>
    ''' <param name="value">The StyledChar to convert.</param>
    Public Shared Narrowing Operator CType(ByVal value As StyledChar) As Char
        Return value.Character
    End Operator

    ''' <summary>
    ''' Converts a System.Char to a StyledChar using a widening conversion.
    ''' </summary>
    ''' <param name="value">The System.Char to convert.</param>
    Public Shared Widening Operator CType(ByVal value As Char) As StyledChar
        Return New StyledChar(value)
    End Operator

    '//fields
    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
    Private disposedValue As Boolean

    '//properties
    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
    Private _character As Char
    ''' <summary>
    ''' Gets or sets the character that represents the current instance.
    ''' </summary>
    Public Property Character() As Char
        Get
            Return Me._character
        End Get
        Set(ByVal value As Char)
            If Not Object.Equals(value, Me._character) Then
                Me._character = value
            End If
        End Set
    End Property

    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
    Private _font As Font
    ''' <summary>
    ''' Gets or sets the font that is used to style the current instance.
    ''' </summary>
    Public Property Font() As Font
        Get
            Return Me._font
        End Get
        Set(ByVal value As Font)
            If Not Object.Equals(value, Me._font) Then
                Me.Dispose()
                Me._font = value
            End If
        End Set
    End Property

    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
    Private _color As Color
    ''' <summary>
    ''' Gets or sets the color that is used to style the current instance.
    ''' </summary>
    Public Property Color() As Color
        Get
            Return Me._color
        End Get
        Set(ByVal value As Color)
            If Not Object.Equals(value, Me._color) Then
                Me._color = value
            End If
        End Set
    End Property

    '//constructors
    ''' <summary>
    ''' Initializes a new instance of the StyledChar class with the specified Unicode character.
    ''' </summary>
    ''' <param name="ch">The unicode character to represent this instance.</param>
    Public Sub New(ByVal ch As Char)
        Me._character = ch
        Me._color = StyledString.DefaultColor
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the StyledChar class with the specified Unicode character and specified color.
    ''' </summary>
    ''' <param name="ch">The unicode character to represent this instance.</param>
    ''' <param name="color">The color used to style the this instance.</param>
    Public Sub New(ByVal ch As Char, ByVal color As Color)
        Me.New(ch)
        Me._color = color
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the StyledChar class with the specified Unicode character and System.Drawing.Font.
    ''' </summary>
    ''' <param name="ch">The unicode character to represent this instance.</param>
    ''' <param name="font">The font used to style the this instance.</param>
    Public Sub New(ByVal ch As Char, ByVal font As Font)
        Me.New(ch)
        Me._font = font
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the StyledChar class with the specified parameters.
    ''' </summary>
    ''' <param name="ch">The unicode character to represent this instance.</param>
    ''' <param name="font">The font used to style the this instance.</param>
    ''' <param name="color">The color used to style the this instance.</param>
    Public Sub New(ByVal ch As Char, ByVal font As Font, ByVal color As Color)
        Me.New(ch, font)
        Me._color = color
    End Sub

    '//methods
    ''' <summary>
    ''' Releases all resources used by the current instance.
    ''' </summary>
    Public Sub Dispose() Implements IDisposable.Dispose
        Me.Dispose(True)
        System.GC.SuppressFinalize(Me)
    End Sub

    ''' <summary>
    ''' Creates and returns a copy of the current instance.
    ''' </summary>
    Public Function Clone() As Object Implements System.ICloneable.Clone
        Return New StyledChar(Me.Character, Me.Font, Me.Color)
    End Function

    ''' <summary>
    ''' Creates a StyledChar from the specified Unicode character.
    ''' </summary>
    ''' <param name="ch">A Unicode character.</param>
    Public Shared Function FromChar(ByVal ch As Char) As StyledChar
        Return New StyledChar(ch)
    End Function

    ''' <summary>
    ''' Creates a StyledChar from the specified System.String.
    ''' </summary>
    ''' <param name="value">A System.String to use.</param>
    Public Shared Function FromString(ByVal value As String) As StyledChar()
        Return value.Select(Function(c) StyledChar.FromChar(c)).ToArray()
    End Function

    ''' <summary>
    ''' Gets a string representation of the current instance.
    ''' </summary>
    Public Overrides Function ToString() As String
        Return Me.Character.ToString()
    End Function

    ''' <summary>
    ''' Releases all unmanaged resources and optionally releases managed resources.
    ''' </summary>
    ''' <param name="disposing">True to release both unmanaged and managed resources; false to release only unmanaged resources.</param>
    Protected Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                If Me._font IsNot Nothing Then
                    Me._font.Dispose()
                End If
                Me._font = Nothing
            End If
        End If
        Me.disposedValue = True
    End Sub

End Class