Option Strict On
Option Explicit On

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

Namespace Nexamas.UI.Components

    ''' <summary>
    ''' MAS-owned toggle-switch renderer. It remains Friend so public projects
    ''' never depend on track/thumb drawing details, shader allocation, or internal
    ''' interaction overlay rules.
    ''' </summary>
    Friend NotInheritable Class ToggleSwitchPainter
        Implements IDisposable

        Private _disposed As Boolean

        Private ReadOnly _shadow As New MASShadowPainter()
        Private ReadOnly _fill As New SKPaint With {
            .IsAntialias = True,
            .IsDither = True,
            .Style = SKPaintStyle.Fill
        }
        Private ReadOnly _stroke As New SKPaint With {
            .IsAntialias = True,
            .IsDither = True,
            .Style = SKPaintStyle.Stroke
        }

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

            Try
                _shadow.Dispose()
            Catch masCaughtException1 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(masCaughtException1)
            End Try
            Try
                _fill.Dispose()
            Catch masCaughtException2 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(masCaughtException2)
            End Try
            Try
                _stroke.Dispose()
            Catch masCaughtException3 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(masCaughtException3)
            End Try
        End Sub

        Friend Sub Draw(canvas As SKCanvas,
                        ctx As MASThemeContext,
                        boundsPx As SKRect,
                        text As String,
                        dpi As Single,
                        isHover As Boolean,
                        isPressed As Boolean,
                        hasFocus As Boolean,
                        isEnabled As Boolean,
                        isChecked As Boolean,
                        contract As MASResolvedComponentContract,
                        Optional selectionProgress As Single = -1.0F)

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

            Dim scope As MASContractRenderScope =
    MASArchitectureRuntime.CreateRenderScopeForComponent(Of MASToggleSwitch)(
        NameOf(ToggleSwitchPainter),
        contract,
        MASRenderLayer.Surface,
        MASRenderLayer.Shadow,
        MASRenderLayer.Text,
        MASRenderLayer.Border,
        MASRenderLayer.Hover,
        MASRenderLayer.Pressed,
        MASRenderLayer.Focus)

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

            dpi = PixelSnap.SafeDpi(dpi)

            Dim lo As ToggleSwitchLayoutHelper.LayoutInfo =
                ToggleSwitchLayoutHelper.BuildLayout(
                    ctx:=ctx,
                    boundsPx:=boundsPx,
                    text:=text,
                    isPressed:=isPressed,
                    isChecked:=isChecked,
                    selectionProgress:=selectionProgress)

            If lo.TrackRect.Width <= 1.0F OrElse lo.TrackRect.Height <= 1.0F Then Return
            If lo.ThumbRect.Width <= 1.0F OrElse lo.ThumbRect.Height <= 1.0F Then Return

            Dim appTheme As IMASAppTheme = ctx.Theme
            Dim tsTheme As IMASToggleSwitchTheme = ctx.ToggleSwitchTheme

            If appTheme Is Nothing Then Return
            If tsTheme Is Nothing Then Return
            If tsTheme.Track Is Nothing Then Return
            If tsTheme.Thumb Is Nothing Then Return
            If tsTheme.Text Is Nothing Then Return

            If ToggleSwitchTokens.ShadowEnabled AndAlso isEnabled Then
                _shadow.DrawLayeredShadow(
                    canvas:=canvas,
                    r:=lo.TrackRect,
                    contract:=contract,
                    dpi:=dpi,
                    surfaceTheme:=MASAppThemeContracts.Foundation(appTheme).Surface)
            End If

            DrawTrack(
                canvas:=canvas,
                trackRect:=lo.TrackRect,
                tsTheme:=tsTheme,
                isEnabled:=isEnabled,
                isChecked:=isChecked)

            Dim masState As MASInteractionState =
                ResolveSwitchMASInteractionState(
                    isEnabled:=isEnabled,
                    isHover:=isHover,
                    isPressed:=isPressed,
                    hasFocus:=hasFocus,
                    isChecked:=isChecked)

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

            If spec.FillEnabled OrElse spec.RingEnabled Then
                MASSurfaceInteractionRing.DrawOverlay(
                    canvas:=canvas,
                    baseRectPx:=lo.TrackRect,
                    contract:=contract,
                    spec:=spec,
                    dpi:=dpi)
            End If

            DrawThumb(
                canvas:=canvas,
                thumbRect:=lo.ThumbRect,
                tsTheme:=tsTheme,
                isEnabled:=isEnabled,
                isChecked:=isChecked)

            DrawLabel(
                canvas:=canvas,
                ctx:=ctx,
                textRect:=lo.TextRect,
                dpi:=dpi,
                text:=text,
                tsTheme:=tsTheme,
                isEnabled:=isEnabled)
        End Sub

        Private Sub DrawTrack(canvas As SKCanvas,
                              trackRect As SKRect,
                              tsTheme As IMASToggleSwitchTheme,
                              isEnabled As Boolean,
                              isChecked As Boolean)

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

            Dim fillTop As SKColor
            Dim fillBot As SKColor
            Dim borderCol As SKColor

            If Not isEnabled Then
                fillTop = tsTheme.Track.DisabledTop
                fillBot = tsTheme.Track.DisabledBottom
                borderCol = tsTheme.Track.DisabledBorder
            ElseIf isChecked Then
                fillTop = tsTheme.Track.OnTop
                fillBot = tsTheme.Track.OnBottom
                borderCol = tsTheme.Track.OnBorder
            Else
                fillTop = tsTheme.Track.OffTop
                fillBot = tsTheme.Track.OffBottom
                borderCol = tsTheme.Track.OffBorder
            End If

            Using sh As SKShader = SKShader.CreateLinearGradient(
                New SKPoint(trackRect.Left, trackRect.Top),
                New SKPoint(trackRect.Left, trackRect.Bottom),
                New SKColor() {fillTop, fillBot},
                New Single() {0.0F, 1.0F},
                SKShaderTileMode.Clamp)

                Try
                    _fill.Shader = sh
                    canvas.DrawRoundRect(
                        trackRect,
                        trackRect.Height * 0.5F,
                        trackRect.Height * 0.5F,
                        _fill)
                Finally
                    _fill.Shader = Nothing
                End Try
            End Using

            _stroke.StrokeWidth = 1.0F
            _stroke.Color = borderCol
            canvas.DrawRoundRect(
                trackRect,
                trackRect.Height * 0.5F,
                trackRect.Height * 0.5F,
                _stroke)
        End Sub

        Private Sub DrawThumb(canvas As SKCanvas,
                              thumbRect As SKRect,
                              tsTheme As IMASToggleSwitchTheme,
                              isEnabled As Boolean,
                              isChecked As Boolean)

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

            Dim thumbFill As SKColor
            Dim thumbBorder As SKColor

            If Not isEnabled Then
                thumbFill = tsTheme.Thumb.DisabledFill
                thumbBorder = tsTheme.Thumb.DisabledBorder
            ElseIf isChecked Then
                thumbFill = tsTheme.Thumb.OnFill
                thumbBorder = tsTheme.Thumb.OnBorder
            Else
                thumbFill = tsTheme.Thumb.OffFill
                thumbBorder = tsTheme.Thumb.OffBorder
            End If

            _fill.Shader = Nothing
            _fill.Color = thumbFill
            canvas.DrawOval(thumbRect, _fill)

            DrawThumbHighlight(canvas, thumbRect, isEnabled, isChecked)

            _stroke.StrokeWidth = 1.0F
            _stroke.Color = thumbBorder
            canvas.DrawOval(thumbRect, _stroke)
        End Sub

        Private Sub DrawThumbHighlight(canvas As SKCanvas,
                                       thumbRect As SKRect,
                                       isEnabled As Boolean,
                                       isChecked As Boolean)

            If canvas Is Nothing Then Return
            If thumbRect.Width <= 4.0F OrElse thumbRect.Height <= 4.0F Then Return

            Dim alpha As Byte = If(isEnabled, If(isChecked, CByte(70), CByte(50)), CByte(24))
            Dim inset As Single = Math.Max(1.0F, thumbRect.Width * 0.18F)
            Dim highlightRect As SKRect = thumbRect
            highlightRect.Inflate(-inset, -inset)
            highlightRect.Bottom = Math.Max(highlightRect.Top + 1.0F, highlightRect.Top + (highlightRect.Height * 0.46F))

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

            _fill.Shader = Nothing
            _fill.Color = ColorMath.WithAlpha(SKColors.White, alpha)
            canvas.DrawOval(highlightRect, _fill)
        End Sub

        Private Sub DrawLabel(canvas As SKCanvas,
                              ctx As MASThemeContext,
                              textRect As SKRect,
                              dpi As Single,
                              text As String,
                              tsTheme As IMASToggleSwitchTheme,
                              isEnabled As Boolean)

            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If textRect.Width <= 1.0F OrElse textRect.Height <= 1.0F Then Return
            If String.IsNullOrEmpty(text) Then Return
            If tsTheme Is Nothing OrElse tsTheme.Text Is Nothing Then Return

            Dim c As SKColor =
                If(isEnabled, tsTheme.Text.Normal, tsTheme.Text.Disabled)

            TypographyTextPrimitives.DrawSingleLineText(
                canvas:=canvas,
                ctx:=ctx,
                text:=text,
                bounds:=textRect,
                dpi:=dpi,
                style:=MASTypography.MASTextStyle.Body,
                color:=c,
                align:=TypographyTextPrimitives.TextAlign.Left,
                drawShadow:=False)
        End Sub

        Private Shared Function ResolveSwitchMASInteractionState(isEnabled As Boolean,
                                                                 isHover As Boolean,
                                                                 isPressed As Boolean,
                                                                 hasFocus As Boolean,
                                                                 isChecked As Boolean) As MASInteractionState

            If Not isEnabled Then Return MASInteractionState.Disabled
            If isPressed Then Return MASInteractionState.Pressed
            If hasFocus Then Return MASInteractionState.Focused
            If isHover Then Return MASInteractionState.Hovered
            If isChecked Then Return MASInteractionState.Checked

            Return MASInteractionState.Enabled
        End Function

    End Class

End Namespace
