Option Strict On
Option Explicit On

Imports System
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.General

    Partial Friend NotInheritable Class MASSurfaceInteractionRing

        Private Sub New()
        End Sub

        Friend Structure Metrics
            Friend SnapStepPx As Single

            Friend RingStrokePx As Single
            Friend RingEdgeInsetPx As Single
            Friend RingPathInsetPx As Single

            Friend FillInsetPx As Single

            Friend ReadOnly Property RingInsetPx As Single
                Get
                    Return Me.RingPathInsetPx
                End Get
            End Property
        End Structure

#Region "Shared paints"

        Private Shared ReadOnly _fill As New SKPaint With {
            .IsAntialias = True,
            .IsDither = True,
            .Style = SKPaintStyle.Fill,
            .BlendMode = SKBlendMode.SrcOver
        }

        Private Shared ReadOnly _ring As New SKPaint With {
            .IsAntialias = True,
            .IsDither = True,
            .Style = SKPaintStyle.Stroke,
            .StrokeJoin = SKStrokeJoin.Round,
            .StrokeCap = SKStrokeCap.Round,
            .BlendMode = SKBlendMode.SrcOver
        }

        Private Shared ReadOnly _pathFill As New SKPaint With {
            .IsAntialias = True,
            .IsDither = True,
            .Style = SKPaintStyle.Fill,
            .BlendMode = SKBlendMode.SrcOver
        }

        Private Shared ReadOnly _pathRing As New SKPaint With {
            .IsAntialias = True,
            .IsDither = True,
            .Style = SKPaintStyle.Stroke,
            .StrokeJoin = SKStrokeJoin.Round,
            .StrokeCap = SKStrokeCap.Round,
            .BlendMode = SKBlendMode.SrcOver
        }

        Friend Shared Sub DisposeShared()
            Try
                _fill.Dispose()
            Catch masCaughtException1 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowRenderFallback(masCaughtException1)
            End Try
            Try
                _ring.Dispose()
            Catch masCaughtException2 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowRenderFallback(masCaughtException2)
            End Try
            Try
                _pathFill.Dispose()
            Catch masCaughtException3 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowRenderFallback(masCaughtException3)
            End Try
            Try
                _pathRing.Dispose()
            Catch masCaughtException4 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowRenderFallback(masCaughtException4)
            End Try
        End Sub

#End Region

#Region "Metrics"

        Friend Shared Function GetMetrics(dpi As Single,
                                          Optional fillInsetDipOverride As Single = -1.0F,
                                          Optional ringInsetDipOverride As Single = -1.0F,
                                          Optional ringStrokeDipOverride As Single = -1.0F,
                                          Optional ringMinStrokePxOverride As Single = -1.0F) As Metrics

            dpi = PixelSnap.SafeDpi(dpi)

            Dim m As Metrics

            m.SnapStepPx = SurfaceTokens.FxSnapStepPx

            If m.SnapStepPx <= 0.0F OrElse
               Single.IsNaN(m.SnapStepPx) OrElse
               Single.IsInfinity(m.SnapStepPx) Then

                Throw New InvalidOperationException("Tokens.SurfaceFx.SnapStepPx must be a valid positive value.")
            End If

            Dim strokeDip As Single =
                If(ringStrokeDipOverride >= 0.0F,
                   ringStrokeDipOverride,
                   SurfaceTokens.FxRingStroke)

            If Single.IsNaN(strokeDip) OrElse
               Single.IsInfinity(strokeDip) OrElse
               strokeDip < 0.0F Then

                Throw New InvalidOperationException("Ring stroke DIP is invalid.")
            End If

            Dim strokePx As Single = strokeDip * dpi

            Dim minPx As Single =
                If(ringMinStrokePxOverride >= 0.0F,
                   ringMinStrokePxOverride,
                   SurfaceTokens.FxRingMinStrokePx)

            If Single.IsNaN(minPx) OrElse
               Single.IsInfinity(minPx) OrElse
               minPx <= 0.0F Then

                Throw New InvalidOperationException("Ring minimum stroke PX is invalid.")
            End If

            m.RingStrokePx = Math.Max(minPx, strokePx) * 0.94F

            If m.RingStrokePx < minPx Then
                m.RingStrokePx = minPx
            End If

            Dim insetDip As Single =
                If(ringInsetDipOverride >= 0.0F,
                   ringInsetDipOverride,
                   SurfaceTokens.FxRingInset + 0.08F)

            If Single.IsNaN(insetDip) OrElse
               Single.IsInfinity(insetDip) OrElse
               insetDip < 0.0F Then

                Throw New InvalidOperationException("Ring inset DIP is invalid.")
            End If

            Dim edgeInsetPx As Single = insetDip * dpi

            If Single.IsNaN(edgeInsetPx) OrElse
               Single.IsInfinity(edgeInsetPx) OrElse
               edgeInsetPx < 0.0F Then

                Throw New InvalidOperationException("Ring edge inset PX is invalid.")
            End If

            m.RingEdgeInsetPx = edgeInsetPx
            m.RingPathInsetPx = PixelSnap.PathInset(m.RingEdgeInsetPx, m.RingStrokePx)

            Dim fillDip As Single =
                If(fillInsetDipOverride >= 0.0F,
                   fillInsetDipOverride,
                   SurfaceTokens.FxOverlayFillInset + 0.06F)

            If Single.IsNaN(fillDip) OrElse
               Single.IsInfinity(fillDip) OrElse
               fillDip < 0.0F Then

                Throw New InvalidOperationException("Fill inset DIP is invalid.")
            End If

            m.FillInsetPx = fillDip * dpi

            If Single.IsNaN(m.FillInsetPx) OrElse
               Single.IsInfinity(m.FillInsetPx) OrElse
               m.FillInsetPx < 0.0F Then

                Throw New InvalidOperationException("Fill inset PX is invalid.")
            End If

            Return m

        End Function

        Friend Shared Function SnapBaseRect(rPx As SKRect,
                                            m As Metrics) As SKRect

            Return PixelSnap.SnapRect(rPx, m.SnapStepPx)

        End Function

        Friend Shared Function FillRect(baseRectPx As SKRect,
                                        m As Metrics) As SKRect

            Return PixelSnap.SnapRect(
                PixelSnap.InsetAll(baseRectPx, m.FillInsetPx),
                m.SnapStepPx)

        End Function

        Friend Shared Function RingRect(baseRectPx As SKRect,
                                        m As Metrics) As SKRect

            Return PixelSnap.SnapRect(
                PixelSnap.InsetAll(baseRectPx, m.RingPathInsetPx),
                m.SnapStepPx)

        End Function

#End Region

    End Class

End Namespace
