Option Strict On
Option Explicit On

Imports System
Imports System.Diagnostics

Namespace Nexamas.UI.TextInput

    ' CONTRACT:
    ' MASTextLineRange represents a logical line range inside the full text.
    '
    ' StartIndex points to the first text character in the line.
    ' Length excludes the line-break characters.
    ' LineBreakLength is 0, 1, or 2 and belongs to this line for navigation purposes.
    '
    ' This type is immutable and must not know UI/rendering/layout.
    Friend NotInheritable Class MASTextLineRange

        Friend Sub New(lineIndex As Integer,
                       startIndex As Integer,
                       length As Integer,
                       lineBreakLength As Integer)

            If lineIndex < 0 Then Throw New ArgumentOutOfRangeException(NameOf(lineIndex))
            If startIndex < 0 Then Throw New ArgumentOutOfRangeException(NameOf(startIndex))
            If length < 0 Then Throw New ArgumentOutOfRangeException(NameOf(length))
            If lineBreakLength < 0 OrElse lineBreakLength > 2 Then Throw New ArgumentOutOfRangeException(NameOf(lineBreakLength))

            Me.LineIndex = lineIndex
            Me.StartIndex = startIndex
            Me.Length = length
            Me.LineBreakLength = lineBreakLength

            Validate()
        End Sub

        Friend ReadOnly Property LineIndex As Integer
        Friend ReadOnly Property StartIndex As Integer
        Friend ReadOnly Property Length As Integer
        Friend ReadOnly Property LineBreakLength As Integer

        Friend ReadOnly Property EndIndexExclusive As Integer
            Get
                Return StartIndex + Length
            End Get
        End Property

        Friend ReadOnly Property NextLineStartIndex As Integer
            Get
                Return StartIndex + Length + LineBreakLength
            End Get
        End Property

        Friend ReadOnly Property HasLineBreak As Boolean
            Get
                Return LineBreakLength > 0
            End Get
        End Property

        Friend Function ContainsTextIndex(index As Integer) As Boolean
            Return index >= StartIndex AndAlso index <= EndIndexExclusive
        End Function

        Friend Function ClampIndexToLine(index As Integer) As Integer
            If index < StartIndex Then Return StartIndex
            If index > EndIndexExclusive Then Return EndIndexExclusive
            Return index
        End Function

        Friend Function GetText(source As String) As String
            Dim text As String = If(source, String.Empty)
            If StartIndex >= text.Length Then Return String.Empty

            Dim safeLength As Integer = Math.Min(Length, text.Length - StartIndex)
            If safeLength <= 0 Then Return String.Empty

            Return text.Substring(StartIndex, safeLength)
        End Function

        <Conditional("DEBUG")>
        Private Sub Validate()
            Debug.Assert(LineIndex >= 0, "LineRange contract failed: LineIndex must be >= 0.")
            Debug.Assert(StartIndex >= 0, "LineRange contract failed: StartIndex must be >= 0.")
            Debug.Assert(Length >= 0, "LineRange contract failed: Length must be >= 0.")
            Debug.Assert(LineBreakLength >= 0 AndAlso LineBreakLength <= 2,
                         "LineRange contract failed: LineBreakLength must be 0, 1, or 2.")
        End Sub

    End Class

End Namespace
