Option Strict On
Option Explicit On

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

Namespace Nexamas.UI.Components

    Partial Public NotInheritable Class MASDateRangePicker

#Region "Range motion"

        Private _rangeMotionRunner As MASMotionTimelineRunner
        Private _rangeMotionProgress As Single = 1.0F
        Private _hasRangeMotion As Boolean
        Private _rangeMotionHadCompleteRange As Boolean
        Private _rangeMotionHasCompleteRange As Boolean

        Private Sub StartDateRangeMotion(previousStart As Nullable(Of Date),
                                         previousEnd As Nullable(Of Date),
                                         nextStart As Nullable(Of Date),
                                         nextEnd As Nullable(Of Date))
            StopDateRangeMotion(resetState:=False)

            _rangeMotionHadCompleteRange = previousStart.HasValue AndAlso previousEnd.HasValue
            _rangeMotionHasCompleteRange = nextStart.HasValue AndAlso nextEnd.HasValue
            _rangeMotionProgress = 0.0F
            _hasRangeMotion = True

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

            _rangeMotionRunner = MASMotionSystem.CreateTimelineRunner(
                owner:=Me,
                consumerName:="MASDateRangePicker.RangeFeedback",
                plan:=plan,
                requestFrame:=AddressOf InvalidateVisual,
                applyFrame:=AddressOf ApplyDateRangeMotionFrame,
                completed:=AddressOf CompleteDateRangeMotionFrame)
            _rangeMotionRunner.Start()
        End Sub

        Private Sub ApplyDateRangeMotionFrame(frame As MASMotionFrame)
            If frame Is Nothing Then Return
            _rangeMotionProgress = MASMotionSystem.ClampProgress(frame.Value)
            _hasRangeMotion = True
            InvalidateVisual()
        End Sub

        Private Sub CompleteDateRangeMotionFrame(frame As MASMotionFrame)
            StopDateRangeMotion(resetState:=True)
            InvalidateVisual()
        End Sub

        Private Sub StopDateRangeMotion(resetState As Boolean)
            If _rangeMotionRunner IsNot Nothing Then
                _rangeMotionRunner.Dispose()
                _rangeMotionRunner = Nothing
            End If

            If resetState Then
                _rangeMotionProgress = 1.0F
                _hasRangeMotion = False
                _rangeMotionHadCompleteRange = False
                _rangeMotionHasCompleteRange = False
            End If
        End Sub

        Private Sub DrawDateRangeMotionOverlay(canvas As SKCanvas,
                                               ctx As MASThemeContext,
                                               pixelBounds As SKRect,
                                               dpi As Single)
            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If Not _hasRangeMotion Then Return
            If pixelBounds.Width <= 1.0F OrElse pixelBounds.Height <= 1.0F Then Return

            Dim progress As Single = MASMotionSystem.ClampProgress(_rangeMotionProgress)
            Dim alpha As Byte = RangeMotionAlpha(130, progress)
            If alpha = 0 Then Return

            Dim startRect As SKRect = LogicalToPixelRect(_startPicker.BoundsLogical, dpi)
            Dim endRect As SKRect = LogicalToPixelRect(_endPicker.BoundsLogical, dpi)
            If startRect.Width <= 1.0F OrElse startRect.Height <= 1.0F OrElse endRect.Width <= 1.0F OrElse endRect.Height <= 1.0F Then Return

            Dim accent As SKColor = ResolveDateRangeMotionAccent(ctx)
            Dim strokeWidth As Single = Math.Max(1.0F, 1.6F * dpi)
            Dim radius As Single = Math.Max(6.0F * dpi, Math.Min(startRect.Height, endRect.Height) * 0.22F)

            Using paint As New SKPaint With {
                .IsAntialias = True,
                .Style = SKPaintStyle.Stroke,
                .StrokeWidth = strokeWidth,
                .StrokeCap = SKStrokeCap.Round,
                .StrokeJoin = SKStrokeJoin.Round,
                .Color = accent.WithAlpha(alpha),
                .BlendMode = SKBlendMode.SrcOver
            }
                Dim startOutline As SKRect = InsetRangeMotionRect(startRect, 1.0F * dpi)
                Dim endOutline As SKRect = InsetRangeMotionRect(endRect, 1.0F * dpi)
                canvas.DrawRoundRect(startOutline, radius, radius, paint)
                canvas.DrawRoundRect(endOutline, radius, radius, paint)

                If _rangeMotionHasCompleteRange OrElse _rangeMotionHadCompleteRange Then
                    Dim y1 As Single = startOutline.MidY
                    Dim y2 As Single = endOutline.MidY
                    Dim x1 As Single = startOutline.Right
                    Dim x2 As Single = endOutline.Left
                    If Math.Abs(y2 - y1) < 2.0F * dpi AndAlso x2 > x1 Then
                        canvas.DrawLine(x1 + (4.0F * dpi), y1, x2 - (4.0F * dpi), y2, paint)
                    Else
                        Dim centerX As Single = pixelBounds.MidX
                        canvas.DrawLine(centerX, startOutline.Bottom + (2.0F * dpi), centerX, endOutline.Top - (2.0F * dpi), paint)
                    End If
                End If
            End Using
        End Sub

        Private Shared Function LogicalToPixelRect(rect As SKRect,
                                                   dpi As Single) As SKRect
            If rect.IsEmpty Then Return SKRect.Empty
            Return PixelSnap.SnapRectHalf(New SKRect(rect.Left * dpi,
                                                     rect.Top * dpi,
                                                     rect.Right * dpi,
                                                     rect.Bottom * dpi))
        End Function

        Private Shared Function InsetRangeMotionRect(rect As SKRect,
                                                     amount As Single) As SKRect
            Return New SKRect(rect.Left + amount,
                              rect.Top + amount,
                              rect.Right - amount,
                              rect.Bottom - amount)
        End Function

        Private Shared Function ResolveDateRangeMotionAccent(ctx As MASThemeContext) As SKColor
            If ctx IsNot Nothing AndAlso ctx.BrandTheme IsNot Nothing Then Return ctx.BrandTheme.BrandPrimary
            If ctx IsNot Nothing AndAlso ctx.SemanticTheme IsNot Nothing Then Return ctx.SemanticTheme.Info
            Return New SKColor(64, 132, 246)
        End Function

        Private Shared Function RangeMotionAlpha(baseAlpha As Integer,
                                                 progress As Single) As Byte
            Dim value As Integer = CInt(Math.Max(0.0R, Math.Min(255.0R, CDbl(baseAlpha) * (1.0R - CDbl(MASMotionSystem.ClampProgress(progress))))))
            Return CByte(value)
        End Function

#End Region

    End Class

End Namespace
