Option Strict On
Option Explicit On

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

Namespace Nexamas.UI.Rendering

    Partial Friend NotInheritable Class MASShadowPainter
        Private Sub RebuildOuter(w As Integer,
                                 h As Integer,
                                 radiusPx As Single,
                                 dpi As Single,
                                 shadowBase As SKColor,
                                 s As MASShadowSpec)

            SafeDisposeImage(_img)

            Dim sigma As Single = s.SigmaDip * dpi
            Dim offsetY As Single = s.OffsetYDip * dpi
            Dim alpha As Byte = s.Alpha

            Dim pad As Integer =
                Math.Max(
                    s.PadMinPx,
                    CInt(Math.Ceiling(sigma * s.PadSigmaMul)) + 3)

            Dim imgW As Integer = w + pad * 2
            Dim imgH As Integer =
                h + pad * 2 + CInt(Math.Ceiling(Math.Max(0.0F, offsetY))) + 3

            Using surface = SKSurface.Create(New SKImageInfo(imgW, imgH))
                Dim c As SKCanvas = surface.Canvas
                c.Clear(Values.Color.Core.Transparent)

                _buildFill.BlendMode = SKBlendMode.SrcOver
                _buildFill.Color = New SKColor(shadowBase.Red, shadowBase.Green, shadowBase.Blue, alpha)
                _buildFill.MaskFilter = SKMaskFilter.CreateBlur(SKBlurStyle.Normal, sigma)

                Dim rr As SKRect =
                    PixelSnap.SnapRectHalf(
                        New SKRect(pad, pad + offsetY, pad + w, pad + offsetY + h))

                c.DrawRoundRect(rr, radiusPx, radiusPx, _buildFill)

                _buildFill.MaskFilter = Nothing
                _buildFill.BlendMode = SKBlendMode.SrcOver
                _img = surface.Snapshot()
            End Using

            _pad = pad
            _offsetY = 0
        End Sub

        Private Sub EnsureLayeredCache(r As SKRect,
                                       radiusPx As Single,
                                       dpi As Single,
                                       shadowBase As SKColor,
                                       spec As MASLayeredShadowSpec)

            Dim w As Integer = Math.Max(1, CInt(Math.Round(r.Width)))
            Dim h As Integer = Math.Max(1, CInt(Math.Round(r.Height)))
            Dim dpi100 As Integer = CInt(Math.Round(dpi * 100.0F))
            Dim rad100 As Integer = CInt(Math.Round(radiusPx * 100.0F))

            Dim sigA As Single = spec.SigmaA_Dip * dpi
            Dim offA As Single = spec.OffY_A_Dip * dpi
            Dim sigB As Single = spec.SigmaB_Dip * dpi
            Dim offB As Single = spec.OffY_B_Dip * dpi

            Dim sigA100 As Integer = CInt(Math.Round(sigA * 100.0F))
            Dim offA100 As Integer = CInt(Math.Round(offA * 100.0F))
            Dim sigB100 As Integer = CInt(Math.Round(sigB * 100.0F))
            Dim offB100 As Integer = CInt(Math.Round(offB * 100.0F))

            Dim need As Boolean =
                (_layerImg Is Nothing) OrElse
                (w <> _layerLastW) OrElse
                (h <> _layerLastH) OrElse
                (dpi100 <> _layerLastDpi100) OrElse
                (rad100 <> _layerLastRadius100) OrElse
                (_layerLastBase <> shadowBase) OrElse
                (_layerLastSigA100 <> sigA100) OrElse
                (_layerLastOffA100 <> offA100) OrElse
                (_layerLastAlA <> CInt(spec.AlphaA)) OrElse
                (_layerLastSigB100 <> sigB100) OrElse
                (_layerLastOffB100 <> offB100) OrElse
                (_layerLastAlB <> CInt(spec.AlphaB))

            If Not need Then Return

            RebuildLayered(w, h, radiusPx, dpi, shadowBase, spec)

            _layerLastW = w
            _layerLastH = h
            _layerLastDpi100 = dpi100
            _layerLastRadius100 = rad100
            _layerLastBase = shadowBase
            _layerLastSigA100 = sigA100
            _layerLastOffA100 = offA100
            _layerLastAlA = CInt(spec.AlphaA)
            _layerLastSigB100 = sigB100
            _layerLastOffB100 = offB100
            _layerLastAlB = CInt(spec.AlphaB)
        End Sub

        Private Sub RebuildLayered(w As Integer,
                                   h As Integer,
                                   radiusPx As Single,
                                   dpi As Single,
                                   shadowBase As SKColor,
                                   s As MASLayeredShadowSpec)

            SafeDisposeImage(_layerImg)

            Dim sigA As Single = s.SigmaA_Dip * dpi
            Dim offA As Single = s.OffY_A_Dip * dpi
            Dim sigB As Single = s.SigmaB_Dip * dpi
            Dim offB As Single = s.OffY_B_Dip * dpi

            Dim maxSig As Single = Math.Max(sigA, sigB)
            Dim maxOff As Single = Math.Max(offA, offB)

            Dim pad As Integer =
                Math.Max(
                    s.PadMinPx,
                    CInt(Math.Ceiling(maxSig * s.PadSigmaMul)) + 3)

            Dim imgW As Integer = w + pad * 2
            Dim imgH As Integer = h + pad * 2 + CInt(Math.Ceiling(maxOff)) + 3

            Using surface = SKSurface.Create(New SKImageInfo(imgW, imgH))
                Dim c As SKCanvas = surface.Canvas
                c.Clear(Values.Color.Core.Transparent)

                If s.AlphaA <> 0 AndAlso sigA > 0.001F Then
                    _tmpFill.BlendMode = SKBlendMode.SrcOver
                    _tmpFill.Color = New SKColor(shadowBase.Red, shadowBase.Green, shadowBase.Blue, s.AlphaA)
                    _tmpFill.MaskFilter = SKMaskFilter.CreateBlur(SKBlurStyle.Normal, sigA)

                    Dim rrA As SKRect =
                        PixelSnap.SnapRectHalf(
                            New SKRect(pad, pad + offA, pad + w, pad + offA + h))

                    c.DrawRoundRect(rrA, radiusPx, radiusPx, _tmpFill)
                    _tmpFill.MaskFilter = Nothing
                End If

                If s.AlphaB <> 0 AndAlso sigB > 0.001F Then
                    _tmpFill.BlendMode = SKBlendMode.SrcOver
                    _tmpFill.Color = New SKColor(shadowBase.Red, shadowBase.Green, shadowBase.Blue, s.AlphaB)
                    _tmpFill.MaskFilter = SKMaskFilter.CreateBlur(SKBlurStyle.Normal, sigB)

                    Dim rrB As SKRect =
                        PixelSnap.SnapRectHalf(
                            New SKRect(pad, pad + offB, pad + w, pad + offB + h))

                    c.DrawRoundRect(rrB, radiusPx, radiusPx, _tmpFill)
                    _tmpFill.MaskFilter = Nothing
                End If

                _tmpFill.BlendMode = SKBlendMode.SrcOver
                _layerImg = surface.Snapshot()
            End Using

            _layerPad = pad
            _layerOffsetY = 0
        End Sub

        Private Sub EnsureCardCache(r As SKRect,
                                    radiusPx As Single,
                                    dpi As Single,
                                    shadowBase As SKColor,
                                    spec As MASCardShadowSpec)

            Dim w As Integer = Math.Max(1, CInt(Math.Round(r.Width)))
            Dim h As Integer = Math.Max(1, CInt(Math.Round(r.Height)))
            Dim dpi100 As Integer = CInt(Math.Round(dpi * 100.0F))
            Dim rad100 As Integer = CInt(Math.Round(radiusPx * 100.0F))

            Dim sigA As Single = spec.SigmaA_Dip * dpi
            Dim offA As Single = spec.OffY_A_Dip * dpi
            Dim sigB As Single = spec.SigmaB_Dip * dpi
            Dim offB As Single = spec.OffY_B_Dip * dpi

            Dim sigA100 As Integer = CInt(Math.Round(sigA * 100.0F))
            Dim offA100 As Integer = CInt(Math.Round(offA * 100.0F))
            Dim sigB100 As Integer = CInt(Math.Round(sigB * 100.0F))
            Dim offB100 As Integer = CInt(Math.Round(offB * 100.0F))

            Dim need As Boolean =
                (_cardImg Is Nothing) OrElse
                (w <> _cardLastW) OrElse
                (h <> _cardLastH) OrElse
                (dpi100 <> _cardLastDpi100) OrElse
                (rad100 <> _cardLastRadius100) OrElse
                (_cardLastBase <> shadowBase) OrElse
                (_cardLastSigA100 <> sigA100) OrElse
                (_cardLastOffA100 <> offA100) OrElse
                (_cardLastAlA <> CInt(spec.AlphaA)) OrElse
                (_cardLastSigB100 <> sigB100) OrElse
                (_cardLastOffB100 <> offB100) OrElse
                (_cardLastAlB <> CInt(spec.AlphaB))

            If Not need Then Return

            RebuildCard(w, h, radiusPx, dpi, shadowBase, spec)

            _cardLastW = w
            _cardLastH = h
            _cardLastDpi100 = dpi100
            _cardLastRadius100 = rad100
            _cardLastBase = shadowBase
            _cardLastSigA100 = sigA100
            _cardLastOffA100 = offA100
            _cardLastAlA = CInt(spec.AlphaA)
            _cardLastSigB100 = sigB100
            _cardLastOffB100 = offB100
            _cardLastAlB = CInt(spec.AlphaB)
        End Sub

        Private Sub RebuildCard(w As Integer,
                                h As Integer,
                                radiusPx As Single,
                                dpi As Single,
                                shadowBase As SKColor,
                                spec As MASCardShadowSpec)

            SafeDisposeImage(_cardImg)

            Dim sigA As Single = spec.SigmaA_Dip * dpi
            Dim offA As Single = spec.OffY_A_Dip * dpi
            Dim sigB As Single = spec.SigmaB_Dip * dpi
            Dim offB As Single = spec.OffY_B_Dip * dpi

            Dim maxSig As Single = Math.Max(sigA, sigB)
            Dim maxOff As Single = Math.Max(offA, offB)

            Dim pad As Integer =
                Math.Max(
                    spec.PadMinPx,
                    CInt(Math.Ceiling(maxSig * spec.PadSigmaMul)) + 3)

            Dim imgW As Integer = w + pad * 2
            Dim imgH As Integer = h + pad * 2 + CInt(Math.Ceiling(maxOff)) + 3

            Using surface = SKSurface.Create(New SKImageInfo(imgW, imgH))
                Dim c As SKCanvas = surface.Canvas
                c.Clear(Values.Color.Core.Transparent)

                If spec.AlphaA <> 0 AndAlso sigA > 0.001F Then
                    _tmpFill.BlendMode = SKBlendMode.SrcOver
                    _tmpFill.Color = New SKColor(shadowBase.Red, shadowBase.Green, shadowBase.Blue, spec.AlphaA)
                    _tmpFill.MaskFilter = SKMaskFilter.CreateBlur(SKBlurStyle.Normal, sigA)

                    Dim rrA As SKRect =
                        PixelSnap.SnapRectHalf(
                            New SKRect(pad, pad + offA, pad + w, pad + offA + h))

                    c.DrawRoundRect(rrA, radiusPx, radiusPx, _tmpFill)
                    _tmpFill.MaskFilter = Nothing
                End If

                If spec.AlphaB <> 0 AndAlso sigB > 0.001F Then
                    _tmpFill.BlendMode = SKBlendMode.SrcOver
                    _tmpFill.Color = New SKColor(shadowBase.Red, shadowBase.Green, shadowBase.Blue, spec.AlphaB)
                    _tmpFill.MaskFilter = SKMaskFilter.CreateBlur(SKBlurStyle.Normal, sigB)

                    Dim rrB As SKRect =
                        PixelSnap.SnapRectHalf(
                            New SKRect(pad, pad + offB, pad + w, pad + offB + h))

                    c.DrawRoundRect(rrB, radiusPx, radiusPx, _tmpFill)
                    _tmpFill.MaskFilter = Nothing
                End If

                _tmpFill.BlendMode = SKBlendMode.SrcOver
                _cardImg = surface.Snapshot()
            End Using

            _cardPad = pad
            _cardOffsetY = 0
        End Sub

    End Class

End Namespace
