' 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>
    ''' Soft chrome-like interactive component chrome renderer for action/bar skins.
    ''' Component Chrome owns this painter; Surface Material remains responsible only for
    ''' reusable surface bodies and does not know action-specific chrome.
    ''' </summary>
    Friend NotInheritable Class MASActionChromePainter
        Implements IDisposable

#Region "Fields"

        Private _disposed As Boolean

        Private ReadOnly _roundRectPath As New SKPath()
        Private ReadOnly _fillPaint As New SKPaint With {
            .IsAntialias = True,
            .IsDither = True,
            .Style = SKPaintStyle.Fill
        }
        Private ReadOnly _strokePaint As New SKPaint With {
            .IsAntialias = True,
            .IsDither = True,
            .Style = SKPaintStyle.Stroke,
            .StrokeJoin = SKStrokeJoin.Round,
            .StrokeCap = SKStrokeCap.Round
        }
        Private ReadOnly _effectPaint As New SKPaint With {
            .IsAntialias = True,
            .IsDither = True,
            .Style = SKPaintStyle.Fill
        }

#End Region

#Region "Specs"

        Friend Structure MASActionChromeSpec
            Friend FillTop As SKColor
            Friend FillBottom As SKColor

            Friend BorderOuter As SKColor
            Friend BorderInner As SKColor

            Friend InnerHighlight As SKColor
            Friend InnerShade As SKColor

            Friend MaterialWash As SKColor
            Friend BodyGlow As SKColor
            Friend TopSheen As SKColor
        End Structure

        Friend Structure MASBarChromeFxSpec
            Friend TopLift As SKColor
            Friend Sheen As SKColor
            Friend Base As SKColor
            Friend BottomShade As SKColor

            Friend InnerHighlightBase As SKColor
            Friend InnerShadowBase As SKColor

            Friend ShadowBase As SKColor

            Friend BulgeAlpha As Byte
            Friend SpecularAlpha As Byte
            Friend ShadowAlpha As Byte

            Friend SpecularBandHDip As Single
            Friend SpecularTopPadDip As Single

            Friend ShadowDyDip As Single
            Friend ShadowSigmaDip As Single
            Friend ShadowCasterHDip As Single
        End Structure

#End Region

#Region "Dispose"

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

            SafeDispose(_roundRectPath)
            SafeDispose(_fillPaint)
            SafeDispose(_strokePaint)
            SafeDispose(_effectPaint)
        End Sub

#End Region

#Region "Chrome draw"

        Friend Sub DrawActionChrome(canvas As SKCanvas,
                                          ctx As MASThemeContext,
                                          baseRect As SKRect,
                                          radius As Single,
                                          dpi As Single,
                                          spec As MASActionChromeSpec)

            If _disposed Then Return
            If canvas Is Nothing Then Return
            If baseRect.Width <= 1 OrElse baseRect.Height <= 1 Then Return

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

            _roundRectPath.Reset()
            _roundRectPath.AddRoundRect(baseRect, radius, radius)

            Dim materialBodyDrawn As Boolean =
                MASSurfaceMaterialComponentGateway.TryDrawActionChromeMaterialBody(
                    canvas:=canvas,
                    ctx:=ctx,
                    boundsPx:=baseRect,
                    materialKey:=MASSurfaceMaterialIds.Auto,
                    dpi:=dpi,
                    cornerRadiusPx:=radius,
                    opacity:=1.0F,
                    surfaceStrengthLevel:=3,
                    frameStrength:=MASSurfaceFrameStrength.Level3)

            '========================================================
            ' 1) BODY
            '========================================================
            If Not materialBodyDrawn Then

            Dim topSoft As SKColor =
                ColorMath.MixLinear(spec.FillTop, spec.FillBottom, 0.18F)

            Dim mid As SKColor =
                ColorMath.MixLinear(spec.FillTop, spec.FillBottom, 0.48F)

            Dim botSoft As SKColor =
                ColorMath.MixLinear(spec.FillBottom, SKColors.Black, 0.04F)

            Using sh As SKShader = SKShader.CreateLinearGradient(
                New SKPoint(baseRect.Left, baseRect.Top),
                New SKPoint(baseRect.Left, baseRect.Bottom),
                New SKColor() {topSoft, mid, botSoft},
                New Single() {0.0F, 0.55F, 1.0F},
                SKShaderTileMode.Clamp)

                PrepareFillPaint()
                _fillPaint.Shader = sh
                canvas.DrawPath(_roundRectPath, _fillPaint)
                _fillPaint.Shader = Nothing
            End Using

            End If

            '========================================================
            ' 2) BODY GLOW
            '========================================================
            If spec.BodyGlow.Alpha <> 0 Then
                Dim cx As Single = baseRect.MidX
                Dim cy As Single = baseRect.Top + (baseRect.Height * 0.58F)
                Dim rad As Single = Math.Max(baseRect.Width, baseRect.Height) * 0.9F

                Dim glowPeak As SKColor =
                    ColorMath.WithAlpha(
                        ColorMath.MixLinear(spec.BodyGlow, spec.FillTop, 0.6F),
                        CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(spec.BodyGlow.Alpha * 0.32F))))))

                Using sh As SKShader = SKShader.CreateRadialGradient(
                    New SKPoint(cx, cy),
                    rad,
                    New SKColor() {glowPeak, SKColors.Transparent},
                    New Single() {0.0F, 1.0F},
                    SKShaderTileMode.Clamp)

                    PrepareFillPaint()
                    _fillPaint.Shader = sh
                    canvas.DrawPath(_roundRectPath, _fillPaint)
                    _fillPaint.Shader = Nothing
                End Using
            End If

            '========================================================
            ' 3) MATERIAL WASH
            '========================================================
            If spec.MaterialWash.Alpha <> 0 Then
                Using sh As SKShader = SKShader.CreateLinearGradient(
                    New SKPoint(baseRect.Left, baseRect.Top),
                    New SKPoint(baseRect.Left, baseRect.Bottom),
                    New SKColor() {
                        ColorMath.WithAlpha(spec.MaterialWash, CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(spec.MaterialWash.Alpha * 0.1F)))))),
                        ColorMath.WithAlpha(spec.MaterialWash, CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(spec.MaterialWash.Alpha * 0.06F)))))),
                        SKColors.Transparent
                    },
                    New Single() {0.0F, 0.5F, 1.0F},
                    SKShaderTileMode.Clamp)

                    PrepareFillPaint()
                    _fillPaint.Shader = sh
                    canvas.DrawPath(_roundRectPath, _fillPaint)
                    _fillPaint.Shader = Nothing
                End Using
            End If

            '========================================================
            ' 4) TOP VEIL
            '========================================================
            If spec.TopSheen.Alpha <> 0 Then
                Dim h As Single = Math.Max(2.0F * dpi, baseRect.Height * 0.2F)

                Dim rect As New SKRect(
                    baseRect.Left,
                    baseRect.Top,
                    baseRect.Right,
                    baseRect.Top + h)

                Dim topCol As SKColor =
                    ColorMath.WithAlpha(
                        ColorMath.MixLinear(spec.TopSheen, spec.FillTop, 0.68F),
                        CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(spec.TopSheen.Alpha * 0.12F))))))

                Using sh As SKShader = SKShader.CreateLinearGradient(
                    New SKPoint(rect.Left, rect.Top),
                    New SKPoint(rect.Left, rect.Bottom),
                    New SKColor() {topCol, SKColors.Transparent},
                    New Single() {0.0F, 1.0F},
                    SKShaderTileMode.Clamp)

                    PrepareFillPaint()
                    _fillPaint.Shader = sh
                    canvas.DrawRect(rect, _fillPaint)
                    _fillPaint.Shader = Nothing
                End Using
            End If

            '========================================================
            ' 5) TOP LIFT
            '========================================================
            If spec.InnerHighlight.Alpha <> 0 Then
                Dim h As Single = baseRect.Height * 0.1F

                Using sh As SKShader = SKShader.CreateLinearGradient(
                    New SKPoint(baseRect.Left, baseRect.Top),
                    New SKPoint(baseRect.Left, baseRect.Top + h),
                    New SKColor() {
                        ColorMath.WithAlpha(
                            spec.InnerHighlight,
                            CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(spec.InnerHighlight.Alpha * 0.1F)))))),
                        SKColors.Transparent
                    },
                    New Single() {0.0F, 1.0F},
                    SKShaderTileMode.Clamp)

                    PrepareFillPaint()
                    _fillPaint.Shader = sh
                    canvas.DrawPath(_roundRectPath, _fillPaint)
                    _fillPaint.Shader = Nothing
                End Using
            End If

            '========================================================
            ' 6) BOTTOM ANCHOR
            '========================================================
            If spec.InnerShade.Alpha <> 0 Then
                Using sh As SKShader = SKShader.CreateLinearGradient(
                    New SKPoint(baseRect.Left, baseRect.Top),
                    New SKPoint(baseRect.Left, baseRect.Bottom),
                    New SKColor() {
                        SKColors.Transparent,
                        ColorMath.WithAlpha(spec.InnerShade, CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(spec.InnerShade.Alpha * 0.44F)))))),
                        ColorMath.WithAlpha(spec.InnerShade, CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(spec.InnerShade.Alpha * 0.72F))))))
                    },
                    New Single() {0.6F, 0.85F, 1.0F},
                    SKShaderTileMode.Clamp)

                    PrepareFillPaint(SKBlendMode.Multiply)
                    _fillPaint.Shader = sh
                    canvas.DrawPath(_roundRectPath, _fillPaint)
                    _fillPaint.Shader = Nothing
                    _fillPaint.BlendMode = SKBlendMode.SrcOver
                End Using
            End If

            '========================================================
            ' 7) BORDER
            '========================================================
            Dim outer As SKColor =
                ColorMath.WithAlpha(
                    spec.BorderOuter,
                    CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(spec.BorderOuter.Alpha * 0.18F))))))

            Dim inner As SKColor =
                ColorMath.WithAlpha(
                    spec.BorderInner,
                    CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(spec.BorderInner.Alpha * 0.08F))))))

            PrepareStrokePaint(Math.Max(1.0F, dpi), outer)
            canvas.DrawRoundRect(baseRect, radius, radius, _strokePaint)

            Dim innerInset As Single = Math.Max(1.0F, dpi)
            Dim innerRect As SKRect = PixelSnap.InsetAll(baseRect, innerInset)

            If innerRect.Width > 2.0F AndAlso innerRect.Height > 2.0F Then
                Dim innerRadius As Single = CornerRadii.ResolveInsetRadiusPx(radius, innerInset)

                PrepareStrokePaint(Math.Max(0.8F, dpi * 0.8F), inner)
                canvas.DrawRoundRect(innerRect, innerRadius, innerRadius, _strokePaint)
            End If
        End Sub

#End Region

#Region "State morph"

        Friend Shared Function MorphActionChromeSpecByState(baseSpec As MASActionChromeSpec,
                                                          state As MASVisualInteractionState) As MASActionChromeSpec

            Dim s As MASActionChromeSpec = baseSpec

            Select Case state

                Case MASVisualInteractionState.Hover
                    s.FillTop = ColorMath.MixLinear(s.FillTop, SKColors.White, 0.06F)
                    s.FillBottom = ColorMath.MixLinear(s.FillBottom, SKColors.White, 0.04F)

                    s.BodyGlow = ColorMath.WithAlpha(
                        s.BodyGlow,
                        CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(s.BodyGlow.Alpha * 1.25F))))))

                    s.TopSheen = ColorMath.WithAlpha(
                        s.TopSheen,
                        CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(s.TopSheen.Alpha * 1.15F))))))

                    s.InnerShade = ColorMath.WithAlpha(
                        s.InnerShade,
                        CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(s.InnerShade.Alpha * 0.75F))))))

                Case MASVisualInteractionState.Pressed
                    s.FillTop = ColorMath.MixLinear(s.FillTop, SKColors.Black, 0.08F)
                    s.FillBottom = ColorMath.MixLinear(s.FillBottom, SKColors.Black, 0.12F)

                    s.BodyGlow = ColorMath.WithAlpha(
                        s.BodyGlow,
                        CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(s.BodyGlow.Alpha * 0.6F))))))

                    s.TopSheen = ColorMath.WithAlpha(
                        s.TopSheen,
                        CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(s.TopSheen.Alpha * 0.5F))))))

                    s.InnerShade = ColorMath.WithAlpha(
                        s.InnerShade,
                        CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(s.InnerShade.Alpha * 1.4F))))))

                Case MASVisualInteractionState.Focused
                    s.FillTop = ColorMath.MixLinear(s.FillTop, SKColors.White, 0.03F)

                    s.BodyGlow = ColorMath.WithAlpha(
                        s.BodyGlow,
                        CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(s.BodyGlow.Alpha * 1.1F))))))

                Case Else
                    Return s

            End Select

            Return s
        End Function

#End Region

#Region "Bar FX"

        Friend Sub DrawBarChromeFx(canvas As SKCanvas,
                                       r As SKRect,
                                       radiusPx As Single,
                                       dpi As Single,
                                       spec As MASBarChromeFxSpec)

            If _disposed Then Return
            If canvas Is Nothing Then Return
            If r.Width <= 1 OrElse r.Height <= 1 Then Return

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

            MASVisualPrimitivesPainter.WithClipRoundRect(canvas, r, radiusPx,
                Sub()
                    Dim topBulgeH As Single = Math.Max(1.0F, spec.SpecularBandHDip * dpi)
                    Dim topBulgeRect As New SKRect(r.Left, r.Top, r.Right, r.Top + topBulgeH)

                    Using sh As SKShader = SKShader.CreateLinearGradient(
                        New SKPoint(topBulgeRect.Left, topBulgeRect.Top),
                        New SKPoint(topBulgeRect.Left, topBulgeRect.Bottom),
                        New SKColor() {
                            ColorMath.WithAlpha(spec.TopLift, spec.BulgeAlpha),
                            ColorMath.WithAlpha(spec.TopLift, CByte(Math.Max(0, spec.BulgeAlpha \ 3))),
                            SKColors.Transparent
                        },
                        New Single() {0.0F, 0.55F, 1.0F},
                        SKShaderTileMode.Clamp)

                        PrepareEffectPaint()
                        _effectPaint.Shader = sh
                        canvas.DrawRect(topBulgeRect, _effectPaint)
                        _effectPaint.Shader = Nothing
                    End Using

                    Dim specTopPad As Single = Math.Max(0.0F, spec.SpecularTopPadDip * dpi)
                    Dim specRect As New SKRect(
                        r.Left + 1.0F,
                        r.Top + specTopPad,
                        r.Right - 1.0F,
                        r.Top + specTopPad + Math.Max(1.0F, topBulgeH * 0.65F))

                    Using sh As SKShader = SKShader.CreateLinearGradient(
                        New SKPoint(specRect.Left, specRect.Top),
                        New SKPoint(specRect.Left, specRect.Bottom),
                        New SKColor() {
                            ColorMath.WithAlpha(spec.Sheen, spec.SpecularAlpha),
                            ColorMath.WithAlpha(spec.Sheen, CByte(Math.Max(0, spec.SpecularAlpha \ 4))),
                            SKColors.Transparent
                        },
                        New Single() {0.0F, 0.35F, 1.0F},
                        SKShaderTileMode.Clamp)

                        PrepareEffectPaint()
                        _effectPaint.Shader = sh
                        canvas.DrawRect(specRect, _effectPaint)
                        _effectPaint.Shader = Nothing
                    End Using
                End Sub)

            Dim shadowCasterH As Single = Math.Max(1.0F, spec.ShadowCasterHDip * dpi)

            Dim shadowRect As New SKRect(
                r.Left + 1.0F,
                r.Bottom - shadowCasterH - 1.0F,
                r.Right - 1.0F,
                r.Bottom - 1.0F)

            Dim shadowDy As Single = Math.Max(0.0F, spec.ShadowDyDip * dpi)
            Dim shadowRectOffset As New SKRect(
                shadowRect.Left,
                shadowRect.Top + shadowDy,
                shadowRect.Right,
                shadowRect.Bottom + shadowDy)

            MASShadowPainter.DrawImmediateBlurredRoundRectShadow(
                canvas:=canvas,
                rectPx:=shadowRectOffset,
                radiusPx:=Math.Max(0.0F, radiusPx - 1.0F),
                sigmaPx:=Math.Max(0.01F, spec.ShadowSigmaDip * dpi),
                color:=ColorMath.WithAlpha(spec.ShadowBase, spec.ShadowAlpha))

            PrepareStrokePaint(Math.Max(1.0F, dpi * 0.9F), ColorMath.WithAlpha(spec.InnerHighlightBase, 18))
            Dim innerRect As SKRect = PixelSnap.InsetAll(r, Math.Max(1.0F, dpi))
            If innerRect.Width > 2.0F AndAlso innerRect.Height > 2.0F Then
                Dim inset As Single = Math.Max(1.0F, dpi)
                Dim innerRadius As Single = CornerRadii.ResolveInsetRadiusPx(radiusPx, inset)

                canvas.DrawRoundRect(
                    innerRect,
                    innerRadius,
                    innerRadius,
                    _strokePaint)
            End If

            PrepareStrokePaint(Math.Max(1.0F, dpi), ColorMath.WithAlpha(spec.InnerShadowBase, 22))
            canvas.DrawRoundRect(r, radiusPx, radiusPx, _strokePaint)
        End Sub

#End Region

#Region "Reusable paint state"

        Private Sub PrepareFillPaint(Optional blendMode As SKBlendMode = SKBlendMode.SrcOver)
            _fillPaint.IsAntialias = True
            _fillPaint.IsDither = True
            _fillPaint.Style = SKPaintStyle.Fill
            _fillPaint.Color = SKColors.White
            _fillPaint.Shader = Nothing
            _fillPaint.ImageFilter = Nothing
            _fillPaint.BlendMode = blendMode
        End Sub

        Private Sub PrepareEffectPaint(Optional blendMode As SKBlendMode = SKBlendMode.SrcOver)
            _effectPaint.IsAntialias = True
            _effectPaint.IsDither = True
            _effectPaint.Style = SKPaintStyle.Fill
            _effectPaint.Color = SKColors.White
            _effectPaint.Shader = Nothing
            _effectPaint.ImageFilter = Nothing
            _effectPaint.BlendMode = blendMode
        End Sub

        Private Sub PrepareStrokePaint(strokeWidth As Single, color As SKColor)
            _strokePaint.IsAntialias = True
            _strokePaint.IsDither = True
            _strokePaint.Style = SKPaintStyle.Stroke
            _strokePaint.StrokeJoin = SKStrokeJoin.Round
            _strokePaint.StrokeCap = SKStrokeCap.Round
            _strokePaint.StrokeWidth = Math.Max(0.1F, strokeWidth)
            _strokePaint.Color = color
            _strokePaint.Shader = Nothing
            _strokePaint.ImageFilter = Nothing
            _strokePaint.BlendMode = SKBlendMode.SrcOver
        End Sub

        Private Shared Sub SafeDispose(resource As IDisposable)
            If resource Is Nothing Then Return

            Try
                resource.Dispose()
            Catch masCaughtException1 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(masCaughtException1)
            End Try
        End Sub

#End Region

    End Class

End Namespace
