' SSOT-V2 Signature: This class/file follows the SSOT-V2 architecture.
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

    Friend NotInheritable Class MASInputFieldChromePainter
        Implements IDisposable

#Region "Fields"

        Private _disposed As Boolean

#End Region

#Region "Specs"

        Private Structure MASInputFieldChromeSpec
            Friend Top As SKColor
            Friend Mid As SKColor
            Friend Bot As SKColor
            Friend Wash As SKColor

            Friend BorderOuter As SKColor
            Friend BorderInner As SKColor
            Friend Hairline As SKColor
        End Structure

#End Region

#Region "Dispose"

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

#End Region

#Region "Public entry points"

        Friend Sub DrawInputFieldChromeFlat(canvas As SKCanvas,
                                               ctx As MASThemeContext,
                                               r As SKRect,
                                               dpi As Single,
                                               isDisabled As Boolean,
                                               contract As MASResolvedComponentContract)

            If _disposed Then Return
            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If r.Width <= 1.0F OrElse r.Height <= 1.0F Then Return
            If contract Is Nothing Then Throw New ArgumentNullException(NameOf(contract))

            contract.AssertConsumable()

            dpi = PixelSnap.SafeDpi(dpi)

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

            Dim m As MASSurfaceInteractionRing.Metrics
            Dim outer As SKRect

            If Not MASVisualEffectGeometry.TryResolveBaseRect(r, dpi, m, outer) Then Return

            Dim radius As Single =
                MASRadiusResolver.ResolveSurfacePx(
                    contract:=contract,
                    rPx:=outer,
                    dpi:=dpi)

            Dim spec As MASInputFieldChromeSpec =
                BuildMASInputFieldChromeSpec(
                    theme:=theme,
                    isDisabled:=isDisabled,
                    isReadOnly:=False,
                    hasFocus:=False)

            DrawInputFieldChromeCore(
                canvas:=canvas,
                ctx:=ctx,
                outer:=outer,
                radius:=radius,
                dpi:=dpi,
                spec:=spec)

        End Sub

        Friend Sub DrawInputFieldChrome(canvas As SKCanvas,
                                          ctx As MASThemeContext,
                                          r As SKRect,
                                          dpi As Single,
                                          isHover As Boolean,
                                          isPressed As Boolean,
                                          hasFocus As Boolean,
                                          isDisabled As Boolean,
                                          contract As MASResolvedComponentContract,
                                          Optional isReadOnly As Boolean = False)

            If _disposed Then Return
            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If r.Width <= 1.0F OrElse r.Height <= 1.0F Then Return
            If contract Is Nothing Then Throw New ArgumentNullException(NameOf(contract))

            contract.AssertConsumable()

            dpi = PixelSnap.SafeDpi(dpi)

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

            Dim appTheme As IMASAppTheme = ctx.Theme
            If appTheme Is Nothing Then Return

            Dim rm As MASSurfaceInteractionRing.Metrics = MASSurfaceInteractionRing.GetMetrics(dpi)
            Dim outer As SKRect = MASSurfaceInteractionRing.SnapBaseRect(r, rm)

            If outer.Width <= 1.0F OrElse outer.Height <= 1.0F Then Return

            Dim masState As MASInteractionState =
    ResolveInputMASState(
        isDisabled:=isDisabled,
        isPressed:=isPressed,
        hasFocus:=hasFocus,
        isHover:=isHover)

            Dim baseSpec As MASInputFieldChromeSpec =
                BuildMASInputFieldChromeSpec(
                    theme:=inputTheme,
                    isDisabled:=isDisabled,
                    isReadOnly:=isReadOnly,
                    hasFocus:=hasFocus)

            Dim radius As Single =
                MASRadiusResolver.ResolveSurfacePx(
                    contract:=contract,
                    rPx:=outer,
                    dpi:=dpi)

            DrawInputFieldChromeCore(
                canvas:=canvas,
                ctx:=ctx,
                outer:=outer,
                radius:=radius,
                dpi:=dpi,
                spec:=baseSpec)



            If masState = MASInteractionState.Enabled Then Return

            Dim overlaySpec As MASVisualInteractionOverlaySpec =
                MASInteractionResolver.Build(
                    contract:=contract,
                    state:=masState,
                    theme:=appTheme)

            If Not overlaySpec.FillEnabled AndAlso Not overlaySpec.RingEnabled Then Return

            MASSurfaceInteractionRing.DrawOverlay(
                canvas:=canvas,
                baseRectPx:=outer,
                contract:=contract,
                spec:=overlaySpec,
                dpi:=dpi)

        End Sub
        Private Shared Function ResolveInputMASState(isDisabled As Boolean,
                                             isPressed As Boolean,
                                             hasFocus As Boolean,
                                             isHover As Boolean) As MASInteractionState

            If isDisabled Then Return MASInteractionState.Disabled
            If isPressed Then Return MASInteractionState.Pressed
            If hasFocus Then Return MASInteractionState.Focused
            If isHover Then Return MASInteractionState.Hovered

            Return MASInteractionState.Enabled

        End Function

#End Region

#Region "Spec resolution"

        Private Shared Function BuildMASInputFieldChromeSpec(theme As IMASInputTheme,
                                                            isDisabled As Boolean,
                                                            isReadOnly As Boolean,
                                                            hasFocus As Boolean) As MASInputFieldChromeSpec

            If theme Is Nothing Then Throw New ArgumentNullException(NameOf(theme))

            Dim top As SKColor
            Dim mid As SKColor
            Dim bot As SKColor
            Dim wash As SKColor = theme.Surface.MaterialWash

            Dim borderOuter As SKColor
            Dim borderInner As SKColor = theme.Border.InnerHighlight
            Dim hairline As SKColor = theme.Border.Hairline

            If isDisabled Then
                top = theme.States.DisabledFill.Color
                mid = theme.States.DisabledFill.Color
                bot = theme.States.DisabledFill.Color

                borderOuter = theme.Border.DisabledBorder
                borderInner = ColorMath.WithAlpha(theme.Border.InnerHighlight, 110)
                hairline = ColorMath.WithAlpha(theme.Border.Hairline, 90)

            ElseIf isReadOnly Then
                top = theme.Surface.ReadOnlyTop
                mid = theme.Surface.ReadOnlyMid
                bot = theme.Surface.ReadOnlyBot

                borderOuter = theme.Border.ReadOnlyBorder
                borderInner = ColorMath.WithAlpha(theme.Border.InnerHighlight, 150)

            Else
                top = theme.Surface.Top
                mid = theme.Surface.Mid
                bot = theme.Surface.Bot

                borderOuter = theme.Border.Outer

                If hasFocus Then
                    borderOuter = ColorMath.Mix(theme.Border.Outer, theme.Border.FocusBase, 0.34F)
                    borderInner = ColorMath.Mix(theme.Border.InnerHighlight, theme.Surface.Top, 0.35F)
                End If
            End If

            Return New MASInputFieldChromeSpec With {
                .Top = top,
                .Mid = mid,
                .Bot = bot,
                .Wash = wash,
                .BorderOuter = borderOuter,
                .BorderInner = borderInner,
                .Hairline = hairline
            }

        End Function

#End Region

#Region "Core draw"

        Private Sub DrawInputFieldChromeCore(canvas As SKCanvas,
                                               ctx As MASThemeContext,
                                               outer As SKRect,
                                               radius As Single,
                                               dpi As Single,
                                               spec As MASInputFieldChromeSpec)

            If _disposed Then Return
            If canvas Is Nothing Then Return
            If outer.Width <= 1.0F OrElse outer.Height <= 1.0F Then Return

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

            Using rrPath As New SKPath()

                rrPath.AddRoundRect(outer, radius, radius)

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

                If Not materialBodyDrawn Then

                Dim baseShader As SKShader = Nothing

                Try
                    baseShader =
                        SKShader.CreateLinearGradient(
                            New SKPoint(outer.Left, outer.Top),
                            New SKPoint(outer.Left, outer.Bottom),
                            New SKColor() {spec.Top, spec.Mid, spec.Bot},
                            New Single() {0.0F, 0.48F, 1.0F},
                            SKShaderTileMode.Clamp)

                    Using fill As New SKPaint With {
                        .IsAntialias = True,
                        .IsDither = True,
                        .Style = SKPaintStyle.Fill,
                        .Shader = baseShader
                    }
                        canvas.DrawPath(rrPath, fill)
                    End Using

                Finally
                    MASVisualPrimitivesPainter.DisposeShader(baseShader)
                End Try

                End If

                MASVisualPrimitivesPainter.WithClipRoundRect(
                    canvas:=canvas,
                    r:=outer,
                    radiusPx:=radius,
                    drawAction:=Sub()
                                    DrawInputFieldWash(
                                        canvas:=canvas,
                                        outer:=outer,
                                        wash:=spec.Wash)
                                End Sub)

                DrawOuterStroke(
                    canvas:=canvas,
                    outer:=outer,
                    radius:=radius,
                    dpi:=dpi,
                    color:=spec.BorderOuter)

                DrawInnerStroke(
                    canvas:=canvas,
                    outer:=outer,
                    radius:=radius,
                    dpi:=dpi,
                    color:=spec.BorderInner)

                DrawHairlineStroke(
                    canvas:=canvas,
                    outer:=outer,
                    radius:=radius,
                    dpi:=dpi,
                    color:=spec.Hairline)

            End Using

        End Sub

        Private Shared Sub DrawInputFieldWash(canvas As SKCanvas,
                                            outer As SKRect,
                                            wash As SKColor)

            If canvas Is Nothing Then Return
            If outer.Width <= 1.0F OrElse outer.Height <= 1.0F Then Return
            If wash.Alpha = 0 Then Return

            Dim washRect As New SKRect(
                outer.Left,
                outer.Top,
                outer.Right,
                outer.Top + Math.Max(outer.Height * 0.68F, 1.0F))

            Dim washShader As SKShader = Nothing

            Try
                washShader =
                    SKShader.CreateLinearGradient(
                        New SKPoint(washRect.Left, washRect.Top),
                        New SKPoint(washRect.Left, washRect.Bottom),
                        New SKColor() {
                            ColorMath.WithAlpha(wash, 120),
                            ColorMath.WithAlpha(wash, 46),
                            ColorMath.WithAlpha(wash, 0)
                        },
                        New Single() {0.0F, 0.55F, 1.0F},
                        SKShaderTileMode.Clamp)

                Using washPaint As New SKPaint With {
                    .IsAntialias = True,
                    .IsDither = True,
                    .Style = SKPaintStyle.Fill,
                    .Shader = washShader
                }
                    canvas.DrawRect(washRect, washPaint)
                End Using

            Finally
                MASVisualPrimitivesPainter.DisposeShader(washShader)
            End Try

        End Sub

        Private Shared Sub DrawOuterStroke(canvas As SKCanvas,
                                           outer As SKRect,
                                           radius As Single,
                                           dpi As Single,
                                           color As SKColor)

            If canvas Is Nothing Then Return
            If color.Alpha = 0 Then Return

            Dim outerStroke As MASVisualPrimitivesPainter.StrokeRoundRectInfo =
                MASVisualPrimitivesPainter.BuildStrokeRoundRectInfo(
                    baseRect:=outer,
                    baseRadius:=radius,
                    dpi:=dpi,
                    edgeInsetPx:=0.0F,
                    strokeWidthPx:=Math.Max(
                        SurfaceTokens.FxRingStroke * dpi,
                        SurfaceTokens.FxRingMinStrokePx))

            If Not outerStroke.HasValue Then Return

            Using p As New SKPaint With {
                .IsAntialias = True,
                .IsDither = False,
                .Style = SKPaintStyle.Stroke,
                .StrokeJoin = SKStrokeJoin.Round,
                .StrokeCap = SKStrokeCap.Round,
                .StrokeWidth = outerStroke.StrokeWidth,
                .Color = color
            }
                canvas.DrawRoundRect(
                    outerStroke.Rect,
                    outerStroke.Radius,
                    outerStroke.Radius,
                    p)
            End Using

        End Sub

        Private Shared Sub DrawInnerStroke(canvas As SKCanvas,
                                           outer As SKRect,
                                           radius As Single,
                                           dpi As Single,
                                           color As SKColor)

            If canvas Is Nothing Then Return
            If color.Alpha = 0 Then Return

            Dim innerStroke As MASVisualPrimitivesPainter.StrokeRoundRectInfo =
                MASVisualPrimitivesPainter.BuildStrokeRoundRectInfo(
                    baseRect:=outer,
                    baseRadius:=radius,
                    dpi:=dpi,
                    edgeInsetPx:=1.0F * dpi,
                    strokeWidthPx:=Math.Max(1.0F, 0.9F * dpi))

            If Not innerStroke.HasValue Then Return

            Using p As New SKPaint With {
                .IsAntialias = True,
                .IsDither = False,
                .Style = SKPaintStyle.Stroke,
                .StrokeJoin = SKStrokeJoin.Round,
                .StrokeCap = SKStrokeCap.Round,
                .StrokeWidth = innerStroke.StrokeWidth,
                .Color = color
            }
                canvas.DrawRoundRect(
                    innerStroke.Rect,
                    innerStroke.Radius,
                    innerStroke.Radius,
                    p)
            End Using

        End Sub

        Private Shared Sub DrawHairlineStroke(canvas As SKCanvas,
                                              outer As SKRect,
                                              radius As Single,
                                              dpi As Single,
                                              color As SKColor)

            If canvas Is Nothing Then Return
            If color.Alpha = 0 Then Return

            Dim hairStroke As MASVisualPrimitivesPainter.StrokeRoundRectInfo =
                MASVisualPrimitivesPainter.BuildStrokeRoundRectInfo(
                    baseRect:=outer,
                    baseRadius:=radius,
                    dpi:=dpi,
                    edgeInsetPx:=0.0F,
                    strokeWidthPx:=1.0F)

            If Not hairStroke.HasValue Then Return

            Using p As New SKPaint With {
                .IsAntialias = True,
                .IsDither = False,
                .Style = SKPaintStyle.Stroke,
                .StrokeJoin = SKStrokeJoin.Round,
                .StrokeCap = SKStrokeCap.Round,
                .StrokeWidth = hairStroke.StrokeWidth,
                .Color = color
            }
                canvas.DrawRoundRect(
                    hairStroke.Rect,
                    hairStroke.Radius,
                    hairStroke.Radius,
                    p)
            End Using

        End Sub

#End Region

#Region "State mapping"



#End Region

    End Class

End Namespace