Option Strict On
Option Explicit On

Imports Nexamas.UI.Theming
Imports SkiaSharp

Namespace Nexamas.UI.Rendering

    ''' <summary>
    ''' Runtime compositor for official broad surfaces. It composes material body and frame
    ''' through their owning systems; shadow remains owned by the caller's architecture contract.
    ''' </summary>
    Friend NotInheritable Class MASSurfaceTreatmentRuntimeSurfaceVisual

        Private Sub New()
        End Sub


        Friend Shared Function IsSurfaceVisualConsumer(consumerKind As MASSurfaceTreatmentConsumerKind) As Boolean
            Return MASSurfaceTreatmentRuntimeMaterialBody.IsSurfaceVisualConsumer(consumerKind)
        End Function

        Friend Shared Function IsLowRiskSurfaceVisualConsumer(consumerKind As MASSurfaceTreatmentConsumerKind) As Boolean
            Return MASSurfaceTreatmentRuntimeMaterialBody.IsLowRiskSurfaceVisualConsumer(consumerKind)
        End Function

        Friend Shared Function ResolveRuntimeResolution(consumerKind As MASSurfaceTreatmentConsumerKind,
                                                        requestedMaterialKey As String,
                                                        Optional requestedFrameStrength As MASSurfaceFrameStrength = MASSurfaceFrameStrength.Level3,
                                                        Optional hasExplicitMaterialOverride As Boolean = True) As MASSurfaceTreatmentResolution
            Return MASSurfaceTreatmentRuntimeMaterialBody.ResolveMaterialResolution(
                consumerKind:=consumerKind,
                requestedMaterialKey:=requestedMaterialKey,
                requestedFrameStrength:=requestedFrameStrength,
                hasExplicitMaterialOverride:=hasExplicitMaterialOverride)
        End Function

        Friend Shared Function ResolveRuntimeMaterialKey(consumerKind As MASSurfaceTreatmentConsumerKind,
                                                         materialKey As String,
                                                         Optional requestedFrameStrength As MASSurfaceFrameStrength = MASSurfaceFrameStrength.Level3,
                                                         Optional hasExplicitMaterialOverride As Boolean = True) As String
            Return MASSurfaceTreatmentRuntimeMaterialBody.ResolveMaterialKey(
                consumerKind:=consumerKind,
                materialKey:=materialKey,
                requestedFrameStrength:=requestedFrameStrength,
                hasExplicitMaterialOverride:=hasExplicitMaterialOverride)
        End Function

        Friend Shared Function TryDrawRuntimeSurfaceVisual(canvas As SKCanvas,
                                                           ctx As MASThemeContext,
                                                           boundsPx As SKRect,
                                                           consumerKind As MASSurfaceTreatmentConsumerKind,
                                                           materialKey As String,
                                                           dpi As Single,
                                                           Optional cornerRadiusPx As Single = 0.0F,
                                                           Optional opacity As Single = 1.0F,
                                                           Optional surfaceRole As MASSurfaceRole = MASSurfaceRole.Unknown,
                                                           Optional surfaceStrengthLevel As Integer = 4,
                                                           Optional borderStrengthLevel As Integer = 4,
                                                           Optional requestedFrameStrength As MASSurfaceFrameStrength = MASSurfaceFrameStrength.Level3,
                                                           Optional hasExplicitMaterialOverride As Boolean = True,
                                                           Optional drawFrame As Boolean = True) As Boolean
            If canvas Is Nothing OrElse ctx Is Nothing Then Return False
            If boundsPx.IsEmpty OrElse boundsPx.Width <= 1.0F OrElse boundsPx.Height <= 1.0F Then Return False

            Dim materialDrawn As Boolean =
                MASSurfaceTreatmentRuntimeMaterialBody.TryDrawMaterialBody(
                    canvas:=canvas,
                    ctx:=ctx,
                    boundsPx:=boundsPx,
                    consumerKind:=consumerKind,
                    materialKey:=materialKey,
                    dpi:=dpi,
                    cornerRadiusPx:=cornerRadiusPx,
                    opacity:=opacity,
                    surfaceRole:=surfaceRole,
                    surfaceStrengthLevel:=surfaceStrengthLevel,
                    requestedFrameStrength:=requestedFrameStrength,
                    hasExplicitMaterialOverride:=hasExplicitMaterialOverride)

            If Not materialDrawn Then Return False

            If drawFrame Then
                DrawRuntimeFrame(
                    canvas:=canvas,
                    ctx:=ctx,
                    boundsPx:=boundsPx,
                    dpi:=dpi,
                    cornerRadiusPx:=cornerRadiusPx,
                    opacity:=opacity,
                    surfaceStrengthLevel:=surfaceStrengthLevel,
                    borderStrengthLevel:=borderStrengthLevel)
            End If

            Return True
        End Function

        Friend Shared Function TryDrawExplicitSurfaceVisual(canvas As SKCanvas,
                                                            ctx As MASThemeContext,
                                                            boundsPx As SKRect,
                                                            consumerKind As MASSurfaceTreatmentConsumerKind,
                                                            materialKey As String,
                                                            dpi As Single,
                                                            Optional cornerRadiusPx As Single = 0.0F,
                                                            Optional opacity As Single = 1.0F,
                                                            Optional surfaceRole As MASSurfaceRole = MASSurfaceRole.Unknown,
                                                            Optional surfaceStrengthLevel As Integer = 4,
                                                            Optional borderStrengthLevel As Integer = 4,
                                                            Optional requestedFrameStrength As MASSurfaceFrameStrength = MASSurfaceFrameStrength.Level3,
                                                            Optional drawFrame As Boolean = True) As Boolean
            If Not MASSurfaceTreatmentMaterialKeyNormalizer.IsExplicitMaterial(materialKey) Then Return False

            Return TryDrawRuntimeSurfaceVisual(
                canvas:=canvas,
                ctx:=ctx,
                boundsPx:=boundsPx,
                consumerKind:=consumerKind,
                materialKey:=materialKey,
                dpi:=dpi,
                cornerRadiusPx:=cornerRadiusPx,
                opacity:=opacity,
                surfaceRole:=surfaceRole,
                surfaceStrengthLevel:=surfaceStrengthLevel,
                borderStrengthLevel:=borderStrengthLevel,
                requestedFrameStrength:=requestedFrameStrength,
                hasExplicitMaterialOverride:=True,
                drawFrame:=drawFrame)
        End Function

        Friend Shared Function TryDrawExplicitMaterialSlotSurfaceVisual(canvas As SKCanvas,
                                                            ctx As MASThemeContext,
                                                            boundsPx As SKRect,
                                                            consumerKind As MASSurfaceTreatmentConsumerKind,
                                                            materialKey As String,
                                                            dpi As Single,
                                                            Optional slot As MASSurfaceSlot = MASSurfaceSlot.Unknown,
                                                            Optional cornerRadiusPx As Single = 0.0F,
                                                            Optional opacity As Single = 1.0F,
                                                            Optional surfaceRole As MASSurfaceRole = MASSurfaceRole.Unknown,
                                                            Optional surfaceStrengthLevel As Integer = 4,
                                                            Optional borderStrengthLevel As Integer = 4,
                                                            Optional requestedFrameStrength As MASSurfaceFrameStrength = MASSurfaceFrameStrength.Level3,
                                                            Optional drawFrame As Boolean = True) As Boolean
            If Not MASSurfaceTreatmentMaterialKeyNormalizer.IsExplicitMaterial(materialKey) Then Return False

            Dim resolvedSlot As MASSurfaceSlot = MASSurfaceMaterialConsumerSlotCatalog.ResolveDefaultSlot(consumerKind, slot)
            Dim resolvedRole As MASSurfaceRole = MASSurfaceMaterialConsumerSlotCatalog.ResolveDefaultRole(consumerKind, resolvedSlot, surfaceRole)
            Dim route As MASSurfaceMaterialSlotRoute = MASSurfaceMaterialSlotResolver.ResolveMaterialKey(
                consumerKind:=consumerKind,
                materialKey:=materialKey,
                slot:=resolvedSlot,
                fallbackRole:=resolvedRole,
                frameStrength:=requestedFrameStrength)

            If route Is Nothing Then Return False

            Return TryDrawRuntimeSurfaceVisual(
                canvas:=canvas,
                ctx:=ctx,
                boundsPx:=boundsPx,
                consumerKind:=consumerKind,
                materialKey:=route.MaterialKey,
                dpi:=dpi,
                cornerRadiusPx:=cornerRadiusPx,
                opacity:=opacity,
                surfaceRole:=route.Role,
                surfaceStrengthLevel:=surfaceStrengthLevel,
                borderStrengthLevel:=borderStrengthLevel,
                requestedFrameStrength:=route.FrameStrength,
                hasExplicitMaterialOverride:=True,
                drawFrame:=drawFrame)
        End Function

        Friend Shared Function TryDrawMaterialSlotSurfaceVisual(canvas As SKCanvas,
                                                                      ctx As MASThemeContext,
                                                                      boundsPx As SKRect,
                                                                      consumerKind As MASSurfaceTreatmentConsumerKind,
                                                                      slot As MASSurfaceSlot,
                                                                      materialKey As String,
                                                                      dpi As Single,
                                                                      Optional cornerRadiusPx As Single = 0.0F,
                                                                      Optional opacity As Single = 1.0F,
                                                                      Optional fallbackRole As MASSurfaceRole = MASSurfaceRole.Unknown,
                                                                      Optional surfaceRole As MASSurfaceRole = MASSurfaceRole.Unknown,
                                                                      Optional surfaceStrengthLevel As Integer = -1,
                                                                      Optional borderStrengthLevel As Integer = -1,
                                                                      Optional frameStrength As MASSurfaceFrameStrength = MASSurfaceFrameStrength.Level3,
                                                                      Optional requestedFrameStrength As MASSurfaceFrameStrength = MASSurfaceFrameStrength.Level3,
                                                                      Optional drawFrame As Boolean = True) As Boolean
            Dim effectiveFrameStrength As MASSurfaceFrameStrength = ResolveEffectiveFrameStrength(frameStrength, requestedFrameStrength)
            Dim effectiveFallbackRole As MASSurfaceRole = If(surfaceRole = MASSurfaceRole.Unknown, fallbackRole, surfaceRole)

            Dim route As MASSurfaceMaterialSlotRoute = MASSurfaceMaterialSlotResolver.ResolveMaterialKey(
                consumerKind:=consumerKind,
                materialKey:=materialKey,
                slot:=slot,
                fallbackRole:=effectiveFallbackRole,
                frameStrength:=effectiveFrameStrength)

            If route Is Nothing Then Return False

            Dim effectiveSurfaceStrengthLevel As Integer = If(surfaceStrengthLevel < 0, route.SurfaceStrengthLevel, surfaceStrengthLevel)
            Dim effectiveBorderStrengthLevel As Integer = If(borderStrengthLevel < 0, CInt(route.FrameStrength), borderStrengthLevel)

            Return TryDrawRuntimeSurfaceVisual(
                canvas:=canvas,
                ctx:=ctx,
                boundsPx:=boundsPx,
                consumerKind:=consumerKind,
                materialKey:=route.MaterialKey,
                dpi:=dpi,
                cornerRadiusPx:=cornerRadiusPx,
                opacity:=opacity,
                surfaceRole:=route.Role,
                surfaceStrengthLevel:=effectiveSurfaceStrengthLevel,
                borderStrengthLevel:=effectiveBorderStrengthLevel,
                requestedFrameStrength:=route.FrameStrength,
                hasExplicitMaterialOverride:=True,
                drawFrame:=drawFrame)
        End Function

        Friend Shared Function TryDrawProfileSlotSurfaceVisual(canvas As SKCanvas,
                                                                      ctx As MASThemeContext,
                                                                      boundsPx As SKRect,
                                                                      consumerKind As MASSurfaceTreatmentConsumerKind,
                                                                      profileId As String,
                                                                      slot As MASSurfaceSlot,
                                                                      dpi As Single,
                                                                      Optional cornerRadiusPx As Single = 0.0F,
                                                                      Optional opacity As Single = 1.0F,
                                                                      Optional fallbackRole As MASSurfaceRole = MASSurfaceRole.Unknown,
                                                                      Optional drawFrame As Boolean = True) As Boolean
            Dim route As MASSurfaceMaterialSlotRoute = MASSurfaceMaterialSlotResolver.ResolveProfileSlot(
                consumerKind:=consumerKind,
                profileId:=profileId,
                slot:=slot,
                fallbackRole:=fallbackRole)

            If route Is Nothing Then Return False

            Return TryDrawRuntimeSurfaceVisual(
                canvas:=canvas,
                ctx:=ctx,
                boundsPx:=boundsPx,
                consumerKind:=consumerKind,
                materialKey:=route.MaterialKey,
                dpi:=dpi,
                cornerRadiusPx:=cornerRadiusPx,
                opacity:=opacity,
                surfaceRole:=route.Role,
                surfaceStrengthLevel:=route.SurfaceStrengthLevel,
                requestedFrameStrength:=route.FrameStrength,
                hasExplicitMaterialOverride:=True,
                drawFrame:=drawFrame)
        End Function

        Private Shared Function ResolveEffectiveFrameStrength(frameStrength As MASSurfaceFrameStrength,
                                                               requestedFrameStrength As MASSurfaceFrameStrength) As MASSurfaceFrameStrength
            If requestedFrameStrength <> MASSurfaceFrameStrength.Level3 Then Return requestedFrameStrength
            Return frameStrength
        End Function

        Private Shared Sub DrawRuntimeFrame(canvas As SKCanvas,
                                            ctx As MASThemeContext,
                                            boundsPx As SKRect,
                                            dpi As Single,
                                            cornerRadiusPx As Single,
                                            opacity As Single,
                                            surfaceStrengthLevel As Integer,
                                            borderStrengthLevel As Integer)
            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If boundsPx.Width <= 1.0F OrElse boundsPx.Height <= 1.0F Then Return

            Dim surfaceTheme As MASSurfaceThemeProfile = MASSurfaceThemeProfileResolver.Resolve(ctx)
            If surfaceTheme Is Nothing Then Return

            Dim frameSpec As MASSurfaceFrameSpec =
                MASSurfaceFrameFactory.Build(
                    surfaceTheme:=surfaceTheme.Frame,
                    surfaceStrength:=ToFrameStrength(surfaceStrengthLevel),
                    borderStrength:=ToFrameStrength(borderStrengthLevel),
                    opacity:=Clamp01(opacity))

            Using borderPainter As New MASSurfaceFrameBorderPainter()
                borderPainter.Draw(
                    canvas:=canvas,
                    baseRect:=boundsPx,
                    baseRadiusPx:=cornerRadiusPx,
                    dpi:=dpi,
                    spec:=MASSurfaceFramePainter.ScaleBorderSpec(frameSpec.Border, dpi))
            End Using
        End Sub

        Private Shared Function ToFrameStrength(value As Integer) As MASSurfaceFrameStrength
            If value < 1 Then value = 1
            If value > 6 Then value = 6
            Select Case value
                Case 1
                    Return MASSurfaceFrameStrength.Level1
                Case 2
                    Return MASSurfaceFrameStrength.Level2
                Case 3
                    Return MASSurfaceFrameStrength.Level3
                Case 4
                    Return MASSurfaceFrameStrength.Level4
                Case 6
                    Return MASSurfaceFrameStrength.Level6
                Case Else
                    Return MASSurfaceFrameStrength.Level5
            End Select
        End Function

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

End Namespace
