Option Strict On
Option Explicit On

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

Namespace Nexamas.UI.Rendering

    ''' <summary>
    ''' Draws the outer operation-progress container only.
    ''' Text and the actual progress bar remain real MASLabel and MASProgressBar children owned by MASOperationProgressBox.
    ''' Radius and shadow are resolved from the MASProgressBar architecture contract so the container follows the
    ''' same corner and elevation source as the inner progress control.
    ''' </summary>
    Friend NotInheritable Class MASOperationProgressBoxRenderer
        Implements IDisposable

        Private ReadOnly _shadow As New MASShadowPainter()
        Private _disposed As Boolean

        Friend Sub Draw(canvas As SKCanvas,
                        ctx As MASThemeContext,
                        boundsPx As SKRect,
                        operationContract As MASResolvedComponentContract,
                        progressContract As MASResolvedComponentContract,
                        materialKey As String,
                        borderStrength As MASSurfaceStrength,
                        surfaceStrength As MASSurfaceStrength)

            If _disposed OrElse canvas Is Nothing OrElse ctx Is Nothing Then Return
            If operationContract Is Nothing Then Throw New ArgumentNullException(NameOf(operationContract))
            If progressContract Is Nothing Then Throw New ArgumentNullException(NameOf(progressContract))
            If boundsPx.Width <= 2.0F OrElse boundsPx.Height <= 2.0F Then Return

            Dim scope As MASContractRenderScope =
                MASArchitectureRuntime.CreateRenderScopeForComponent(Of MASOperationProgressBox)(
                    NameOf(MASOperationProgressBoxRenderer),
                    operationContract,
                    MASRenderLayer.Surface,
                    MASRenderLayer.Border,
                    MASRenderLayer.Shadow)

            scope.RequireAnyLayer(
                MASRenderLayer.Surface,
                MASRenderLayer.Border,
                MASRenderLayer.Shadow)

            Dim dpi As Single = PixelSnap.SafeDpi(ctx.Dpi)
            Dim rect As SKRect = PixelSnap.SnapRectHalf(boundsPx)

            Dim radius As Single =
                MASRadiusResolver.ResolveSurfacePx(
                    contract:=progressContract,
                    rPx:=rect,
                    dpi:=dpi)

            radius = PixelSnap.SafeRadius(radius)

            Dim surfaceTheme As MASSurfaceThemeProfile =
                MASSurfaceThemeProfileResolver.Resolve(ctx)

            Dim borderStrengthLevel As Integer = ResolveStrengthLevel(borderStrength, surfaceTheme, True)
            Dim surfaceStrengthLevel As Integer = ResolveStrengthLevel(surfaceStrength, surfaceTheme, False)
            Dim requestedFrameStrength As MASSurfaceFrameStrength = ToFrameStrength(borderStrengthLevel)
            Dim hasExplicitMaterialOverride As Boolean = HasExplicitMaterialKey(materialKey)
            Dim normalizedMaterialKey As String = NormalizeMaterialKey(materialKey)

            DrawContainerShadow(
                canvas:=canvas,
                rect:=rect,
                radius:=radius,
                progressContract:=progressContract,
                dpi:=dpi,
                surfaceTheme:=surfaceTheme)

            MASSurfaceMaterialComponentGateway.TryDrawProgressSurface(
                canvas:=canvas,
                ctx:=ctx,
                boundsPx:=rect,
                materialKey:=normalizedMaterialKey,
                dpi:=dpi,
                cornerRadiusPx:=radius,
                hasExplicitMaterialOverride:=hasExplicitMaterialOverride,
                surfaceStrengthLevel:=surfaceStrengthLevel,
                borderStrengthLevel:=borderStrengthLevel,
                frameStrength:=requestedFrameStrength)

        End Sub

        Private Sub DrawContainerShadow(canvas As SKCanvas,
                               rect As SKRect,
                               radius As Single,
                               progressContract As MASResolvedComponentContract,
                               dpi As Single,
                               surfaceTheme As MASSurfaceThemeProfile)

            If canvas Is Nothing OrElse progressContract Is Nothing Then Return

            Dim spec As MASLayeredShadowSpec =
                MASShadowResolver.ResolveLayered(progressContract)

            _shadow.DrawLayeredShadow(
                canvas:=canvas,
                r:=rect,
                radiusPx:=radius,
                dpi:=dpi,
                shadowBase:=ResolveContainerShadowBase(surfaceTheme),
                spec:=spec)

        End Sub

        Private Shared Function ResolveContainerShadowBase(surfaceTheme As MASSurfaceThemeProfile) As SKColor
            If surfaceTheme IsNot Nothing Then
                Dim c As SKColor = surfaceTheme.Chrome.ShadowBase
                If c.Alpha <> 0 Then Return c

                c = surfaceTheme.Chrome.ShadowAmbient
                If c.Alpha <> 0 Then Return c

                c = surfaceTheme.Chrome.ShadowContact
                If c.Alpha <> 0 Then Return c

                c = surfaceTheme.Frame.ShadowBase
                If c.Alpha <> 0 Then Return c
            End If

            Return Nexamas.UI.Values.Color.Core.ShadowBase
        End Function

        Private Shared Function ResolveStrengthLevel(value As MASSurfaceStrength,
                                                     surfaceTheme As MASSurfaceThemeProfile,
                                                     isBorder As Boolean) As Integer

            If value <> MASSurfaceStrength.ThemeDefault Then
                Return ClampStrength(CInt(value))
            End If

            If surfaceTheme IsNot Nothing Then
                If isBorder Then
                    Return ClampStrength(surfaceTheme.Chrome.DefaultBorderStrengthLevel)
                End If

                Return ClampStrength(surfaceTheme.Chrome.DefaultSurfaceStrengthLevel)
            End If

            Return 3
        End Function

        Private Shared Function ClampStrength(value As Integer) As Integer
            If value < 1 Then Return 1
            If value > 6 Then Return 6
            Return value
        End Function

        Private Shared Function NormalizeMaterialKey(value As String) As String
            Dim key As String = If(value, String.Empty).Trim()
            If key.Length = 0 Then Return MASSurfaceMaterialIds.PlatformDefault
            Return key
        End Function

        Private Shared Function HasExplicitMaterialKey(value As String) As Boolean
            Dim key As String = If(value, String.Empty).Trim()
            Return key.Length > 0 AndAlso Not String.Equals(key, MASSurfaceMaterialIds.Auto, StringComparison.OrdinalIgnoreCase)
        End Function

        Private Shared Function ToFrameStrength(value As Integer) As MASSurfaceFrameStrength
            Select Case ClampStrength(value)
                Case 1
                    Return MASSurfaceFrameStrength.Level1
                Case 2
                    Return MASSurfaceFrameStrength.Level2
                Case 3
                    Return MASSurfaceFrameStrength.Level3
                Case 4
                    Return MASSurfaceFrameStrength.Level4
                Case 5
                    Return MASSurfaceFrameStrength.Level5
                Case Else
                    Return MASSurfaceFrameStrength.Level6
            End Select
        End Function

        Public Sub Dispose() Implements IDisposable.Dispose
            If _disposed Then Return
            _disposed = True
            _shadow.Dispose()
        End Sub

    End Class

End Namespace
