Option Strict On
Option Explicit On

Imports System
Imports Nexamas.UI.Theming
Imports SkiaSharp

Namespace Nexamas.UI.Rendering

    ''' <summary>
    ''' Material-body resolver used only by the Surface Visual runtime compositor. It never draws a
    ''' complete surface, frame, chrome, or shadow; callers outside Surface/Runtime must go through
    ''' <see cref="MASSurfaceTreatmentRuntimeSurfaceVisual"/> so visual output stays composed by the official owners.
    ''' </summary>
    Friend NotInheritable Class MASSurfaceTreatmentRuntimeMaterialBody

        Private Sub New()
        End Sub

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

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

        Friend Shared Function ResolveMaterialResolution(consumerKind As MASSurfaceTreatmentConsumerKind,
                                                        requestedMaterialKey As String,
                                                        Optional requestedFrameStrength As MASSurfaceFrameStrength = MASSurfaceFrameStrength.Level3,
                                                        Optional hasExplicitMaterialOverride As Boolean = True) As MASSurfaceTreatmentResolution
            If Not IsSurfaceVisualConsumer(consumerKind) Then
                Return MASSurfaceTreatmentResolver.ResolveDefault(consumerKind, requestedFrameStrength)
            End If

            Dim normalizedKey As String = MASSurfaceMaterialRuntimeSwitch.ResolveRuntimeMaterialKey(
                consumerKind:=consumerKind,
                requestedMaterialKey:=requestedMaterialKey,
                hasExplicitMaterialOverride:=hasExplicitMaterialOverride,
                slot:=MASSurfaceSlot.Root)

            Dim explicitMaterial As Boolean = MASSurfaceTreatmentMaterialKeyNormalizer.IsExplicitMaterial(normalizedKey) AndAlso
                                              Not MASSurfaceMaterialRuntimeSwitch.IsSwitchableDefaultMaterialKey(normalizedKey)

            If explicitMaterial Then
                Return MASSurfaceTreatmentResolver.ResolveMaterialOverride(
                    consumerKind:=consumerKind,
                    materialKey:=normalizedKey,
                    frameStrength:=requestedFrameStrength)
            End If

            Return MASSurfaceTreatmentResolver.ResolveDefault(consumerKind, requestedFrameStrength)
        End Function

        Friend Shared Function ResolveMaterialKey(consumerKind As MASSurfaceTreatmentConsumerKind,
                                                         materialKey As String,
                                                         Optional requestedFrameStrength As MASSurfaceFrameStrength = MASSurfaceFrameStrength.Level3,
                                                         Optional hasExplicitMaterialOverride As Boolean = True) As String
            Dim resolution As MASSurfaceTreatmentResolution = ResolveMaterialResolution(
                consumerKind:=consumerKind,
                requestedMaterialKey:=materialKey,
                requestedFrameStrength:=requestedFrameStrength,
                hasExplicitMaterialOverride:=hasExplicitMaterialOverride)

            If resolution IsNot Nothing AndAlso IsSurfaceVisualConsumer(consumerKind) Then
                If resolution.IsCompatibleForCurrentRuntime OrElse
                   resolution.IsReadyForCurrentRuntimeConsumption OrElse
                   resolution.CompatibilityStatus = MASSurfaceTreatmentCompatibilityStatus.ResolvedWithMaterialFallback Then
                    Return resolution.ResolvedMaterialKey
                End If
            End If

            If MASSurfaceTreatmentMaterialKeyNormalizer.IsExplicitMaterial(materialKey) AndAlso
               Not MASSurfaceMaterialRuntimeSwitch.IsSwitchableDefaultMaterialKey(materialKey) Then
                Return MASSurfaceMaterialIds.PlatformDefault
            End If

            Return MASSurfaceMaterialRuntimeSwitch.ResolveRuntimeMaterialKey(
                consumerKind:=consumerKind,
                requestedMaterialKey:=materialKey,
                hasExplicitMaterialOverride:=hasExplicitMaterialOverride,
                slot:=MASSurfaceSlot.Root)
        End Function

        Friend Shared Function TryDrawMaterialBody(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 requestedFrameStrength As MASSurfaceFrameStrength = MASSurfaceFrameStrength.Level3,
                                                             Optional hasExplicitMaterialOverride 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 material As IMASSurfaceMaterial = ResolveRuntimeMaterial(
                consumerKind:=consumerKind,
                materialKey:=materialKey,
                requestedFrameStrength:=requestedFrameStrength,
                hasExplicitMaterialOverride:=hasExplicitMaterialOverride)
            If material Is Nothing Then Return False

            Dim safeDpi As Single = dpi
            If safeDpi <= 0.0F OrElse Single.IsNaN(safeDpi) OrElse Single.IsInfinity(safeDpi) Then safeDpi = ctx.Dpi
            If safeDpi <= 0.0F OrElse Single.IsNaN(safeDpi) OrElse Single.IsInfinity(safeDpi) Then safeDpi = 1.0F

            Dim radius As Single = cornerRadiusPx
            If radius < 0.0F OrElse Single.IsNaN(radius) OrElse Single.IsInfinity(radius) Then radius = 0.0F

            Dim materialContext As New MASSurfaceMaterialContext(
                themeContext:=ctx,
                surfaceTheme:=MASSurfaceThemeProfileResolver.Resolve(ctx),
                surfaceStrengthLevel:=ClampLevel(surfaceStrengthLevel),
                dpi:=safeDpi,
                boundsPx:=boundsPx,
                cornerRadiusPx:=radius,
                opacity:=Clamp01(opacity),
                visualState:=MASSurfaceVisualState.Normal,
                surfaceRole:=ResolveRole(consumerKind, surfaceRole))

            material.Draw(canvas, materialContext)
            Return True
        End Function

        Friend Shared Function TryDrawExplicitMaterialBody(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 requestedFrameStrength As MASSurfaceFrameStrength = MASSurfaceFrameStrength.Level3) As Boolean
            If Not MASSurfaceTreatmentMaterialKeyNormalizer.IsExplicitMaterial(materialKey) Then Return False

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

        Friend Shared Function TryDrawMaterialSlotBody(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 frameStrength As MASSurfaceFrameStrength = MASSurfaceFrameStrength.Level3) As Boolean
            Dim route As MASSurfaceMaterialSlotRoute = MASSurfaceMaterialSlotResolver.ResolveMaterialKey(
                consumerKind:=consumerKind,
                materialKey:=materialKey,
                slot:=slot,
                fallbackRole:=fallbackRole,
                frameStrength:=frameStrength)

            If route Is Nothing Then Return False

            Return TryDrawMaterialBody(
                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)
        End Function

        Friend Shared Function TryDrawProfileSlotBody(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) As Boolean
            Dim route As MASSurfaceMaterialSlotRoute = MASSurfaceMaterialSlotResolver.ResolveProfileSlot(
                consumerKind:=consumerKind,
                profileId:=profileId,
                slot:=slot,
                fallbackRole:=fallbackRole)

            If route Is Nothing Then Return False

            Return TryDrawMaterialBody(
                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)
        End Function

        Private Shared Function ResolveRuntimeMaterial(consumerKind As MASSurfaceTreatmentConsumerKind,
                                                       materialKey As String,
                                                       requestedFrameStrength As MASSurfaceFrameStrength,
                                                       hasExplicitMaterialOverride As Boolean) As IMASSurfaceMaterial
            If Not IsSurfaceVisualConsumer(consumerKind) Then Return Nothing

            Dim resolvedKey As String = ResolveMaterialKey(
                consumerKind:=consumerKind,
                materialKey:=materialKey,
                requestedFrameStrength:=requestedFrameStrength,
                hasExplicitMaterialOverride:=hasExplicitMaterialOverride)

            Return MASSurfaceMaterialRegistry.Resolve(resolvedKey)
        End Function

        Private Shared Function ResolveRole(consumerKind As MASSurfaceTreatmentConsumerKind,
                                            fallback As MASSurfaceRole) As MASSurfaceRole
            If fallback <> MASSurfaceRole.Unknown Then Return fallback

            Select Case consumerKind
                Case MASSurfaceTreatmentConsumerKind.PanelShellSurface,
                     MASSurfaceTreatmentConsumerKind.GroupBoxSurface
                    Return MASSurfaceRole.PanelShell
                Case MASSurfaceTreatmentConsumerKind.ApplicationChromeSurface,
                     MASSurfaceTreatmentConsumerKind.BackgroundSurface,
                     MASSurfaceTreatmentConsumerKind.TopBarSurface,
                     MASSurfaceTreatmentConsumerKind.ToolbarSurface
                    Return MASSurfaceRole.ApplicationChrome
                Case MASSurfaceTreatmentConsumerKind.CardSurface
                    Return MASSurfaceRole.Card
                Case MASSurfaceTreatmentConsumerKind.ToastSurface
                    Return MASSurfaceRole.Toast
                Case MASSurfaceTreatmentConsumerKind.TooltipSurface
                    Return MASSurfaceRole.Popup
                Case MASSurfaceTreatmentConsumerKind.ProgressSurface
                    Return MASSurfaceRole.Control
                Case Else
                    Return MASSurfaceRole.Unknown
            End Select
        End Function

        Private Shared Function ClampLevel(value As Integer) As Integer
            If value < 1 Then Return 1
            If value > 6 Then Return 6
            Return value
        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
