Option Strict On
Option Explicit On

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

Namespace Nexamas.UI.Components

    Partial Public NotInheritable Class MASMasterDetailView

#Region "Selection motion"

        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 Sub StartSelectionMotion(previousIndex As Integer,
                                         nextIndex As Integer)
            StopSelectionMotion()

            If previousIndex < 0 OrElse nextIndex < 0 OrElse previousIndex = nextIndex Then
                _hasSelectionMotion = False
                _selectionMotionProgress = 1.0F
                Return
            End If

            _selectionMotionFromIndex = previousIndex
            _selectionMotionToIndex = nextIndex
            _selectionMotionProgress = 0.0F
            _hasSelectionMotion = True

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

            _selectionMotionRunner = MASMotionSystem.CreateTimelineRunner(
                owner:=Me,
                consumerName:="MASMasterDetailView.Selection",
                plan:=plan,
                requestFrame:=AddressOf InvalidateVisual,
                applyFrame:=AddressOf ApplySelectionMotionFrame,
                completed:=AddressOf CompleteSelectionMotionFrame)
            _selectionMotionRunner.Start()
        End Sub

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

        Private Sub CompleteSelectionMotionFrame(frame As MASMotionFrame)
            _selectionMotionProgress = 1.0F
            _hasSelectionMotion = False
            _selectionMotionFromIndex = -1
            _selectionMotionToIndex = -1
            _selectionMotionRunner = Nothing
            InvalidateVisual()
        End Sub

        Private Sub StopSelectionMotion()
            If _selectionMotionRunner IsNot Nothing Then
                _selectionMotionRunner.Dispose()
                _selectionMotionRunner = Nothing
            End If

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

        Private Function IsSelectionMotionActive() As Boolean
            Return _hasSelectionMotion AndAlso _selectionMotionRunner IsNot Nothing AndAlso _selectionMotionRunner.IsRunning
        End Function

        Private Function ShouldSuppressSelectionFill(index As Integer) As Boolean
            Return IsSelectionMotionActive() AndAlso index = _selectionMotionToIndex
        End Function

        Private Sub DrawSelectionMotionOverlay(canvas As SKCanvas,
                                               dpi As Single,
                                               plan As MASMasterDetailViewLayoutPlan,
                                               accent As SKColor,
                                               isRtl As Boolean)
            If canvas Is Nothing OrElse plan Is Nothing OrElse Not IsSelectionMotionActive() Then Return

            Dim fromRect As SKRect = ResolveVisibleItemRect(_selectionMotionFromIndex)
            Dim toRect As SKRect = ResolveVisibleItemRect(_selectionMotionToIndex)
            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(_selectionMotionProgress)
            Dim rect As SKRect = LerpRect(fromRect, toRect, progress)
            rect = PixelSnap.SnapRect(rect, dpi)

            Using fill As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = accent.WithAlpha(MasterDetailViewTokens.ItemSelectedAlpha)}
                canvas.DrawRoundRect(rect, plan.ItemRadiusPx, plan.ItemRadiusPx, fill)
            End Using
            Using stroke As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Stroke, .StrokeWidth = Math.Max(1.0F, dpi), .Color = accent.WithAlpha(MasterDetailViewTokens.ItemSelectedStrokeAlpha)}
                canvas.DrawRoundRect(rect, plan.ItemRadiusPx, plan.ItemRadiusPx, stroke)
            End Using

            Dim marker As SKRect = If(isRtl,
                                      New SKRect(rect.Right - 4.0F * dpi, rect.Top + 10.0F * dpi, rect.Right - 2.0F * dpi, rect.Bottom - 10.0F * dpi),
                                      New SKRect(rect.Left + 2.0F * dpi, rect.Top + 10.0F * dpi, rect.Left + 4.0F * dpi, rect.Bottom - 10.0F * dpi))
            Using markerPaint As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = accent.WithAlpha(230)}
                canvas.DrawRoundRect(marker, 2.0F * dpi, 2.0F * dpi, markerPaint)
            End Using
        End Sub

        Private Function ResolveVisibleItemRect(index As Integer) As SKRect
            If index < 0 Then Return SKRect.Empty
            For Each hit As MasterDetailItemHit In _hits
                If hit IsNot Nothing AndAlso hit.Index = index Then Return hit.Rect
            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

#Region "Dispose"

        Protected Overrides Sub Dispose(disposing As Boolean)
            StopSelectionMotion()
            MyBase.Dispose(disposing)
        End Sub

#End Region

    End Class

End Namespace
