Option Strict On
Option Explicit On

Namespace Nexamas.UI.TextInput

    Friend NotInheritable Class MASTextInputNavigator

#Region "Caret movement"

        Friend Function MoveCaretHorizontal(state As MASTextInputState,
                                            delta As Integer,
                                            extendSelection As Boolean) As Boolean

            If state Is Nothing Then Return False
            Return MoveCaretTo(state, state.CaretIndex + delta, extendSelection)
        End Function

        Friend Function MoveCaretTo(state As MASTextInputState,
                                    target As Integer,
                                    extendSelection As Boolean) As Boolean

            If state Is Nothing Then Return False

            Dim beforeVersion As Integer = state.Version
            Dim textLen As Integer = state.Text.Length

            If target < 0 Then target = 0
            If target > textLen Then target = textLen

            If extendSelection Then
                Dim anchor As Integer = ResolveAnchor(state, target)
                state.ApplySelectionFromAnchor(anchor, target)
            Else
                state.MoveCaretWithoutSelection(target)
            End If

            Return beforeVersion <> state.Version
        End Function

#End Region

#Region "Word movement"

        Friend Function MoveCaretToPreviousWord(state As MASTextInputState,
                                                extendSelection As Boolean) As Boolean

            If state Is Nothing Then Return False

            Dim target As Integer = MASTextWordBoundary.FindPrevious(state.Text, state.CaretIndex)
            Return MoveCaretTo(state, target, extendSelection)
        End Function

        Friend Function MoveCaretToNextWord(state As MASTextInputState,
                                            extendSelection As Boolean) As Boolean

            If state Is Nothing Then Return False

            Dim target As Integer = MASTextWordBoundary.FindNext(state.Text, state.CaretIndex)
            Return MoveCaretTo(state, target, extendSelection)
        End Function

#End Region

#Region "Selection"

        Friend Function ExtendSelectionTo(state As MASTextInputState,
                                          index As Integer) As Boolean

            If state Is Nothing Then Return False

            Dim beforeVersion As Integer = state.Version
            Dim target As Integer = index
            If target < 0 Then target = 0
            If target > state.Text.Length Then target = state.Text.Length

            Dim anchor As Integer = If(state.SelectionAnchor >= 0, state.SelectionAnchor, state.CaretIndex)
            state.ApplySelectionFromAnchor(anchor, target)

            Return beforeVersion <> state.Version
        End Function

        Friend Function SelectWordAt(state As MASTextInputState,
                                     index As Integer) As Boolean

            If state Is Nothing Then Return False

            Dim beforeVersion As Integer = state.Version
            Dim text As String = state.Text

            If String.IsNullOrEmpty(text) Then
                state.MoveCaretWithoutSelection(0)
                Return beforeVersion <> state.Version
            End If

            If index < 0 Then index = 0
            If index > text.Length Then index = text.Length

            If index = text.Length AndAlso index > 0 Then
                index -= 1
            End If

            If index < 0 OrElse index >= text.Length Then
                state.MoveCaretWithoutSelection(text.Length)
                Return beforeVersion <> state.Version
            End If

            If Char.IsWhiteSpace(text(index)) Then
                state.MoveCaretWithoutSelection(index)
                Return beforeVersion <> state.Version
            End If

            Dim startIdx As Integer = MASTextWordBoundary.FindWordStart(text, index)
            Dim endIdx As Integer = MASTextWordBoundary.FindWordEnd(text, index)

            If endIdx <= startIdx Then
                state.MoveCaretWithoutSelection(index)
                Return beforeVersion <> state.Version
            End If

            state.SetCaretAndSelection(
                caretIndex:=endIdx,
                selectionStart:=startIdx,
                selectionLength:=endIdx - startIdx,
                selectionAnchor:=startIdx
            )

            Return beforeVersion <> state.Version
        End Function

#End Region

#Region "Helpers"

        Private Shared Function ResolveAnchor(state As MASTextInputState,
                                              target As Integer) As Integer

            If state.SelectionAnchor >= 0 Then Return state.SelectionAnchor

            If state.SelectionLength > 0 Then
                If target < state.CaretIndex Then
                    Return state.SelectionStart + state.SelectionLength
                End If

                Return state.SelectionStart
            End If

            Return state.CaretIndex
        End Function

#End Region

    End Class

End Namespace
