﻿''' <summary>
''' Displays a formatted serial code.
''' </summary>
''' <remarks>
''' The formatting consists of dashes between groups of characters.  The length of each group is determined by the <see cref="SerialCodeTextBox.GroupLength">GroupLength</see> property.
''' </remarks>
<ToolboxBitmap(GetType(TextBox))> _
Public Class SerialCodeTextBox
    Inherits TextBox

    ''' <summary>
    ''' The length of each group of characters.
    ''' </summary>
    Private _groupLength As Integer = 5


    ''' <summary>
    ''' Gets or sets the length of each group of characters.
    ''' </summary>
    ''' <value>
    ''' The length of each character group between successive dashes.  the default is 5.
    ''' </value>
    Public Property GroupLength() As Integer
        Get
            Return Me._groupLength
        End Get
        Set(ByVal value As Integer)
            Me._groupLength = value
        End Set
    End Property


    Protected Overrides Sub OnKeyDown(ByVal e As System.Windows.Forms.KeyEventArgs)
        Dim selectionStart As Integer = Me.SelectionStart

        Select Case e.KeyData
            Case Keys.Back
                If selectionStart > 0 AndAlso Me.Text(selectionStart - 1) = "-"c Then
                    'The user is backspacing and the character to the left of the caret is a dash.
                    'Shift the caret one place to the left so that the next character is the one deleted.
                    Me.SelectionStart -= 1
                End If
            Case Keys.Delete
                If selectionStart < Me.TextLength AndAlso Me.Text(selectionStart) = "-"c Then
                    'The user is deleting and the character to the right of the caret is a dash.
                    'Shift the caret one place to the right so that the next character is the one deleted.
                    Me.SelectionStart += 1
                End If
        End Select

        MyBase.OnKeyDown(e)
    End Sub

    Protected Overrides Sub OnKeyPress(ByVal e As System.Windows.Forms.KeyPressEventArgs)
        Dim isLetterOrDigit As Boolean = Char.IsLetterOrDigit(e.KeyChar)

        'Accept only letters, digits and control characters.
        If isLetterOrDigit OrElse Char.IsControl(e.KeyChar) Then
            If isLetterOrDigit AndAlso _
               SelectionStart < Me.TextLength AndAlso _
               Me.Text(SelectionStart) = "-"c Then
                'The user is entering a character and the character to the right of the caret is a dash.
                'Shift the caret one place to the right so that the caret is in the correct position afterwards..
                Me.SelectionStart += 1
            End If

            MyBase.OnKeyPress(e)
        Else
            e.Handled = True
        End If
    End Sub

    Protected Overrides Sub OnTextChanged(ByVal e As System.EventArgs)
        Dim text As String = Me.Text

        'Put dashes in all and only the right places.
        text = Me.FormatText(text)

        If text = Me.Text Then
            'The displayed text is already correct so raise the event.
            MyBase.OnTextChanged(e)
        Else
            'Get the current caret position.
            Dim selectionStart As Integer = Me.SelectionStart
            Dim isCaretAtEnd As Boolean = (selectionStart = Me.TextLength)

            'Replace the displayed text with the correctly formatted text.
            Me.Text = text

            'Reposition the caret.
            Me.SelectionStart = If(isCaretAtEnd, Me.TextLength, selectionStart)
        End If
    End Sub

    ''' <summary>
    ''' Inserts dashes into a string to break the text into groups.
    ''' </summary>
    ''' <param name="text">
    ''' The string to format.
    ''' </param>
    ''' <returns>
    ''' The formatted text.
    ''' </returns>
    ''' <remarks>
    ''' The length of each group in the formatted text is determined by the <see cref="GroupLength" /> property.
    ''' </remarks>
    Private Function FormatText(ByVal text As String) As String
        'Remove existing dashes in case any are in the wrong place.
        text = text.Replace("-", String.Empty)

        Dim groups As New List(Of String)
        Dim startIndex = 0

        'Break the text into groups.
        Do While startIndex < text.Length
            groups.Add(text.Substring(startIndex, _
                                      Math.Min(Me._groupLength, _
                                               text.Length - startIndex)))
            startIndex += Me._groupLength
        Loop

        'Rejoin the groups with dashes between them.
        Return String.Join("-", groups.ToArray())
    End Function

End Class
