Option Strict On
Option Explicit On

Imports Nexamas.UI.Architecture
Imports Nexamas.UI.General
Imports Nexamas.UI.Layout
Imports Nexamas.UI.RuntimeEnvironment
Imports Nexamas.UI.TextInput
Imports Nexamas.UI.TextRendering
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.Components

    Partial Public MustInherit Class MASInputBoxSkiaBase

#Region "Render"

        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 OrElse pixelBounds.Height <= 1 Then Return

            Dim contract As MASResolvedComponentContract = ResolveInputContract()
            If contract Is Nothing Then Return

            Dim scope As MASContractRenderScope =
    MASArchitectureRuntime.CreateRenderScopeForComponent(
        NameOf(MASInputBoxSkiaBase),
        contract,
        Me.GetType(),
        MASRenderLayer.Surface,
        MASRenderLayer.Shadow,
        MASRenderLayer.Text,
        MASRenderLayer.Focus,
        MASRenderLayer.Hover,
        MASRenderLayer.Pressed,
        MASRenderLayer.Border)

            scope.RequireAnyLayer(
    MASRenderLayer.Surface,
    MASRenderLayer.Shadow,
    MASRenderLayer.Text,
    MASRenderLayer.Focus,
    MASRenderLayer.Hover,
    MASRenderLayer.Pressed,
    MASRenderLayer.Border)

            UpdateCaretBlink()

            Dim dpi As Single = PixelSnap.SafeDpi(ctx.Dpi)

            Dim th As IMASInputTheme = ctx.InputTheme
            If th Is Nothing Then Return

            Dim hasFocus As Boolean = Me.HasKeyboardFocus
            Dim isDisabled As Boolean = Not Me.Enabled

            Dim ringM As MASSurfaceInteractionRing.Metrics = MASSurfaceInteractionRing.GetMetrics(dpi)
            Dim rBase As SKRect = MASSurfaceInteractionRing.SnapBaseRect(pixelBounds, ringM)
            If rBase.Width <= 1 OrElse rBase.Height <= 1 Then Return

            If Not isDisabled Then
                Dim app As IMASAppTheme = ctx.Theme
                If app IsNot Nothing AndAlso MASAppThemeContracts.Foundation(app).Surface IsNot Nothing Then
                    _renderResources.Shadow.DrawLayeredShadow(
                        canvas:=canvas,
                        r:=rBase,
                        contract:=contract,
                        dpi:=dpi,
                        surfaceTheme:=MASAppThemeContracts.Foundation(app).Surface
                    )
                End If
            End If

            _renderResources.Surface.DrawInputSurface_SSOT(
                canvas:=canvas,
                ctx:=ctx,
                r:=rBase,
                dpi:=dpi,
                isHover:=_selectionController.Hover,
                isPressed:=_selectionController.Pressed,
                hasFocus:=hasFocus,
                isDisabled:=isDisabled,
                contract:=contract,
                isReadOnly:=IsReadOnlyBox
            )

            DrawValidationIndicator(canvas, ctx, rBase, dpi, hasFocus, isDisabled)

            If HasLeftIcon Then
                Dim ir As SKRect = InputLayoutPrimitives.ComputeInputIconSlotRect(rBase, dpi)
                DrawLeftIcon(canvas, ir, dpi)
            End If

            If RightReservedWidthDip > 0.0F Then
                Dim rr As SKRect =
                    InputLayoutPrimitives.ComputeInputRightAccessoryRect(
                        r:=rBase,
                        dpi:=dpi,
                        reservedWidthDip:=RightReservedWidthDip
                    )

                If rr.Width > 1.0F AndAlso rr.Height > 1.0F Then
                    DrawRightAdornment(canvas, ctx, rr, dpi)
                End If
            End If

            Dim contentR As SKRect = GetContentRectPx(ctx)

            If contentR.Width <= 2.0F OrElse contentR.Height <= 2.0F Then Return

            DrawTextLayer(canvas, ctx, th, contentR, dpi, hasFocus, isDisabled)
        End Sub

        Private Function ResolveInputContract() As MASResolvedComponentContract
            Return MASArchitectureRuntime.ResolveContractOrThrow(Me.GetType())
        End Function
        Private Sub DrawTextLayer(canvas As SKCanvas,
                          ctx As MASThemeContext,
                          th As IMASInputTheme,
                          contentR As SKRect,
                          dpi As Single,
                          hasFocus As Boolean,
                          isDisabled As Boolean)

            If canvas Is Nothing OrElse ctx Is Nothing OrElse th Is Nothing Then Return
            If contentR.Width <= 2.0F OrElse contentR.Height <= 2.0F Then Return

            Dim pText As SKPaint = CreateTextPaint(ctx)
            If pText Is Nothing Then Return

            Dim drawText As String = GetDisplayText()
            Dim isRtl As Boolean = ResolveIsRtl(drawText)
            Dim environmentStamp As MASRuntimeEnvironmentStamp =
                MASRuntimeEnvironmentStampBuilder.FromThemeContextAndTextDirection(ctx, isRtl).Build()

            EnsureScrollInRange(
        contentR:=contentR,
        pText:=pText,
        drawText:=drawText,
        isRtl:=isRtl
    )

            Dim saveCount As Integer = canvas.Save()

            Try
                canvas.ClipRect(contentR)

                Dim snap As MASTextInputLayoutSnapshot =
            GetOrBuildTextSnapshot(
                state:=_state,
                layout:=_layoutEngine,
                paint:=pText,
                contentR:=contentR,
                scrollOffsetPx:=_controller.ScrollOffsetPx,
                environmentStamp:=environmentStamp
            )

                If String.IsNullOrEmpty(snap.DisplayText) Then
                    DrawPlaceholderIfNeeded(
                canvas:=canvas,
                ctx:=ctx,
                th:=th,
                contentR:=contentR,
                dpi:=dpi,
                hasFocus:=hasFocus
            )
                End If

                If _multilineController.Enabled AndAlso snap.ParagraphLayout IsNot Nothing Then

                    If _syntaxHighlightingEnabled AndAlso _syntaxHighlighter IsNot Nothing Then
                        _renderResources.MultilineStyledRenderer.DrawFromSnapshot(
                    canvas:=canvas,
                    snap:=snap,
                    theme:=th,
                    hasFocus:=hasFocus AndAlso _renderResources.Caret.IsVisible,
                    highlighter:=_syntaxHighlighter,
                    fontProfile:=_fontProfile
                )
                    Else
                        _renderResources.MultilineRenderer.DrawFromSnapshot(
                    canvas:=canvas,
                    snap:=snap,
                    theme:=th,
                    hasFocus:=hasFocus AndAlso _renderResources.Caret.IsVisible
                )
                    End If

                Else

                    If _syntaxHighlightingEnabled AndAlso _syntaxHighlighter IsNot Nothing Then
                        _renderResources.StyledRenderer.DrawFromSnapshot(
                    canvas:=canvas,
                    snap:=snap,
                    theme:=th,
                    hasFocus:=hasFocus AndAlso _renderResources.Caret.IsVisible,
                    highlighter:=_syntaxHighlighter,
                    fontProfile:=_fontProfile
                )
                    Else
                        _renderResources.SingleLineRenderer.DrawFromSnapshot(
                    canvas:=canvas,
                    snap:=snap,
                    theme:=th,
                    hasFocus:=hasFocus AndAlso _renderResources.Caret.IsVisible
                )
                    End If

                End If

            Finally
                canvas.RestoreToCount(saveCount)
            End Try

        End Sub

        Private Sub DrawPlaceholderIfNeeded(canvas As SKCanvas,
                                            ctx As MASThemeContext,
                                            th As IMASInputTheme,
                                            contentR As SKRect,
                                            dpi As Single,
                                            hasFocus As Boolean)

            If hasFocus Then Return
            If String.IsNullOrEmpty(_placeholder) Then Return

            Dim rtlPlaceholder As Boolean
            Try
                rtlPlaceholder = MASShapedText.IsRtl(_placeholder)
            Catch masCaughtException1 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowRenderFallback(masCaughtException1)
                rtlPlaceholder = False
            End Try

            TypographyTextPrimitives.DrawSingleLineText(
                canvas:=canvas,
                ctx:=ctx,
                text:=_placeholder,
                bounds:=contentR,
                dpi:=dpi,
style:=MASTypography.MASTextStyle.FieldPlaceholder,
                color:=th.Text.Placeholder,
                align:=If(rtlPlaceholder,
                          TypographyTextPrimitives.TextAlign.Right,
                          TypographyTextPrimitives.TextAlign.Left)
            )
        End Sub

        Private Sub DrawValidationIndicator(canvas As SKCanvas,
                                            ctx As MASThemeContext,
                                            bounds As SKRect,
                                            dpi As Single,
                                            hasFocus As Boolean,
                                            isDisabled As Boolean)

            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If isDisabled Then Return
            If Not _validationVisuals.ShowIndicator Then Return

            Dim state As MASTextValidationState = EffectiveValidationState
            If state = MASTextValidationState.None Then Return
            If bounds.Width <= 2.0F OrElse bounds.Height <= 2.0F Then Return

            Dim color As SKColor = ResolveValidationColor(ctx, state)
            Dim lineHeight As Single = If(hasFocus, 2.25F, 1.65F) * PixelSnap.SafeDpi(dpi)
            lineHeight = Math.Max(1.0F, lineHeight)

            Dim indicator As New SKRect(bounds.Left + (6.0F * dpi),
                                        bounds.Bottom - lineHeight,
                                        bounds.Right - (6.0F * dpi),
                                        bounds.Bottom)

            If indicator.Right <= indicator.Left Then Return

            Using p As New SKPaint With {
                .IsAntialias = True,
                .Style = SKPaintStyle.Fill,
                .Color = color
            }
                canvas.DrawRoundRect(indicator, lineHeight, lineHeight, p)
            End Using

            If state = MASTextValidationState.Error OrElse
               state = MASTextValidationState.Warning Then
                DrawValidationDot(canvas, bounds, dpi, color)
            End If
        End Sub

        Private Shared Sub DrawValidationDot(canvas As SKCanvas,
                                             bounds As SKRect,
                                             dpi As Single,
                                             color As SKColor)

            Dim safeDpi As Single = PixelSnap.SafeDpi(dpi)
            Dim radius As Single = Math.Max(2.0F, 2.4F * safeDpi)
            Dim cx As Single = bounds.Right - (10.0F * safeDpi)
            Dim cy As Single = bounds.Top + (10.0F * safeDpi)

            If cx <= bounds.Left OrElse cy >= bounds.Bottom Then Return

            Using p As New SKPaint With {
                .IsAntialias = True,
                .Style = SKPaintStyle.Fill,
                .Color = color
            }
                canvas.DrawCircle(cx, cy, radius, p)
            End Using
        End Sub

        Private Shared Function ResolveValidationColor(ctx As MASThemeContext,
                                                       state As MASTextValidationState) As SKColor
            If ctx Is Nothing OrElse ctx.SemanticTheme Is Nothing Then Return SKColors.Transparent

            Select Case state
                Case MASTextValidationState.Success
                    Return ctx.SemanticTheme.Success.WithAlpha(230)
                Case MASTextValidationState.Warning
                    Return ctx.SemanticTheme.Warning.WithAlpha(235)
                Case MASTextValidationState.Error
                    Return ctx.SemanticTheme.Danger.WithAlpha(235)
                Case MASTextValidationState.Information
                    Return ctx.SemanticTheme.Info.WithAlpha(220)
                Case Else
                    Return SKColors.Transparent
            End Select
        End Function

#End Region

    End Class

End Namespace
