Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.Globalization
Imports System.Windows.Forms
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Composition
Imports Nexamas.UI.Controls
Imports Nexamas.UI.General
Imports Nexamas.UI.Layout
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.TextRendering
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.Components

    Partial Public NotInheritable Class MASDataForm

#Region "Rendering"

        Protected Overrides Sub Render(canvas As SKCanvas,
                                       ctx As MASThemeContext,
                                       pixelBounds As SKRect)
            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If pixelBounds.Width <= 1.0F OrElse pixelBounds.Height <= 1.0F Then Return

            _contract.AssertConsumable()

            Dim dpi As Single = PixelSnap.SafeDpi(ctx.Dpi)
            Dim bounds As SKRect = PixelSnap.SnapRect(pixelBounds, dpi)
            Dim responsiveProfile As MASResponsiveLayoutProfile = MASResponsiveLayoutResolver.FromTheme(ctx, bounds, SizeIntent, MASDataFormLayoutPlan.Thresholds)
            Dim plan As MASDataFormLayoutPlan = MASDataFormLayoutPlan.Create(responsiveProfile, _compact)
            Dim radius As Single = PixelSnap.SafeRadius(plan.RadiusPx)

            DrawSurface(canvas, ctx, bounds, radius, dpi)

            Dim content As New SKRect(bounds.Left + plan.PaddingHorizontalPx,
                                      bounds.Top + plan.PaddingVerticalPx,
                                      bounds.Right - plan.PaddingHorizontalPx,
                                      bounds.Bottom - plan.PaddingVerticalPx)
            If content.Width <= 1.0F OrElse content.Height <= 1.0F Then Return

            Dim y As Single = DrawHeader(canvas, ctx, content, dpi, plan)
            DrawRows(canvas, ctx, New SKRect(content.Left, y, content.Right, content.Bottom), dpi, ResolveRightToLeft(), plan)
        End Sub

        Private Sub DrawSurface(canvas As SKCanvas,
                                ctx As MASThemeContext,
                                bounds As SKRect,
                                radius As Single,
                                dpi As Single)
            Dim surfaceMaterialDrawn As Boolean = MASSurfaceMaterialComponentGateway.TryDrawCardViewportSurface(
                canvas:=canvas,
                ctx:=ctx,
                boundsPx:=bounds,
                materialKey:=MASSurfaceMaterialIds.Auto,
                dpi:=dpi,
                cornerRadiusPx:=radius,
                surfaceStrengthLevel:=3,
                borderStrengthLevel:=2,
                frameStrength:=MASSurfaceFrameStrength.Level3)

            If surfaceMaterialDrawn Then Return

            Using paint As New SKPaint With {
                .IsAntialias = True,
                .Style = SKPaintStyle.Fill,
                .Color = ResolveSurfaceColor(ctx).WithAlpha(DataFormTokens.SurfaceAlpha)
            }
                canvas.DrawRoundRect(bounds, radius, radius, paint)
            End Using

            Using stroke As New SKPaint With {
                .IsAntialias = True,
                .Style = SKPaintStyle.Stroke,
                .StrokeWidth = Math.Max(1.0F, DataFormTokens.BorderStrokeDip * dpi),
                .Color = ResolveAccentColor(ctx).WithAlpha(DataFormTokens.BorderAlpha)
            }
                canvas.DrawRoundRect(bounds, radius, radius, stroke)
            End Using
        End Sub

        Private Function DrawHeader(canvas As SKCanvas,
                                    ctx As MASThemeContext,
                                    content As SKRect,
                                    dpi As Single,
                                    plan As MASDataFormLayoutPlan) As Single
            Dim isRtl As Boolean = ResolveRightToLeft()
            Dim textAlign As TypographyTextPrimitives.TextAlign = If(isRtl, TypographyTextPrimitives.TextAlign.Right, TypographyTextPrimitives.TextAlign.Left)
            Dim titleRect As New SKRect(content.Left, content.Top, content.Right, content.Top + plan.HeaderTitleHeightPx)
            TypographyTextPrimitives.DrawSingleLineText(
                canvas:=canvas,
                ctx:=ctx,
                text:=_title,
                bounds:=titleRect,
                dpi:=dpi,
                style:=MASTypography.MASTextStyle.Title,
                color:=ResolvePrimaryTextColor(ctx).WithAlpha(DataFormTokens.TextAlpha),
                align:=textAlign,
                drawShadow:=False)

            Dim y As Single = titleRect.Bottom
            If _description.Length > 0 AndAlso plan.ShowDescription Then
                y += plan.HeaderGapPx
                Dim descriptionRect As New SKRect(content.Left, y, content.Right, y + plan.HeaderDescriptionHeightPx)
                TypographyTextPrimitives.DrawSingleLineText(
                    canvas:=canvas,
                    ctx:=ctx,
                    text:=_description,
                    bounds:=descriptionRect,
                    dpi:=dpi,
                    style:=MASTypography.MASTextStyle.Small,
                    color:=ResolveMutedTextColor(ctx).WithAlpha(DataFormTokens.MutedTextAlpha),
                    align:=textAlign,
                    drawShadow:=False)
                y = descriptionRect.Bottom
            End If

            If plan.ShowSummary Then
                y += plan.SummaryGapPx
                Dim summaryRect As New SKRect(content.Left, y, content.Right, y + plan.SummaryHeightPx)
                TypographyTextPrimitives.DrawSingleLineText(
                    canvas:=canvas,
                    ctx:=ctx,
                    text:=BuildFormSummaryText(),
                    bounds:=summaryRect,
                    dpi:=dpi,
                    style:=MASTypography.MASTextStyle.Micro,
                    color:=ResolveMutedTextColor(ctx).WithAlpha(DataFormTokens.MutedTextAlpha),
                    align:=textAlign,
                    drawShadow:=False)
                y = summaryRect.Bottom
            End If

            Return y + plan.HeaderBottomGapPx
        End Function

        Private Sub DrawRows(canvas As SKCanvas,
                             ctx As MASThemeContext,
                             rowsBounds As SKRect,
                             dpi As Single,
                             isRtl As Boolean,
                             plan As MASDataFormLayoutPlan)
            Dim rowHeight As Single = plan.RowHeightPx
            Dim y As Single = rowsBounds.Top
            Dim rowRects As New List(Of SKRect)()

            If _fields.Count = 0 Then
                _lastFieldRects = Array.Empty(Of SKRect)()
                Dim emptyRect As New SKRect(rowsBounds.Left, y, rowsBounds.Right, Math.Min(rowsBounds.Bottom, y + rowHeight))
                DrawFieldRow(canvas, ctx, emptyRect, Nothing, 0, dpi, isRtl, plan)
                Return
            End If

            For i As Integer = 0 To _fields.Count - 1
                If y + rowHeight > rowsBounds.Bottom + 0.5F Then Exit For
                Dim rowRect As New SKRect(rowsBounds.Left, y, rowsBounds.Right, y + rowHeight)
                DrawFieldRow(canvas, ctx, rowRect, _fields(i), i, dpi, isRtl, plan)
                rowRects.Add(rowRect)
                y = rowRect.Bottom + plan.RowGapPx
            Next

            _lastFieldRects = rowRects.ToArray()
        End Sub

        Private Sub DrawFieldRow(canvas As SKCanvas,
                                 ctx As MASThemeContext,
                                 rowRect As SKRect,
                                 field As FieldEntry,
                                 rowIndex As Integer,
                                 dpi As Single,
                                 isRtl As Boolean,
                                 plan As MASDataFormLayoutPlan)
            If rowRect.Width <= 1.0F OrElse rowRect.Height <= 1.0F Then Return
            Dim snapped As SKRect = PixelSnap.SnapRect(rowRect, dpi)
            Dim radius As Single = PixelSnap.SafeRadius(plan.RowRadiusPx)
            Dim focused As Boolean = field IsNot Nothing AndAlso rowIndex = _focusedFieldIndex AndAlso HasKeyboardFocus
            Dim fillAlpha As Byte = If(rowIndex Mod 2 = 0, DataFormTokens.RowFillAlpha, DataFormTokens.RowAltFillAlpha)
            If focused Then fillAlpha = CByte(Math.Max(CInt(fillAlpha), CInt(DataFormTokens.RowFocusedFillAlpha)))

            Using paint As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = ResolveSurfaceColor(ctx).WithAlpha(fillAlpha)}
                canvas.DrawRoundRect(snapped, radius, radius, paint)
            End Using

            If focused Then
                Using focusPaint As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Stroke, .StrokeWidth = Math.Max(1.0F, DataFormTokens.RowFocusStrokeDip * dpi), .Color = ResolveAccentColor(ctx).WithAlpha(DataFormTokens.RowFocusAlpha)}
                    canvas.DrawRoundRect(snapped, radius, radius, focusPaint)
                End Using
            End If

            If field Is Nothing Then
                DrawEmptyRow(canvas, ctx, snapped, dpi)
                Return
            End If

            Dim statusColor As SKColor = ResolveStateColor(ctx, field.State)
            Dim statusRect As SKRect
            If isRtl Then
                statusRect = New SKRect(snapped.Right - 4.0F * dpi, snapped.Top + 9.0F * dpi, snapped.Right - 1.5F * dpi, snapped.Bottom - 9.0F * dpi)
            Else
                statusRect = New SKRect(snapped.Left + 1.5F * dpi, snapped.Top + 9.0F * dpi, snapped.Left + 4.0F * dpi, snapped.Bottom - 9.0F * dpi)
            End If

            Using statusPaint As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = statusColor.WithAlpha(DataFormTokens.StatusAlpha)}
                canvas.DrawRoundRect(PixelSnap.SnapRect(statusRect, dpi), DataFormTokens.StatusRadiusDip * dpi, DataFormTokens.StatusRadiusDip * dpi, statusPaint)
            End Using

            Dim inner As New SKRect(snapped.Left + plan.RowPaddingHorizontalPx,
                                    snapped.Top + plan.RowPaddingVerticalPx,
                                    snapped.Right - plan.RowPaddingHorizontalPx,
                                    snapped.Bottom - plan.RowPaddingVerticalPx)
            Dim labelWidth As Single = Math.Min(plan.LabelColumnWidthPx, inner.Width * 0.42F)
            Dim labelRect As SKRect
            Dim valueRect As SKRect
            If plan.StackLabelAndValue Then
                labelRect = New SKRect(inner.Left, inner.Top, inner.Right, inner.Top + Math.Min(18.0F * dpi, inner.Height * 0.42F))
                valueRect = New SKRect(inner.Left, labelRect.Bottom + 2.0F * dpi, inner.Right, inner.Bottom)
            ElseIf isRtl Then
                labelRect = New SKRect(inner.Right - labelWidth, inner.Top, inner.Right, inner.Bottom)
                valueRect = New SKRect(inner.Left, inner.Top, labelRect.Left - 10.0F * dpi, inner.Bottom)
            Else
                labelRect = New SKRect(inner.Left, inner.Top, inner.Left + labelWidth, inner.Bottom)
                valueRect = New SKRect(labelRect.Right + 10.0F * dpi, inner.Top, inner.Right, inner.Bottom)
            End If

            Dim labelText As String = field.Label & If(field.Required, " *", String.Empty)
            TypographyTextPrimitives.DrawSingleLineText(
                canvas:=canvas,
                ctx:=ctx,
                text:=labelText,
                bounds:=labelRect,
                dpi:=dpi,
                style:=MASTypography.MASTextStyle.Small,
                color:=ResolveMutedTextColor(ctx).WithAlpha(If(field.State = MASDataFormFieldState.Disabled, DataFormTokens.DisabledTextAlpha, DataFormTokens.MutedTextAlpha)),
                align:=If(isRtl, TypographyTextPrimitives.TextAlign.Right, TypographyTextPrimitives.TextAlign.Left),
                drawShadow:=False)

            Dim valueTop As Single = valueRect.Top
            Dim valueHeight As Single = If(field.HelperText.Length > 0 AndAlso plan.ShowHelperText, valueRect.Height - plan.HelperHeightPx, valueRect.Height)
            Dim mainValueRect As New SKRect(valueRect.Left, valueTop, valueRect.Right, valueTop + Math.Max(12.0F * dpi, valueHeight))
            Dim valueText As String = If(field.ValueText.Length = 0, ResolveKindEmptyText(field.Kind), field.ValueText)
            TypographyTextPrimitives.DrawSingleLineText(
                canvas:=canvas,
                ctx:=ctx,
                text:=valueText,
                bounds:=mainValueRect,
                dpi:=dpi,
                style:=MASTypography.MASTextStyle.Body,
                color:=ResolveValueColor(ctx, field.State),
                align:=If(isRtl, TypographyTextPrimitives.TextAlign.Right, TypographyTextPrimitives.TextAlign.Left),
                drawShadow:=False)

            If field.HelperText.Length > 0 AndAlso plan.ShowHelperText Then
                Dim helperRect As New SKRect(valueRect.Left, mainValueRect.Bottom, valueRect.Right, valueRect.Bottom)
                TypographyTextPrimitives.DrawSingleLineText(
                    canvas:=canvas,
                    ctx:=ctx,
                    text:=field.HelperText,
                    bounds:=helperRect,
                    dpi:=dpi,
                    style:=MASTypography.MASTextStyle.Micro,
                    color:=ResolveStateColor(ctx, field.State).WithAlpha(DataFormTokens.MutedTextAlpha),
                    align:=If(isRtl, TypographyTextPrimitives.TextAlign.Right, TypographyTextPrimitives.TextAlign.Left),
                    drawShadow:=False)
            End If
        End Sub

        Private Sub DrawEmptyRow(canvas As SKCanvas,
                                 ctx As MASThemeContext,
                                 rowRect As SKRect,
                                 dpi As Single)
            TypographyTextPrimitives.DrawSingleLineText(
                canvas:=canvas,
                ctx:=ctx,
                text:=DataFormTokens.EmptyText,
                bounds:=rowRect,
                dpi:=dpi,
                style:=MASTypography.MASTextStyle.Body,
                color:=ResolveMutedTextColor(ctx).WithAlpha(DataFormTokens.MutedTextAlpha),
                align:=TypographyTextPrimitives.TextAlign.Center,
                drawShadow:=False)
        End Sub

#End Region

    End Class

End Namespace
