' SSOT-V2 Signature: This class/file follows the SSOT-V2 architecture.
Option Strict On
Option Explicit On

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

Namespace Nexamas.UI.Rendering

    ''' <summary>
    ''' Premium elevated surface fill renderer.
    ''' Owns only the card surface body and internal surface light/shade effects.
    ''' Outer visual composition responsibilities are handled outside this material fill.
    ''' </summary>
    Friend NotInheritable Class MASElevatedSurfaceFillPainter
        Implements IDisposable

#Region "Fields"

        Private _disposed As Boolean

        Private ReadOnly _fill As New SKPaint With {.IsAntialias = True, .IsDither = True, .Style = SKPaintStyle.Fill}
        Private ReadOnly _fx As New SKPaint With {.IsAntialias = True, .IsDither = True, .Style = SKPaintStyle.Fill}
        Private _palStamp As Integer = -1
        Private _palDefault As MASCardSurfaceVisualPalette
        Private _palSecondary As MASCardSurfaceVisualPalette
        Private _palThemeRef As IMASAppTheme = Nothing
        Private _palThemeId As String = Nothing
        Private _palThemeVersion As Integer = Integer.MinValue

#End Region

#Region "Types"

        Private Structure ElevatedSurfaceColors
            Friend Top As SKColor
            Friend Mid As SKColor
            Friend Bot As SKColor
        End Structure

        Private Structure ElevatedSurfaceFxRecipe
            Friend LiftAlpha As Byte
            Friend LiftMixT As Single
            Friend LiftHeightRatio As Single

            Friend UseSheen As Boolean
            Friend SheenAlpha As Byte
            Friend SheenHeightMul As Single

            Friend VignetteAlpha As Byte
            Friend VignetteMixT As Single
            Friend VignetteInnerStop As Single
            Friend VignetteRadiusMul As Single
        End Structure

#End Region

#Region "Dispose"

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

            SafeDispose(_fill)
            SafeDispose(_fx)
        End Sub

        Private Shared Sub SafeDispose(p As SKPaint)
            If p Is Nothing Then Return
            Try
                p.Dispose()
            Catch masCaughtException2 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowRenderFallback(masCaughtException2)
            End Try
        End Sub

#End Region

#Region "Public API"

        Friend Sub DrawElevatedSurfaceFill(canvas As SKCanvas,
                               ctx As MASThemeContext,
                               r As SKRect,
                               radius As Single,
                               dpi As Single,
                               visualStyle As MASCardVisualStyle)

            If _disposed Then Return
            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If r.Width <= 1.0F OrElse r.Height <= 1.0F Then Return

            dpi = PixelSnap.SafeDpi(dpi)
            radius = PixelSnap.SafeRadius(radius)

            Dim rm As MASSurfaceInteractionRing.Metrics = MASSurfaceInteractionRing.GetMetrics(dpi)
            Dim baseR As SKRect = MASSurfaceInteractionRing.SnapBaseRect(r, rm)

            If baseR.Width <= 1.0F OrElse baseR.Height <= 1.0F Then Return

            Dim pal As MASCardSurfaceVisualPalette = GetPalette(ctx, visualStyle)
            Dim colors As ElevatedSurfaceColors =
        ResolveSurfaceColors(ctx, pal, visualStyle)

            MASVisualPrimitivesPainter.WithClipRoundRect(
        canvas,
        baseR,
        radius,
        Sub()
            If visualStyle = MASCardVisualStyle.Secondary Then
                DrawBaseGradientSecondary(
                    canvas:=canvas,
                    r:=baseR,
                    radius:=radius,
                    dpi:=dpi,
                    pal:=pal,
                    colors:=colors)
            Else
                DrawBaseGradient(
                    canvas:=canvas,
                    r:=baseR,
                    radius:=radius,
                    colors:=colors)
            End If

            DrawTopLiftBandFx(
                canvas:=canvas,
                baseRect:=baseR,
                radius:=radius,
                dpi:=dpi,
                pal:=pal)

            DrawBottomAnchorFx(
                canvas:=canvas,
                baseRect:=baseR,
                radius:=radius,
                dpi:=dpi,
                pal:=pal)
        End Sub)

        End Sub
#End Region

#Region "Palette"

        Private Function GetPalette(ctx As MASThemeContext, visualStyle As MASCardVisualStyle) As MASCardSurfaceVisualPalette
            Dim mustRebuild As Boolean = True

            Dim stamp As Integer = Integer.MinValue
            Dim theme As IMASAppTheme = Nothing
            Dim themeId As String = Nothing
            Dim themeVersion As Integer = Integer.MinValue

            If ctx IsNot Nothing Then
                stamp = ctx.ThemeStamp
                theme = ctx.Theme

                If theme IsNot Nothing Then
                    themeId = If(theme.Id, String.Empty)
                    themeVersion = theme.Version
                End If
            End If

            mustRebuild =
                (_palThemeRef Is Nothing) OrElse
                (Not Object.ReferenceEquals(_palThemeRef, theme)) OrElse
                (_palStamp <> stamp) OrElse
                (Not String.Equals(_palThemeId, themeId, StringComparison.Ordinal)) OrElse
                (_palThemeVersion <> themeVersion)

            If mustRebuild Then
                _palDefault = ResolvePalette(ctx, MASCardVisualStyle.Default)
                _palSecondary = ResolvePalette(ctx, MASCardVisualStyle.Secondary)

                _palStamp = stamp
                _palThemeRef = theme
                _palThemeId = themeId
                _palThemeVersion = themeVersion
            End If

            If visualStyle = MASCardVisualStyle.Secondary Then Return _palSecondary
            Return _palDefault
        End Function

        Private Shared Function ResolvePalette(ctx As MASThemeContext, visualStyle As MASCardVisualStyle) As MASCardSurfaceVisualPalette
            Return MASCardSurfaceVisualPaletteResolver.Resolve(ctx, visualStyle)
        End Function

        Private Shared Function ResolveSurfaceColors(ctx As MASThemeContext,
                                                     p As MASCardSurfaceVisualPalette,
                                                     visualStyle As MASCardVisualStyle) As ElevatedSurfaceColors

            Dim fam As CommonEnums.SurfaceFamily = CommonEnums.SurfaceFamily.Card
            Dim intensity As MASVisualEffectIntensityProfiles.AcrylicSurfaceIntensityProfile =
                MASVisualEffectIntensityProfiles.ResolveAcrylic(fam)

            Dim topCol As SKColor = p.SurfaceTop
            Dim midCol As SKColor = p.SurfaceMid
            Dim botCol As SKColor = p.SurfaceBot

            If p.AccentTopT > 0.0F OrElse p.AccentMidT > 0.0F OrElse p.AccentBotT > 0.0F Then
                Dim tint As SKColor = p.AccentTint

                Dim hasTint As Boolean =
                    (tint.Alpha <> 0) OrElse
                    (tint.Red <> 0) OrElse
                    (tint.Green <> 0) OrElse
                    (tint.Blue <> 0)

                If hasTint Then
                    topCol = ColorMath.Mix(topCol, tint, p.AccentTopT)
                    midCol = ColorMath.Mix(midCol, tint, p.AccentMidT)
                    botCol = ColorMath.Mix(botCol, tint, p.AccentBotT)
                End If
            End If

            Dim topLift As SKColor = ColorMath.Mix(topCol, p.Sheen, 0.34F)
            Dim midLift As SKColor = ColorMath.Mix(midCol, p.ColdTint, 0.14F)
            Dim botAnchor As SKColor = ColorMath.Mix(botCol, p.InnerShade, 0.1F)

            topCol = ColorMath.Mix(topCol, topLift, 0.075F + intensity.TopBandEmphasis * 0.035F)
            midCol = ColorMath.Mix(midCol, midLift, 0.03F + intensity.TopBandEmphasis * 0.015F)
            botCol = ColorMath.Mix(botCol, botAnchor, 0.065F + intensity.BottomAnchorStrength * 0.04F)

            Return New ElevatedSurfaceColors With {
                .Top = topCol,
                .Mid = midCol,
                .Bot = botCol
            }
        End Function

#End Region

#Region "Base Gradient"

        Private Sub DrawBaseGradient(canvas As SKCanvas,
                                     r As SKRect,
                                     radius As Single,
                                     colors As ElevatedSurfaceColors)

            Dim top0 As SKColor = ColorMath.Mix(colors.Top, colors.Mid, 0.12F)
            Dim top1 As SKColor = ColorMath.Mix(colors.Top, colors.Mid, 0.28F)
            Dim mid0 As SKColor = colors.Mid
            Dim mid1 As SKColor = ColorMath.Mix(colors.Mid, colors.Bot, 0.22F)
            Dim bot0 As SKColor = colors.Bot

            Using shd As SKShader = SKShader.CreateLinearGradient(
                New SKPoint(r.Left, r.Top),
                New SKPoint(r.Left, r.Bottom),
                New SKColor() {top0, top1, mid0, mid1, bot0},
                New Single() {0.0F, 0.12F, 0.46F, 0.8F, 1.0F},
                SKShaderTileMode.Clamp)

                _fill.Shader = shd
                _fill.BlendMode = SKBlendMode.SrcOver
                canvas.DrawRoundRect(r, radius, radius, _fill)
                _fill.Shader = Nothing
            End Using
        End Sub

        Private Sub DrawBaseGradientSecondary(canvas As SKCanvas,
                                              r As SKRect,
                                              radius As Single,
                                              dpi As Single,
                                              pal As MASCardSurfaceVisualPalette,
                                              colors As ElevatedSurfaceColors)

            Dim top0 As SKColor = ColorMath.Mix(colors.Top, colors.Mid, 0.34F)
            Dim top1 As SKColor = ColorMath.Mix(colors.Top, colors.Mid, 0.46F)
            Dim mid0 As SKColor = ColorMath.Mix(colors.Mid, pal.ColdTint, 0.012F)
            Dim mid1 As SKColor = ColorMath.Mix(colors.Mid, colors.Bot, 0.045F)
            Dim bot0 As SKColor = ColorMath.Mix(colors.Bot, pal.InnerShade, 0.005F)

            Using shd As SKShader = SKShader.CreateLinearGradient(
                New SKPoint(r.Left, r.Top),
                New SKPoint(r.Left, r.Bottom),
                New SKColor() {top0, top1, mid0, mid1, bot0},
                New Single() {0.0F, 0.18F, 0.52F, 0.86F, 1.0F},
                SKShaderTileMode.Clamp)

                _fill.Shader = shd
                _fill.BlendMode = SKBlendMode.SrcOver
                canvas.DrawRoundRect(r, radius, radius, _fill)
                _fill.Shader = Nothing
            End Using
        End Sub

#End Region

#Region "FX"

        Private Sub DrawTopLiftBandFx(canvas As SKCanvas,
                                      baseRect As SKRect,
                                      radius As Single,
                                      dpi As Single,
                                      pal As MASCardSurfaceVisualPalette)

            Dim recipe As ElevatedSurfaceFxRecipe = ResolveFxRecipe()
            If recipe.LiftAlpha = 0 Then Return

            Dim liftH As Single = Math.Max(10.0F * dpi, baseRect.Height * recipe.LiftHeightRatio)

            Dim liftTop As SKColor = ColorMath.Mix(pal.SurfaceTop, pal.Sheen, 0.72F)
            Dim liftMid As SKColor = ColorMath.Mix(pal.SurfaceMid, pal.Sheen, 0.44F)

            Using sh As SKShader =
                SKShader.CreateLinearGradient(
                    New SKPoint(baseRect.Left, baseRect.Top),
                    New SKPoint(baseRect.Left, baseRect.Top + liftH),
                    New SKColor() {
                        ColorMath.WithAlpha(liftTop, recipe.LiftAlpha),
                        ColorMath.WithAlpha(liftMid, CByte(Math.Max(0, CInt(recipe.LiftAlpha) - 10))),
                        SKColors.Transparent
                    },
                    New Single() {0.0F, 0.42F, 1.0F},
                    SKShaderTileMode.Clamp)

                Using p As New SKPaint With {
                    .IsAntialias = True,
                    .IsDither = True,
                    .Style = SKPaintStyle.Fill,
                    .BlendMode = SKBlendMode.Screen,
                    .Shader = sh
                }
                    canvas.DrawRoundRect(baseRect, radius, radius, p)
                End Using
            End Using
        End Sub

        Private Sub DrawBottomAnchorFx(canvas As SKCanvas,
                                       baseRect As SKRect,
                                       radius As Single,
                                       dpi As Single,
                                       pal As MASCardSurfaceVisualPalette)

            Dim recipe As ElevatedSurfaceFxRecipe = ResolveFxRecipe()
            If recipe.VignetteAlpha = 0 Then Return

            Using sh As SKShader =
                SKShader.CreateRadialGradient(
                    New SKPoint(baseRect.MidX, baseRect.MidY + (baseRect.Height * 0.08F)),
                    Math.Max(baseRect.Width, baseRect.Height) * recipe.VignetteRadiusMul,
                    New SKColor() {
                        SKColors.Transparent,
                        ColorMath.WithAlpha(
                            ColorMath.Mix(pal.SurfaceBot, SKColors.Black, recipe.VignetteMixT),
                            CByte(Math.Max(0, CInt(recipe.VignetteAlpha) - 4))
                        ),
                        ColorMath.WithAlpha(
                            ColorMath.Mix(pal.InnerShade, SKColors.Black, 0.35F),
                            recipe.VignetteAlpha
                        )
                    },
                    New Single() {recipe.VignetteInnerStop, 0.9F, 1.0F},
                    SKShaderTileMode.Clamp)

                Using p As New SKPaint With {
                    .IsAntialias = True,
                    .IsDither = True,
                    .Style = SKPaintStyle.Fill,
                    .BlendMode = SKBlendMode.Multiply,
                    .Shader = sh
                }
                    canvas.DrawRoundRect(baseRect, radius, radius, p)
                End Using
            End Using
        End Sub

        Private Shared Function ResolveFxRecipe() As ElevatedSurfaceFxRecipe
            Dim fam As CommonEnums.SurfaceFamily = CommonEnums.SurfaceFamily.Card
            Dim behavior As MASVisualEffectIntensityProfiles.AcrylicSurfaceIntensityProfile =
                MASVisualEffectIntensityProfiles.ResolveAcrylic(fam)

            Return New ElevatedSurfaceFxRecipe With {
                .LiftAlpha = CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(16.0F + behavior.TopBandEmphasis * 18.0F))))),
                .LiftMixT = 0.045F + (behavior.TopBandEmphasis * 0.045F),
                .LiftHeightRatio = 0.18F + behavior.TopBandEmphasis * 0.05F,
                .UseSheen = behavior.UseTopSheen,
                .SheenAlpha = CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(10.0F + behavior.SheenStrength * 16.0F))))),
                .SheenHeightMul = Math.Max(0.28F, behavior.SheenHeightMul),
                .VignetteAlpha = CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(10.0F + behavior.BottomAnchorStrength * 16.0F))))),
                .VignetteMixT = 0.08F + behavior.BottomAnchorStrength * 0.06F,
                .VignetteInnerStop = 0.7F,
                .VignetteRadiusMul = 0.8F
            }
        End Function

#End Region


    End Class

End Namespace
