Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Composition
Imports Nexamas.UI.General
Imports Nexamas.UI.Icons
Imports Nexamas.UI.Layout
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.TextInput
Imports Nexamas.UI.TextRendering
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.Controls

    Partial Public NotInheritable Class MASStatusBanner

#Region "Rendering / content"

        Private Sub DrawContentPlate(canvas As SKCanvas,
                                     ctx As MASThemeContext,
                                     surface As StatusBannerSurfaceGeometry,
                                     layout As StatusBannerChromeLayout,
                                     toneColor As SKColor)
            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If Not surface.HasValue Then Return
            If Not layout.HasContentPlate Then Return
            If layout.ContentPlateRect.Width <= 2.0F OrElse layout.ContentPlateRect.Height <= 2.0F Then Return

            Dim plateEdge As Single = If(layout.IsRtl, layout.ContentPlateRect.Right, layout.ContentPlateRect.Left)
            plateEdge = PixelSnap.Snap(plateEdge, surface.Dpi)

            Dim materialRect As SKRect
            If layout.IsRtl Then
                materialRect = New SKRect(
                    surface.Bounds.Left,
                    surface.Bounds.Top,
                    Math.Min(surface.Bounds.Right, Math.Max(surface.Bounds.Left, plateEdge)),
                    surface.Bounds.Bottom)
            Else
                materialRect = New SKRect(
                    Math.Max(surface.Bounds.Left, Math.Min(surface.Bounds.Right, plateEdge)),
                    surface.Bounds.Top,
                    surface.Bounds.Right,
                    surface.Bounds.Bottom)
            End If

            materialRect = PixelSnap.SnapRect(materialRect, surface.Dpi)
            If materialRect.Width <= 2.0F OrElse materialRect.Height <= 2.0F Then Return

            Dim fillTop As SKColor = If(ctx.SurfaceTheme IsNot Nothing, ctx.SurfaceTheme.SurfaceTop, SKColors.White)
            Dim fillBottom As SKColor = If(ctx.SurfaceTheme IsNot Nothing, ctx.SurfaceTheme.SurfaceBot, New SKColor(245, 248, 250, 255))
            fillTop = ColorMath.Mix(fillTop, SKColors.White, 0.28F).WithAlpha(StatusBannerTokens.ContentSurfaceFillAlpha)
            fillBottom = ColorMath.Mix(fillBottom, SKColors.White, 0.12F).WithAlpha(StatusBannerTokens.ContentSurfaceFillAlpha)

            Dim save As Integer = canvas.Save()
            Try
                Using clip As SKPath = BuildOuterCardPath(surface.Bounds, surface.Radius)
                    canvas.ClipPath(clip, SKClipOperation.Intersect, True)
                End Using

                Using materialPath As SKPath = BuildContentMaterialPath(materialRect, surface.Radius, layout.IsRtl)
                    Using shader As SKShader = SKShader.CreateLinearGradient(
                        New SKPoint(materialRect.Left, materialRect.Top),
                        New SKPoint(materialRect.Left, materialRect.Bottom),
                        New SKColor() {fillTop, fillBottom},
                        Nothing,
                        SKShaderTileMode.Clamp)

                        Using fill As New SKPaint With {
                            .IsAntialias = True,
                            .Style = SKPaintStyle.Fill,
                            .Shader = shader
                        }
                            canvas.DrawPath(materialPath, fill)
                        End Using
                    End Using
                End Using
            Finally
                canvas.RestoreToCount(save)
            End Try
        End Sub

        Private Sub DrawStatusBannerFrame(canvas As SKCanvas,
                                          ctx As MASThemeContext,
                                          surface As StatusBannerSurfaceGeometry)
            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If Not surface.HasValue Then Return

            Dim borderSpec As MASSurfaceFrameBorderSpec =
                MASCardSecondaryFrameBorderResolver.Resolve(
                    ctx:=ctx,
                    strength:=MASSurfaceFrameStrength.Level6)

            borderSpec = ScaleStatusBannerBorderSpec(borderSpec, surface.Dpi)

            _frameBorder.Draw(
                canvas:=canvas,
                baseRect:=surface.Bounds,
                baseRadiusPx:=surface.Radius,
                dpi:=surface.Dpi,
                spec:=borderSpec)
        End Sub

        Private Shared Function ScaleStatusBannerBorderSpec(spec As MASSurfaceFrameBorderSpec,
                                                            dpi As Single) As MASSurfaceFrameBorderSpec
            Dim safeDpi As Single = PixelSnap.SafeDpi(dpi)
            spec.OuterStrokePx = Math.Max(0.55F, spec.OuterStrokePx * safeDpi)
            spec.InnerStrokePx = Math.Max(0.5F, spec.InnerStrokePx * safeDpi)
            spec.HairlineStrokePx = Math.Max(0.6F, spec.HairlineStrokePx * safeDpi)
            spec.GlowStrokePx = Math.Max(0.55F, spec.GlowStrokePx * safeDpi)
            spec.InnerInsetPx = Math.Max(0.5F, spec.InnerInsetPx * safeDpi)
            spec.GlowBlurSigmaPx = Math.Max(0.0F, spec.GlowBlurSigmaPx * safeDpi)
            Return spec
        End Function

        Private Shared Function BuildContentMaterialPath(materialRect As SKRect,
                                                         radius As Single,
                                                         isRtl As Boolean) As SKPath
            Dim path As New SKPath()
            Dim rad As Single = PixelSnap.SafeRadius(Math.Min(radius, materialRect.Height / 2.0F))
            Dim rr As New SKRoundRect()

            If isRtl Then
                rr.SetRectRadii(materialRect, {
                    New SKPoint(0.0F, 0.0F),
                    New SKPoint(rad, rad),
                    New SKPoint(rad, rad),
                    New SKPoint(0.0F, 0.0F)
                })
            Else
                rr.SetRectRadii(materialRect, {
                    New SKPoint(rad, rad),
                    New SKPoint(0.0F, 0.0F),
                    New SKPoint(0.0F, 0.0F),
                    New SKPoint(rad, rad)
                })
            End If

            path.AddRoundRect(rr)
            Return path
        End Function

        Private Shared Function BuildOuterCardPath(bounds As SKRect,
                                                   radius As Single) As SKPath
            Dim path As New SKPath()
            path.AddRoundRect(bounds, radius, radius)
            Return path
        End Function

        Private Sub DrawContent(canvas As SKCanvas,
                                ctx As MASThemeContext,
                                layout As StatusBannerChromeLayout,
                                toneColor As SKColor,
                                titleText As String,
                                messageText As String,
                                isRtl As Boolean,
                                dpi As Single)
            If layout.HasBadge Then
                DrawIcon(canvas, layout.IconBadgeRect, toneColor, _tone, dpi)
            End If

            If layout.TextRect.Width <= 1.0F OrElse layout.TextRect.Height <= 1.0F Then Return
            DrawTextBlock(canvas, ctx, titleText, messageText, layout.TextRect, toneColor, isRtl, dpi)
        End Sub

        Private Sub DrawTextBlock(canvas As SKCanvas,
                                  ctx As MASThemeContext,
                                  titleText As String,
                                  messageText As String,
                                  bounds As SKRect,
                                  toneColor As SKColor,
                                  isRtl As Boolean,
                                  dpi As Single)
            If canvas Is Nothing OrElse ctx Is Nothing OrElse ctx.Typography Is Nothing Then Return
            If bounds.Width <= 2.0F OrElse bounds.Height <= 2.0F Then Return

            Dim resolvedTitleColor As SKColor = ColorMath.Mix(MASStatusBannerVisualPolicy.ResolveTitleColor(ctx), toneColor, 0.36F).WithAlpha(StatusBannerTokens.TitleTextAlpha)
            Dim titlePaint As SKPaint = ctx.Typography.GetPaint(MASTypography.MASTextStyle.Small, resolvedTitleColor)
            Dim messagePaint As SKPaint = ctx.Typography.GetPaint(MASTypography.MASTextStyle.Body, MASStatusBannerVisualPolicy.ResolveMessageColor(ctx))
            If titlePaint Is Nothing OrElse messagePaint Is Nothing Then Return

            Dim titleLines As List(Of String) = MASStatusBannerVisualPolicy.BuildWrappedLines(titleText, titlePaint, bounds.Width, 1)
            Dim messageLines As List(Of String) = MASStatusBannerVisualPolicy.BuildWrappedLines(messageText, messagePaint, bounds.Width, MASStatusBannerVisualPolicy.MaxMessageLines)
            If titleLines.Count = 0 Then titleLines.Add(titleText)
            If messageLines.Count = 0 Then messageLines.Add(messageText)

            Dim titleLineHeight As Single = Math.Max(StatusBannerTokens.TitleLineHeightDip * dpi, titlePaint.TextSize * MASTypography.WrapLineHeightMul)
            Dim messageLineHeight As Single = Math.Max(StatusBannerTokens.MessageLineHeightDip * dpi, messagePaint.TextSize * MASTypography.WrapLineHeightMul)
            Dim gap As Single = StatusBannerTokens.TitleMessageGapDip * dpi
            Dim totalHeight As Single = titleLineHeight + gap + messageLineHeight * Math.Max(1, messageLines.Count)
            Dim y As Single = bounds.MidY - totalHeight / 2.0F

            DrawLines(canvas, titleLines, titlePaint, bounds, y, titleLineHeight, isRtl)
            y += titleLineHeight + gap
            DrawLines(canvas, messageLines, messagePaint, bounds, y, messageLineHeight, isRtl)
        End Sub

        Private Shared Sub DrawLines(canvas As SKCanvas,
                                     lines As List(Of String),
                                     paint As SKPaint,
                                     bounds As SKRect,
                                     top As Single,
                                     lineHeight As Single,
                                     isRtl As Boolean)
            If canvas Is Nothing OrElse lines Is Nothing OrElse paint Is Nothing Then Return
            Dim baseline As Single = top - paint.FontMetrics.Ascent

            For Each line As String In lines
                If baseline > bounds.Bottom + lineHeight Then Exit For
                If Not String.IsNullOrEmpty(line) Then
                    Dim x As Single = bounds.Left
                    If isRtl Then x = bounds.Right - MASShapedText.MeasureWidth(line, paint)
                    MASShapedText.Draw(canvas, line, MASTextCrispRendering.SnapTextX(x), MASTextCrispRendering.SnapBaseline(baseline), paint)
                End If
                baseline += lineHeight
            Next
        End Sub

        Private Shared Sub DrawIcon(canvas As SKCanvas,
                                    bounds As SKRect,
                                    color As SKColor,
                                    tone As MASToastTone,
                                    dpi As Single)
            If canvas Is Nothing Then Return
            If bounds.Width <= 1.0F OrElse bounds.Height <= 1.0F Then Return

            Dim shadowRect As SKRect = bounds
            shadowRect.Offset(0.0F, Math.Max(1.0F, 1.5F * dpi))
            MASShadowPainter.DrawFlatOvalShadow(
                canvas:=canvas,
                rectPx:=shadowRect,
                color:=SKColors.Black.WithAlpha(StatusBannerTokens.IconBadgeShadowAlpha))

            Using badgeFill As New SKPaint With {
                .IsAntialias = True,
                .Style = SKPaintStyle.Fill,
                .Color = SKColors.White.WithAlpha(StatusBannerTokens.IconBadgeFillAlpha)
            }
                canvas.DrawOval(bounds, badgeFill)
            End Using

            Using badgeBorder As New SKPaint With {
                .IsAntialias = True,
                .Style = SKPaintStyle.Stroke,
                .StrokeWidth = Math.Max(1.0F, dpi),
                .Color = color.WithAlpha(StatusBannerTokens.IconBadgeBorderAlpha)
            }
                canvas.DrawOval(bounds, badgeBorder)
            End Using

            Dim innerSize As Single = Math.Min(bounds.Width, bounds.Height) * 0.58F
            Dim innerRect As New SKRect(
                bounds.MidX - innerSize / 2.0F,
                bounds.MidY - innerSize / 2.0F,
                bounds.MidX + innerSize / 2.0F,
                bounds.MidY + innerSize / 2.0F)

            Using fill As New SKPaint With {
                .IsAntialias = True,
                .Style = SKPaintStyle.Fill,
                .Color = color.WithAlpha(StatusBannerTokens.IconFillAlpha)
            }
                canvas.DrawOval(innerRect, fill)
            End Using

            Dim iconRect As New SKRect(
                innerRect.Left + innerRect.Width * 0.2F,
                innerRect.Top + innerRect.Height * 0.2F,
                innerRect.Right - innerRect.Width * 0.2F,
                innerRect.Bottom - innerRect.Height * 0.2F)

            MASIconPainter.Draw(
                canvas:=canvas,
                bounds:=iconRect,
                dpi:=PixelSnap.SafeDpi(dpi),
                kind:=MASStatusBannerVisualPolicy.ResolveIconKind(tone),
                color:=SKColors.White.WithAlpha(StatusBannerTokens.IconGlyphAlpha),
                style:=MASIconStyle.Outline)
        End Sub

#End Region


    End Class

End Namespace
