Option Strict On
Option Explicit On

Imports System
Imports Nexamas.UI.RuntimeEnvironment
Imports Nexamas.UI.Performance
Imports Nexamas.UI.TextInput
Imports SkiaSharp

Namespace Nexamas.UI.TextInput

    ''' <summary>
    ''' Owns MAS text-input layout snapshot caching for MASInputBoxSkiaBase.
    ''' </summary>
    ''' <remarks>
    ''' This collaborator keeps cache invalidation/versioning, paint signatures, multiline
    ''' paragraph layout, and caret-layout synchronization out of the public input control
    ''' base class. The control remains responsible for public API and events; this class
    ''' is responsible only for producing a current, reusable layout snapshot.
    ''' </remarks>
    Friend NotInheritable Class MASTextInputLayoutCache

#Region "Fields"

        Private ReadOnly _snapshotBuilder As New MASTextInputLayoutSnapshotBuilder()

        Private _snapshot As MASTextInputLayoutSnapshot
        Private _dirty As Boolean = True
        Private _layoutVersion As Integer
        Private _themeVersion As Integer

#End Region

#Region "Invalidation"

        Friend Sub InvalidateLayout()
            _dirty = True
            _layoutVersion += 1
            MASPerformanceSystem.RecordCacheInvalidated(MASPerformanceCacheKind.TextInputLayout)
        End Sub

        Friend Sub InvalidateTheme()
            _dirty = True
            _themeVersion += 1
            MASPerformanceSystem.RecordCacheInvalidated(MASPerformanceCacheKind.TextInputLayout)
        End Sub

#End Region

#Region "Snapshot building"

        Friend Function GetOrBuild(state As MASTextInputState,
                                   layout As MASTextInputLayoutEngine,
                                   paint As SKPaint,
                                   contentR As SKRect,
                                   scrollOffsetPx As Single,
                                   multiline As Boolean,
                                   multilineWordWrap As Boolean,
                                   multilineVerticalScrollOffsetPx As Single,
                                   crispRendering As Boolean,
                                   environmentStamp As MASRuntimeEnvironmentStamp) As MASTextInputLayoutSnapshot

            If state Is Nothing OrElse layout Is Nothing OrElse paint Is Nothing Then
                Return New MASTextInputLayoutSnapshot()
            End If

            Dim safeEnvironmentStamp As MASRuntimeEnvironmentStamp = If(environmentStamp, MASRuntimeEnvironmentStamp.Empty)
            Dim paintSignature As String = MASTextInputLayoutSnapshotBuilder.CreatePaintSignature(paint)

            If CanReuseSnapshot(state, paintSignature, contentR, scrollOffsetPx, multilineVerticalScrollOffsetPx, safeEnvironmentStamp) Then
                MASPerformanceSystem.RecordCacheHit(MASPerformanceCacheKind.TextInputLayout)
                Return _snapshot
            End If

            MASPerformanceSystem.RecordCacheMiss(MASPerformanceCacheKind.TextInputLayout)
            _snapshot =
                _snapshotBuilder.Build(
                    state:=state,
                    layout:=layout,
                    paint:=paint,
                    contentR:=contentR,
                    scrollOffsetPx:=scrollOffsetPx,
                    crispRendering:=crispRendering
                )

            _snapshot.CrispRendering = crispRendering

            If multiline Then
                _snapshot.ParagraphLayout =
                    layout.BuildParagraphLayout(
                        contentR:=contentR,
                        paint:=paint,
                        displayText:=If(state.DisplayText, String.Empty),
                        wordWrap:=multilineWordWrap,
                        verticalScrollOffsetPx:=multilineVerticalScrollOffsetPx,
                        horizontalScrollOffsetPx:=If(multilineWordWrap, 0.0F, scrollOffsetPx),
                        crispRendering:=crispRendering,
                        caretIndex:=_snapshot.CaretIndex
                    )

                _snapshot.VerticalScrollOffsetPx = multilineVerticalScrollOffsetPx

                If _snapshot.ParagraphLayout IsNot Nothing Then
                    Dim caretPoint As SKPoint =
                        _snapshot.ParagraphLayout.GetCaretPoint(_snapshot.CaretIndex)

                    _snapshot.CaretX = caretPoint.X
                End If
            Else
                _snapshot.VerticalScrollOffsetPx = 0.0F
            End If

            _snapshot.LayoutVersion = _layoutVersion
            _snapshot.ThemeVersion = _themeVersion
            _snapshot.EnvironmentStamp = safeEnvironmentStamp

            _dirty = False

            Return _snapshot
        End Function

        Private Function CanReuseSnapshot(state As MASTextInputState,
                                          paintSignature As String,
                                          contentR As SKRect,
                                          scrollOffsetPx As Single,
                                          multilineVerticalScrollOffsetPx As Single,
                                          environmentStamp As MASRuntimeEnvironmentStamp) As Boolean

            If _snapshot Is Nothing OrElse _dirty Then Return False

            Return _snapshot.TextVersion = state.Version AndAlso
                   _snapshot.LayoutVersion = _layoutVersion AndAlso
                   _snapshot.ThemeVersion = _themeVersion AndAlso
                   If(_snapshot.EnvironmentStamp, MASRuntimeEnvironmentStamp.Empty).SameAs(environmentStamp) AndAlso
                   String.Equals(_snapshot.PaintSignature, paintSignature, StringComparison.Ordinal) AndAlso
                   _snapshot.ContentRect.Equals(contentR) AndAlso
                   Math.Abs(_snapshot.ScrollOffsetPx - scrollOffsetPx) < 0.01F AndAlso
                   Math.Abs(_snapshot.VerticalScrollOffsetPx - multilineVerticalScrollOffsetPx) < 0.01F AndAlso
                   _snapshot.CaretIndex = state.CaretIndex AndAlso
                   _snapshot.SelectionStart = state.SelectionStart AndAlso
                   _snapshot.SelectionLength = state.SelectionLength
        End Function

#End Region

    End Class

End Namespace
