Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.Diagnostics

Namespace Nexamas.UI.TextInput

    ' CONTRACT:
    ' MASTextLineBreakScanner converts raw text into logical line ranges.
    '
    ' It handles:
    ' - LF
    ' - CR
    ' - CRLF
    '
    ' It must not perform wrapping. Wrapping belongs to Stage 5B layout.
    Friend NotInheritable Class MASTextLineBreakScanner

        Friend Function Scan(value As String) As IReadOnlyList(Of MASTextLineRange)
            Dim text As String = If(value, String.Empty)
            Dim lines As New List(Of MASTextLineRange)()

            Dim lineStart As Integer = 0
            Dim lineIndex As Integer = 0
            Dim i As Integer = 0

            While i < text.Length
                Dim ch As Char = text.Chars(i)

                If ch = ControlChars.Cr OrElse ch = ControlChars.Lf Then
                    Dim breakLength As Integer = 1

                    If ch = ControlChars.Cr AndAlso i + 1 < text.Length AndAlso text.Chars(i + 1) = ControlChars.Lf Then
                        breakLength = 2
                    End If

                    lines.Add(New MASTextLineRange(
                        lineIndex:=lineIndex,
                        startIndex:=lineStart,
                        length:=i - lineStart,
                        lineBreakLength:=breakLength))

                    i += breakLength
                    lineStart = i
                    lineIndex += 1
                Else
                    i += 1
                End If
            End While

            lines.Add(New MASTextLineRange(
                lineIndex:=lineIndex,
                startIndex:=lineStart,
                length:=text.Length - lineStart,
                lineBreakLength:=0))

            Validate(lines, text.Length)
            Return lines
        End Function

        Friend Shared Function NormalizeLineEndings(value As String) As String
            Dim text As String = If(value, String.Empty)
            If text.Length = 0 Then Return String.Empty

            text = text.Replace(vbCrLf, vbLf)
            text = text.Replace(vbCr, vbLf)
            Return text
        End Function

        <Conditional("DEBUG")>
        Private Shared Sub Validate(lines As IReadOnlyList(Of MASTextLineRange), textLength As Integer)
            Debug.Assert(lines IsNot Nothing, "LineBreakScanner contract failed: lines is Nothing.")
            If lines Is Nothing Then Return

            Debug.Assert(lines.Count > 0, "LineBreakScanner contract failed: at least one logical line is required.")

            Dim expectedStart As Integer = 0

            For i As Integer = 0 To lines.Count - 1
                Dim line As MASTextLineRange = lines(i)
                Debug.Assert(line IsNot Nothing, "LineBreakScanner contract failed: line is Nothing.")
                If line Is Nothing Then Continue For

                Debug.Assert(line.LineIndex = i, "LineBreakScanner contract failed: line index mismatch.")
                Debug.Assert(line.StartIndex = expectedStart, "LineBreakScanner contract failed: non-contiguous line ranges.")
                Debug.Assert(line.EndIndexExclusive <= textLength, "LineBreakScanner contract failed: line exceeds text length.")

                expectedStart = line.NextLineStartIndex
            Next

            Debug.Assert(expectedStart = textLength,
                         "LineBreakScanner contract failed: final range does not end at text length.")
        End Sub

    End Class

End Namespace
