Option Strict On
Option Explicit On

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

Namespace Nexamas.UI.Controls

    Partial Public NotInheritable Class MASCommandPalette

#Region "Command palette selection motion"

        Private _paletteSelectionRunner As MASMotionTimelineRunner
        Private _paletteSelectionFromIndex As Integer = -1
        Private _paletteSelectionToIndex As Integer = -1
        Private _paletteSelectionProgress As Single = 1.0F
        Private _hasPaletteSelectionMotion As Boolean

        Private Sub StartCommandPaletteSelectionMotion(previousIndex As Integer,
                                                       nextIndex As Integer)
            StopCommandPaletteSelectionMotion(resetState:=False)

            If previousIndex < 0 OrElse nextIndex < 0 OrElse previousIndex = nextIndex Then
                _hasPaletteSelectionMotion = False
                _paletteSelectionProgress = 1.0F
                Return
            End If

            _paletteSelectionFromIndex = previousIndex
            _paletteSelectionToIndex = nextIndex
            _paletteSelectionProgress = 0.0F
            _hasPaletteSelectionMotion = True

            Dim plan As MASMotionTransitionPlan = MASMotionSystem.CreateTransitionPlan(MASMotionTokenId.RowSelectionMove, 0.0F, 1.0F)
            If Not plan.ShouldAnimate Then
                CompleteCommandPaletteSelectionMotionFrame(Nothing)
                Return
            End If

            _paletteSelectionRunner = MASMotionSystem.CreateTimelineRunner(
                owner:=Me,
                consumerName:="MASCommandPalette.Selection",
                plan:=plan,
                requestFrame:=AddressOf InvalidateVisual,
                applyFrame:=AddressOf ApplyCommandPaletteSelectionMotionFrame,
                completed:=AddressOf CompleteCommandPaletteSelectionMotionFrame)
            _paletteSelectionRunner.Start()

            InvalidateVisual()
        End Sub

        Private Sub ApplyCommandPaletteSelectionMotionFrame(frame As MASMotionFrame)
            If frame Is Nothing Then Return
            _paletteSelectionProgress = MASMotionSystem.ClampProgress(frame.Value)
            _hasPaletteSelectionMotion = True
            InvalidateVisual()
        End Sub

        Private Sub CompleteCommandPaletteSelectionMotionFrame(frame As MASMotionFrame)
            StopCommandPaletteSelectionMotion(resetState:=True)
            InvalidateVisual()
        End Sub

        Private Sub StopCommandPaletteSelectionMotion(resetState As Boolean)
            If _paletteSelectionRunner IsNot Nothing Then
                _paletteSelectionRunner.Dispose()
                _paletteSelectionRunner = Nothing
            End If

            If resetState Then
                _paletteSelectionFromIndex = -1
                _paletteSelectionToIndex = -1
                _paletteSelectionProgress = 1.0F
                _hasPaletteSelectionMotion = False
            End If
        End Sub

        Private Function IsCommandPaletteSelectionMotionActive() As Boolean
            Return _hasPaletteSelectionMotion AndAlso _paletteSelectionRunner IsNot Nothing AndAlso _paletteSelectionRunner.IsRunning
        End Function

        Private Function ShouldSuppressCommandPaletteSelectionFill(index As Integer) As Boolean
            Return IsCommandPaletteSelectionMotionActive() AndAlso index = _paletteSelectionToIndex
        End Function

        Private Sub DrawCommandPaletteSelectionMotionOverlay(canvas As SKCanvas,
                                                            ctx As MASThemeContext,
                                                            rows As PaletteRow(),
                                                            plan As MASCommandPaletteLayoutPlan)
            If canvas Is Nothing OrElse ctx Is Nothing OrElse plan Is Nothing OrElse rows Is Nothing Then Return
            If Not IsCommandPaletteSelectionMotionActive() Then Return

            Dim fromRect As SKRect = ResolveCommandPaletteMotionRowRect(rows, _paletteSelectionFromIndex)
            Dim toRect As SKRect = ResolveCommandPaletteMotionRowRect(rows, _paletteSelectionToIndex)
            If fromRect.Width <= 1.0F OrElse fromRect.Height <= 1.0F OrElse toRect.Width <= 1.0F OrElse toRect.Height <= 1.0F Then Return

            Dim progress As Single = MASMotionSystem.ClampProgress(_paletteSelectionProgress)
            Dim rect As SKRect = LerpRect(fromRect, toRect, progress)
            rect = PixelSnap.SnapRect(rect, Math.Max(0.64F, plan.Dpi))
            Dim radius As Single = Math.Max(4.0F * plan.Dpi, plan.RowRadiusPx)

            Using fill As New SKPaint With {
                .IsAntialias = True,
                .IsDither = True,
                .Style = SKPaintStyle.Fill,
                .Color = ResolveAccentColor(ctx).WithAlpha(CommandPaletteTokens.RowSelectedAlpha),
                .BlendMode = SKBlendMode.SrcOver
            }
                canvas.DrawRoundRect(rect, radius, radius, fill)
            End Using
        End Sub

        Private Shared Function ResolveCommandPaletteMotionRowRect(rows As PaletteRow(),
                                                                   index As Integer) As SKRect
            If rows Is Nothing OrElse index < 0 Then Return SKRect.Empty
            For i As Integer = 0 To rows.Length - 1
                Dim row As PaletteRow = rows(i)
                If row IsNot Nothing AndAlso row.Index = index Then Return row.BoundsPx
            Next
            Return SKRect.Empty
        End Function

        Private Shared Function LerpRect(fromRect As SKRect,
                                         toRect As SKRect,
                                         progress As Single) As SKRect
            Dim p As Single = MASMotionSystem.ClampProgress(progress)
            Return New SKRect(
                fromRect.Left + ((toRect.Left - fromRect.Left) * p),
                fromRect.Top + ((toRect.Top - fromRect.Top) * p),
                fromRect.Right + ((toRect.Right - fromRect.Right) * p),
                fromRect.Bottom + ((toRect.Bottom - fromRect.Bottom) * p))
        End Function

#End Region

    End Class

End Namespace
