Option Strict On
Option Explicit On

Imports System
Imports System.Globalization

Namespace Nexamas.UI.TextInput

    Friend NotInheritable Class MASTextWordBoundary

        Private Sub New()
        End Sub

        Private Enum CharKind
            WhiteSpace = 0
            LetterOrDigit = 1
            Mark = 2
            Symbol = 3
            Punctuation = 4
            Other = 5
        End Enum

        Friend Shared Function FindPrevious(text As String,
                                            index As Integer) As Integer

            Dim s As String = If(text, String.Empty)

            If index <= 0 Then Return 0
            If index > s.Length Then index = s.Length

            Dim i As Integer = index

            While i > 0 AndAlso IsWhite(s(i - 1))
                i -= 1
            End While

            If i <= 0 Then Return 0

            Dim targetKind As CharKind = GetKind(s(i - 1))

            While i > 0 AndAlso IsSameWordRun(GetKind(s(i - 1)), targetKind)
                i -= 1
            End While

            Return i
        End Function

        Friend Shared Function FindNext(text As String,
                                        index As Integer) As Integer

            Dim s As String = If(text, String.Empty)

            If index < 0 Then index = 0
            If index >= s.Length Then Return s.Length

            Dim i As Integer = index

            While i < s.Length AndAlso IsWhite(s(i))
                i += 1
            End While

            If i >= s.Length Then Return s.Length

            Dim targetKind As CharKind = GetKind(s(i))

            While i < s.Length AndAlso IsSameWordRun(GetKind(s(i)), targetKind)
                i += 1
            End While

            Return i
        End Function

        Friend Shared Function FindWordStart(text As String,
                                             index As Integer) As Integer

            Dim s As String = If(text, String.Empty)
            If s.Length = 0 Then Return 0

            If index < 0 Then index = 0
            If index >= s.Length Then index = s.Length - 1

            If IsWhite(s(index)) Then Return index

            Dim targetKind As CharKind = GetKind(s(index))
            Dim i As Integer = index

            While i > 0 AndAlso IsSameWordRun(GetKind(s(i - 1)), targetKind)
                i -= 1
            End While

            Return i
        End Function

        Friend Shared Function FindWordEnd(text As String,
                                           index As Integer) As Integer

            Dim s As String = If(text, String.Empty)
            If s.Length = 0 Then Return 0

            If index < 0 Then index = 0
            If index >= s.Length Then index = s.Length - 1

            If IsWhite(s(index)) Then Return index

            Dim targetKind As CharKind = GetKind(s(index))
            Dim i As Integer = index

            While i < s.Length AndAlso IsSameWordRun(GetKind(s(i)), targetKind)
                i += 1
            End While

            Return i
        End Function

        Private Shared Function IsWhite(ch As Char) As Boolean
            Return Char.IsWhiteSpace(ch)
        End Function

        Private Shared Function IsSameWordRun(current As CharKind,
                                              target As CharKind) As Boolean

            If target = CharKind.LetterOrDigit Then
                Return current = CharKind.LetterOrDigit OrElse current = CharKind.Mark
            End If

            If target = CharKind.Mark Then
                Return current = CharKind.LetterOrDigit OrElse current = CharKind.Mark
            End If

            Return current = target
        End Function

        Private Shared Function GetKind(ch As Char) As CharKind
            If Char.IsWhiteSpace(ch) Then Return CharKind.WhiteSpace
            If Char.IsLetterOrDigit(ch) Then Return CharKind.LetterOrDigit

            Dim cat As UnicodeCategory = CharUnicodeInfo.GetUnicodeCategory(ch)

            Select Case cat
                Case UnicodeCategory.NonSpacingMark,
                     UnicodeCategory.SpacingCombiningMark,
                     UnicodeCategory.EnclosingMark
                    Return CharKind.Mark

                Case UnicodeCategory.ConnectorPunctuation,
                     UnicodeCategory.DashPunctuation,
                     UnicodeCategory.OpenPunctuation,
                     UnicodeCategory.ClosePunctuation,
                     UnicodeCategory.InitialQuotePunctuation,
                     UnicodeCategory.FinalQuotePunctuation,
                     UnicodeCategory.OtherPunctuation
                    Return CharKind.Punctuation

                Case UnicodeCategory.MathSymbol,
                     UnicodeCategory.CurrencySymbol,
                     UnicodeCategory.ModifierSymbol,
                     UnicodeCategory.OtherSymbol
                    Return CharKind.Symbol

                Case Else
                    Return CharKind.Other
            End Select
        End Function

    End Class

End Namespace