Option Strict On
Option Explicit On

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

Namespace Nexamas.UI.Rendering

    Partial Friend NotInheritable Class MASVisualPrimitivesPainter
#Region "Shared Helpers / Shared Types"

        Friend Structure StrokeRoundRectInfo
            Friend Rect As SKRect
            Friend Radius As Single
            Friend StrokeWidth As Single
            Friend HasValue As Boolean
        End Structure

        Friend Shared Function BuildStrokeRoundRectInfo(baseRect As SKRect,
                                                        baseRadius As Single,
                                                        dpi As Single,
                                                        edgeInsetPx As Single,
                                                        strokeWidthPx As Single) As StrokeRoundRectInfo

            Dim info As New StrokeRoundRectInfo With {
                .Rect = SKRect.Empty,
                .Radius = 0.0F,
                .StrokeWidth = strokeWidthPx,
                .HasValue = False
            }

            If baseRect.Width <= 1 OrElse baseRect.Height <= 1 Then Return info

            Dim sw As Single = Math.Max(1.0F, strokeWidthPx)
            Dim m As MASSurfaceInteractionRing.Metrics = MASSurfaceInteractionRing.GetMetrics(dpi)

            Dim pathInset As Single = PixelSnap.PathInset(edgeInsetPx, sw)
            Dim rr As SKRect = PixelSnap.SnapRect(PixelSnap.InsetAll(baseRect, pathInset), m.SnapStepPx)

            If Not PixelSnap.HasArea(rr) Then Return info

            info.Rect = rr
            info.Radius = CornerRadii.ResolveInsetRadiusPx(baseRadius, pathInset)
            info.StrokeWidth = sw
            info.HasValue = True

            Return info

        End Function

        Friend Shared Function ToDpi100(dpi As Single) As Integer
            Return CInt(Math.Round(dpi * 100.0F))
        End Function

        Friend Shared Function ToRoundedPxInt(v As Single) As Integer
            Return Math.Max(1, CInt(Math.Round(v)))
        End Function

        Friend Shared Function SanitizeUnitSingle(v As Single,
                                                  fallbackValue As Single) As Single

            If Single.IsNaN(v) OrElse Single.IsInfinity(v) Then Return fallbackValue
            If v < 0.0F Then Return 0.0F
            If v > 1.0F Then Return 1.0F

            Return v

        End Function

        Friend Shared Function Clamp01(v As Single) As Single
            If Single.IsNaN(v) OrElse Single.IsInfinity(v) Then Return 0.0F
            If v < 0.0F Then Return 0.0F
            If v > 1.0F Then Return 1.0F
            Return v
        End Function

        Friend Shared Function WithAlphaByte(c As SKColor,
                                             a As Byte) As SKColor

            Return New SKColor(c.Red, c.Green, c.Blue, a)

        End Function

        Friend Shared Sub DisposeShader(ByRef sh As SKShader)
            If sh Is Nothing Then Return

            Try
                sh.Dispose()
            Finally
                sh = Nothing
            End Try
        End Sub

        Friend Shared Sub WithClipRoundRect(canvas As SKCanvas,
                                            r As SKRect,
                                            radiusPx As Single,
                                            drawAction As Action)

            If canvas Is Nothing OrElse drawAction Is Nothing Then Return

            Dim save As Integer = canvas.Save()

            Try
                Using clip As New SKPath()
                    clip.AddRoundRect(r, radiusPx, radiusPx)
                    canvas.ClipPath(clip, SKClipOperation.Intersect, True)
                End Using

                drawAction()
            Finally
                canvas.RestoreToCount(save)
            End Try

        End Sub

        Friend Shared Sub WithTranslatedLocalRect(canvas As SKCanvas,
                                                  baseRect As SKRect,
                                                  drawAction As Action(Of SKRect))

            If canvas Is Nothing OrElse drawAction Is Nothing Then Return

            Dim save As Integer = canvas.Save()

            Try
                canvas.Translate(baseRect.Left, baseRect.Top)

                Dim lr As New SKRect(
                    0.0F,
                    0.0F,
                    baseRect.Width,
                    baseRect.Height)

                drawAction(lr)
            Finally
                canvas.RestoreToCount(save)
            End Try

        End Sub

        Friend Structure SizeDpiKey
            Friend W As Integer
            Friend H As Integer
            Friend Dpi100 As Integer
        End Structure

        Friend Structure AcrylicColorKey
            Friend Top As SKColor
            Friend Mid As SKColor
            Friend Bot As SKColor
            Friend Wash As SKColor
        End Structure

        Friend Shared Function BuildSizeDpiKey(r As SKRect,
                                               dpi As Single) As SizeDpiKey

            Return New SizeDpiKey With {
                .W = ToRoundedPxInt(r.Width),
                .H = ToRoundedPxInt(r.Height),
                .Dpi100 = ToDpi100(dpi)
            }

        End Function

        Friend Shared Function BuildAcrylicColorKey(topCol As SKColor,
                                                    midCol As SKColor,
                                                    botCol As SKColor,
                                                    wash As SKColor) As AcrylicColorKey

            Return New AcrylicColorKey With {
                .Top = topCol,
                .Mid = midCol,
                .Bot = botCol,
                .Wash = wash
            }

        End Function

        Friend Shared Function SizeDpiKeyEquals(a As SizeDpiKey,
                                                b As SizeDpiKey) As Boolean

            Return a.W = b.W AndAlso a.H = b.H AndAlso a.Dpi100 = b.Dpi100

        End Function

        Friend Shared Function AcrylicColorKeyEquals(a As AcrylicColorKey,
                                                     b As AcrylicColorKey) As Boolean

            Return a.Top = b.Top AndAlso
                   a.Mid = b.Mid AndAlso
                   a.Bot = b.Bot AndAlso
                   a.Wash = b.Wash

        End Function

        Private Shared Sub SafeDisposeObject(obj As IDisposable)
            If obj Is Nothing Then Return

            Try
                obj.Dispose()
            Catch masCaughtException1 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowRenderFallback(masCaughtException1)
            End Try
        End Sub

#End Region

    End Class

End Namespace
