Option Strict On
Option Explicit On

Imports System
Imports System.Diagnostics

Namespace Nexamas.UI.TextInput

    ' CONTRACT:
    ' MASTextInputEditor is the component responsible for text-edit operations.
    '
    ' Responsibilities:
    ' - Insert text
    ' - Delete text
    ' - Replace selected text
    ' - Apply edit operations through MASTextInputState public APIs
    '
    ' It must not:
    ' - Know UI, mouse, keyboard, rendering, layout, or Skia
    ' - Access clipboard directly
    ' - Trigger invalidation
    ' - Store its own text/caret/selection state
    Friend NotInheritable Class MASTextInputEditor

        Friend Function InsertTextAtCaret(state As MASTextInputState,
                                          value As String) As Boolean
            ValidateEditorPreconditions(state)

            If state Is Nothing Then Return False
            If state.ReadOnly Then Return False
            If String.IsNullOrEmpty(value) Then Return False

            Dim beforeText As String = state.Text

            Dim replaceStart As Integer = If(state.HasSelection, state.SelectionStart, state.CaretIndex)
            Dim replaceLength As Integer = If(state.HasSelection, state.SelectionLength, 0)

            Dim changed As Boolean = state.ReplaceRange(replaceStart, replaceLength, value)

            ValidateEditorPostconditions(beforeText, state, changed)

            Return changed
        End Function

        Friend Function BackspaceSelectionAware(state As MASTextInputState) As Boolean
            ValidateEditorPreconditions(state)

            If state Is Nothing Then Return False
            If state.ReadOnly Then Return False

            Dim beforeText As String = state.Text
            Dim changed As Boolean

            If state.HasSelection Then
                changed = state.DeleteSelection()
            ElseIf state.CaretIndex <= 0 Then
                changed = False
            Else
                changed = state.ReplaceRange(state.CaretIndex - 1, 1, String.Empty)
            End If

            ValidateEditorPostconditions(beforeText, state, changed)

            Return changed
        End Function

        Friend Function DeleteSelectionAware(state As MASTextInputState) As Boolean
            ValidateEditorPreconditions(state)

            If state Is Nothing Then Return False
            If state.ReadOnly Then Return False

            Dim beforeText As String = state.Text
            Dim changed As Boolean

            If state.HasSelection Then
                changed = state.DeleteSelection()
            ElseIf state.CaretIndex >= state.Text.Length Then
                changed = False
            Else
                changed = state.ReplaceRange(state.CaretIndex, 1, String.Empty)
            End If

            ValidateEditorPostconditions(beforeText, state, changed)

            Return changed
        End Function

        Friend Function BackspacePreviousWord(state As MASTextInputState) As Boolean
            ValidateEditorPreconditions(state)

            If state Is Nothing Then Return False
            If state.ReadOnly Then Return False

            Dim beforeText As String = state.Text
            Dim changed As Boolean

            If state.HasSelection Then
                changed = state.DeleteSelection()
            ElseIf state.CaretIndex <= 0 Then
                changed = False
            Else
                Dim target As Integer = MASTextWordBoundary.FindPrevious(state.Text, state.CaretIndex)
                If target < 0 Then target = 0

                If target >= state.CaretIndex Then
                    changed = False
                Else
                    changed = state.ReplaceRange(target, state.CaretIndex - target, String.Empty)
                End If
            End If

            ValidateEditorPostconditions(beforeText, state, changed)

            Return changed
        End Function

        Friend Function DeleteNextWord(state As MASTextInputState) As Boolean
            ValidateEditorPreconditions(state)

            If state Is Nothing Then Return False
            If state.ReadOnly Then Return False

            Dim beforeText As String = state.Text
            Dim changed As Boolean

            If state.HasSelection Then
                changed = state.DeleteSelection()
            ElseIf state.CaretIndex >= state.Text.Length Then
                changed = False
            Else
                Dim target As Integer = MASTextWordBoundary.FindNext(state.Text, state.CaretIndex)

                If target <= state.CaretIndex Then
                    changed = False
                Else
                    changed = state.ReplaceRange(state.CaretIndex, target - state.CaretIndex, String.Empty)
                End If
            End If

            ValidateEditorPostconditions(beforeText, state, changed)

            Return changed
        End Function

        Friend Function DeleteSelectionCore(state As MASTextInputState) As Boolean
            ValidateEditorPreconditions(state)

            If state Is Nothing Then Return False
            If state.ReadOnly Then Return False

            Dim beforeText As String = state.Text
            Dim changed As Boolean = state.DeleteSelection()

            ValidateEditorPostconditions(beforeText, state, changed)

            Return changed
        End Function

        <Conditional("DEBUG")>
        Private Shared Sub ValidateEditorPreconditions(state As MASTextInputState)
            Debug.Assert(state IsNot Nothing, "Editor contract failed: state is Nothing.")

            If state Is Nothing Then Return

            Debug.Assert(state.Text IsNot Nothing, "Editor contract failed: state.Text is Nothing.")
        End Sub

        <Conditional("DEBUG")>
        Private Shared Sub ValidateEditorPostconditions(beforeText As String,
                                                        state As MASTextInputState,
                                                        changed As Boolean)

            If state Is Nothing Then Return

            Dim afterText As String = state.Text
            Dim textChanged As Boolean =
                Not String.Equals(beforeText, afterText, StringComparison.Ordinal)

            Debug.Assert(Not textChanged OrElse changed,
                         "Editor contract failed: text changed but method returned False.")

            If state.MaxLength > 0 Then
                Debug.Assert(state.Text.Length <= state.MaxLength,
                             "Editor contract failed: text exceeds MaxLength.")
            End If

            Debug.Assert(state.CaretIndex >= 0 AndAlso state.CaretIndex <= state.Text.Length,
                         "Editor contract failed: invalid CaretIndex after edit.")

            Debug.Assert(state.SelectionStart >= 0 AndAlso state.SelectionStart <= state.Text.Length,
                         "Editor contract failed: invalid SelectionStart after edit.")

            Debug.Assert(state.SelectionLength >= 0,
                         "Editor contract failed: invalid SelectionLength after edit.")

            Debug.Assert(state.SelectionStart + state.SelectionLength <= state.Text.Length,
                         "Editor contract failed: selection range exceeds text length after edit.")
        End Sub

    End Class

End Namespace