Option Strict On
Option Explicit On

Imports System
Imports Nexamas.UI.Motion
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.Theming
Imports SkiaSharp
Imports Nexamas.UI.General
Imports Nexamas.UI.Values

Namespace Nexamas.UI.Controls

    Partial Public NotInheritable Class MASFormValidationSummary

#Region "Motion"

        Private ReadOnly _selectionMotionOwner As New Object()
        Private ReadOnly _mutationMotionOwner As New Object()
        Private _selectionMotionRunner As MASMotionTimelineRunner
        Private _selectionMotionFromIndex As Integer = -1
        Private _selectionMotionToIndex As Integer = -1
        Private _selectionMotionProgress As Single = 1.0F
        Private _hasSelectionMotion As Boolean

        Private _mutationMotionRunner As MASMotionTimelineRunner
        Private _mutationProgress As Single = 1.0F
        Private _hasMutationMotion As Boolean
        Private _disposedLocal As Boolean

        Private Sub StartFormValidationSummarySelectionMotion(fromIndex As Integer,
                                                              toIndex As Integer)
            If _disposedLocal Then Return
            If fromIndex < 0 OrElse toIndex < 0 OrElse fromIndex = toIndex Then
                StopFormValidationSummarySelectionMotion(resetSelection:=True)
                Return
            End If

            StopFormValidationSummarySelectionMotion(resetSelection:=False)
            _selectionMotionFromIndex = fromIndex
            _selectionMotionToIndex = toIndex
            _selectionMotionProgress = 0.0F
            _hasSelectionMotion = True

            Dim plan As MASMotionTransitionPlan = MASMotionSystem.CreateTransitionPlan(MASMotionTokenId.RowSelectionMove, 0.0F, 1.0F)
            _selectionMotionRunner = MASMotionSystem.CreateTimelineRunner(
                owner:=_selectionMotionOwner,
                consumerName:="MASFormValidationSummary.RowSelectionMove",
                plan:=plan,
                requestFrame:=AddressOf InvalidateVisual,
                applyFrame:=AddressOf ApplyFormValidationSummarySelectionMotionFrame,
                completed:=AddressOf CompleteFormValidationSummarySelectionMotionFrame)
            _selectionMotionRunner.Start()

            InvalidateVisual()
        End Sub

        Private Sub ApplyFormValidationSummarySelectionMotionFrame(frame As MASMotionFrame)
            If frame Is Nothing Then Return
            _selectionMotionProgress = MASMotionSystem.ClampProgress(frame.EasedProgress)
            _hasSelectionMotion = True
            InvalidateVisual()
        End Sub

        Private Sub CompleteFormValidationSummarySelectionMotionFrame(frame As MASMotionFrame)
            StopFormValidationSummarySelectionMotion(resetSelection:=True)
            InvalidateVisual()
        End Sub

        Private Sub StopFormValidationSummarySelectionMotion(resetSelection As Boolean)
            If _selectionMotionRunner IsNot Nothing Then
                _selectionMotionRunner.Dispose()
                _selectionMotionRunner = Nothing
            End If

            If resetSelection Then
                _selectionMotionFromIndex = -1
                _selectionMotionToIndex = -1
                _selectionMotionProgress = 1.0F
                _hasSelectionMotion = False
            End If
        End Sub

        Private Function HasActiveFormValidationSummarySelectionMotion() As Boolean
            Return _hasSelectionMotion AndAlso _selectionMotionFromIndex >= 0 AndAlso _selectionMotionToIndex >= 0
        End Function

        Private Sub StartFormValidationSummaryMutationMotion()
            If _disposedLocal Then Return

            StopFormValidationSummaryMutationMotion(resetMutation:=False)
            _mutationProgress = 0.0F
            _hasMutationMotion = True

            Dim plan As MASMotionTransitionPlan = MASMotionSystem.CreateTransitionPlan(MASMotionTokenId.ValidationReveal, 0.0F, 1.0F)
            _mutationMotionRunner = MASMotionSystem.CreateTimelineRunner(
                owner:=_mutationMotionOwner,
                consumerName:="MASFormValidationSummary.ValidationReveal",
                plan:=plan,
                requestFrame:=AddressOf InvalidateVisual,
                applyFrame:=AddressOf ApplyFormValidationSummaryMutationMotionFrame,
                completed:=AddressOf CompleteFormValidationSummaryMutationMotionFrame)
            _mutationMotionRunner.Start()

            InvalidateVisual()
        End Sub

        Private Sub ApplyFormValidationSummaryMutationMotionFrame(frame As MASMotionFrame)
            If frame Is Nothing Then Return
            _mutationProgress = MASMotionSystem.ClampProgress(frame.EasedProgress)
            _hasMutationMotion = True
            InvalidateVisual()
        End Sub

        Private Sub CompleteFormValidationSummaryMutationMotionFrame(frame As MASMotionFrame)
            StopFormValidationSummaryMutationMotion(resetMutation:=True)
            InvalidateVisual()
        End Sub

        Private Sub StopFormValidationSummaryMutationMotion(resetMutation As Boolean)
            If _mutationMotionRunner IsNot Nothing Then
                _mutationMotionRunner.Dispose()
                _mutationMotionRunner = Nothing
            End If

            If resetMutation Then
                _mutationProgress = 1.0F
                _hasMutationMotion = False
            End If
        End Sub

        Private Function ResolveFormValidationSummaryMutationRowRect(rect As SKRect,
                                                                      dpi As Single,
                                                                      visibleRowIndex As Integer) As SKRect
            If Not _hasMutationMotion Then Return rect
            If rect.Width <= 1.0F OrElse rect.Height <= 1.0F Then Return rect

            Dim progress As Single = MASMotionSystem.ClampProgress(_mutationProgress)
            Dim safeDpi As Single = PixelSnap.SafeDpi(dpi)
            Dim delay As Single = Math.Min(0.28F, Math.Max(0.0F, CSng(visibleRowIndex) * 0.04F))
            Dim localProgress As Single = MASMotionSystem.ClampProgress(Math.Max(0.0F, (progress - delay) / Math.Max(0.01F, 1.0F - delay)))
            Dim offsetY As Single = (1.0F - localProgress) * 3.0F * safeDpi
            Return New SKRect(rect.Left, rect.Top + offsetY, rect.Right, rect.Bottom + offsetY)
        End Function

        Private Sub DrawFormValidationSummarySelectionMotionOverlay(canvas As SKCanvas,
                                                                     ctx As MASThemeContext,
                                                                     messagesRect As SKRect,
                                                                     dpi As Single,
                                                                     isRtl As Boolean,
                                                                     visibleCapacity As Integer)
            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If Not HasActiveFormValidationSummarySelectionMotion() Then Return
            If _selectionMotionToIndex < 0 OrElse _selectionMotionToIndex >= _messages.Count Then Return

            Dim fromRect As SKRect
            Dim toRect As SKRect
            Dim hasFrom As Boolean = TryResolveFormValidationSummaryMessageRect(messagesRect, dpi, _selectionMotionFromIndex, visibleCapacity, fromRect)
            Dim hasTo As Boolean = TryResolveFormValidationSummaryMessageRect(messagesRect, dpi, _selectionMotionToIndex, visibleCapacity, toRect)
            If Not hasFrom AndAlso Not hasTo Then Return
            If Not hasFrom Then fromRect = toRect
            If Not hasTo Then toRect = fromRect

            Dim progress As Single = MASMotionSystem.ClampProgress(_selectionMotionProgress)
            Dim rect As SKRect = PixelSnap.SnapRect(LerpRect(fromRect, toRect, progress), dpi)
            If rect.Width <= 1.0F OrElse rect.Height <= 1.0F Then Return

            Dim item As SummaryMessageState = _messages(_selectionMotionToIndex)
            Dim tone As SKColor = MASFormValidationSummaryPolicy.ResolveStateColor(ctx, item.State)
            Using fill As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = tone.WithAlpha(28)}
                canvas.DrawRoundRect(rect, FormValidationSummaryTokens.RowRadiusDip * dpi, FormValidationSummaryTokens.RowRadiusDip * dpi, fill)
            End Using
            Using stroke As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Stroke, .StrokeWidth = Math.Max(1.0F, 1.5F * dpi), .Color = tone.WithAlpha(150)}
                canvas.DrawRoundRect(rect, FormValidationSummaryTokens.RowRadiusDip * dpi, FormValidationSummaryTokens.RowRadiusDip * dpi, stroke)
            End Using
        End Sub

        Private Function TryResolveFormValidationSummaryMessageRect(messagesRect As SKRect,
                                                                     dpi As Single,
                                                                     messageIndex As Integer,
                                                                     visibleCapacity As Integer,
                                                                     ByRef rect As SKRect) As Boolean
            rect = SKRect.Empty
            If messageIndex < _topMessageIndex Then Return False
            Dim visibleIndex As Integer = messageIndex - _topMessageIndex
            If visibleIndex < 0 OrElse visibleIndex >= visibleCapacity Then Return False

            Dim rowHeight As Single = FormValidationSummaryTokens.RowHeightDip * dpi
            Dim rowGap As Single = FormValidationSummaryTokens.RowGapDip * dpi
            Dim rowTop As Single = messagesRect.Top + CSng(visibleIndex) * (rowHeight + rowGap)
            rect = PixelSnap.SnapRect(New SKRect(messagesRect.Left, rowTop, messagesRect.Right, rowTop + rowHeight), dpi)
            Return rect.Width > 1.0F AndAlso rect.Height > 1.0F
        End Function

        Private Shared Function LerpRect(fromRect As SKRect,
                                         toRect As SKRect,
                                         progress As Single) As SKRect
            Dim t As Single = Math.Max(0.0F, Math.Min(1.0F, progress))
            Return New SKRect(
                fromRect.Left + (toRect.Left - fromRect.Left) * t,
                fromRect.Top + (toRect.Top - fromRect.Top) * t,
                fromRect.Right + (toRect.Right - fromRect.Right) * t,
                fromRect.Bottom + (toRect.Bottom - fromRect.Bottom) * t)
        End Function

#End Region

#Region "Dispose"

        Protected Overrides Sub Dispose(disposing As Boolean)
            If _disposedLocal Then Return
            _disposedLocal = True

            If disposing Then
                Try
                    StopFormValidationSummarySelectionMotion(resetSelection:=True)
                    StopFormValidationSummaryMutationMotion(resetMutation:=True)
                    _primitives.Dispose()
                Catch masCaughtException1 As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(masCaughtException1)
                End Try
            End If

            MyBase.Dispose(disposing)
        End Sub

#End Region

    End Class

End Namespace
