Option Strict On
Option Explicit On

Imports System
Imports Nexamas.UI.General
Imports Nexamas.UI.TextRendering
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.Components

    ''' <summary>
    ''' Internal layout authority for MASToggleSwitch. It owns track/thumb geometry,
    ''' label fitting, preferred size, and pointer-hit width so rendering and input
    ''' share one deterministic switch contract.
    ''' </summary>
    Friend NotInheritable Class ToggleSwitchLayoutHelper

        Private Sub New()
        End Sub

        Friend Structure LayoutInfo
            Friend Host As SKRect
            Friend TrackRect As SKRect
            Friend ThumbRect As SKRect
            Friend TextRect As SKRect
            Friend InteractiveRect As SKRect
        End Structure

        Friend Shared Function BuildLayout(ctx As MASThemeContext,
                                           boundsPx As SKRect,
                                           text As String,
                                           isPressed As Boolean,
                                           isChecked As Boolean,
                                           Optional selectionProgress As Single = -1.0F) As LayoutInfo

            Dim dpi As Single = ResolveDpi(ctx)

            Dim lo As New LayoutInfo With {
                .Host = boundsPx,
                .TrackRect = SKRect.Empty,
                .ThumbRect = SKRect.Empty,
                .TextRect = SKRect.Empty,
                .InteractiveRect = SKRect.Empty
            }

            If boundsPx.Width <= 0.0F OrElse boundsPx.Height <= 0.0F Then
                Return lo
            End If

            Dim trackW As Single = ToggleSwitchTokens.Width * dpi
            Dim trackH As Single = ToggleSwitchTokens.Height * dpi
            Dim gap As Single = ToggleSwitchTokens.LabelGap * dpi
            Dim textPadR As Single = ToggleSwitchTokens.TextPadRight * dpi
            Dim hitPadAfterText As Single = Math.Max(ToggleSwitchTokens.HitPadAfterText * dpi, textPadR)

            Dim trackRect As New SKRect(
                boundsPx.Left,
                boundsPx.MidY - (trackH * 0.5F),
                boundsPx.Left + trackW,
                boundsPx.MidY + (trackH * 0.5F))

            trackRect = PixelSnap.SnapRectHalf(trackRect)

            Dim thumbInset As Single = ToggleSwitchTokens.ThumbInset * dpi

            If isPressed Then
                thumbInset += ToggleSwitchTokens.ThumbPressedExtraInset * dpi
            End If

            Dim thumbSize As Single = Math.Max(2.0F, trackRect.Height - (thumbInset * 2.0F))
            Dim thumbTop As Single = trackRect.MidY - (thumbSize * 0.5F)
            Dim offLeft As Single = trackRect.Left + thumbInset
            Dim onLeft As Single = trackRect.Right - thumbInset - thumbSize
            Dim resolvedProgress As Single = ResolveSelectionProgress(isChecked, selectionProgress)
            Dim thumbLeft As Single = offLeft + ((onLeft - offLeft) * resolvedProgress)

            Dim thumbRect As New SKRect(
                thumbLeft,
                thumbTop,
                thumbLeft + thumbSize,
                thumbTop + thumbSize)

            thumbRect = PixelSnap.SnapRectHalf(thumbRect)

            Dim hasText As Boolean = Not String.IsNullOrWhiteSpace(text)
            Dim textLeft As Single = trackRect.Right + If(hasText, gap, 0.0F)
            Dim measuredTextWidth As Single = If(hasText, MeasureTextWidthPx(ctx, text, dpi), 0.0F)

            Dim textRight As Single
            If hasText Then
                textRight = Math.Min(textLeft + measuredTextWidth + textPadR, boundsPx.Right)
            Else
                textRight = textLeft
            End If

            Dim textRect As New SKRect(
                textLeft,
                boundsPx.Top,
                Math.Max(textLeft, textRight),
                boundsPx.Bottom)

            Dim interactiveRight As Single
            If hasText Then
                interactiveRight = textLeft + measuredTextWidth + hitPadAfterText
            Else
                interactiveRight = trackRect.Right
            End If

            interactiveRight = Math.Min(Math.Max(boundsPx.Left, interactiveRight), boundsPx.Right)

            Dim interactiveRect As New SKRect(
                boundsPx.Left,
                boundsPx.Top,
                interactiveRight,
                boundsPx.Bottom)

            lo.TrackRect = trackRect
            lo.ThumbRect = thumbRect
            lo.TextRect = textRect
            lo.InteractiveRect = interactiveRect

            Return lo
        End Function


        Private Shared Function ResolveSelectionProgress(isChecked As Boolean, selectionProgress As Single) As Single
            If Single.IsNaN(selectionProgress) OrElse Single.IsInfinity(selectionProgress) OrElse selectionProgress < 0.0F Then
                Return If(isChecked, 1.0F, 0.0F)
            End If

            If selectionProgress <= 0.0F Then Return 0.0F
            If selectionProgress >= 1.0F Then Return 1.0F
            Return selectionProgress
        End Function

        Private Shared Function MeasureTextWidthPx(ctx As MASThemeContext,
                                                   text As String,
                                                   dpi As Single) As Single
            If String.IsNullOrEmpty(text) Then Return 0.0F

            If ctx IsNot Nothing AndAlso ctx.Typography IsNot Nothing Then
                Using p As SKPaint = ctx.Typography.CreatePaint(MASTypography.MASTextStyle.Body, SKColors.Black)
                    If p IsNot Nothing Then
                        PrepareMeasurementPaint(p)
                        Return Math.Max(0.0F, MASShapedText.MeasureWidth(text, p))
                    End If
                End Using
            End If

            Return CSng(text.Length) * ToggleSwitchTokens.FallbackCharacterWidth * dpi
        End Function

        Private Shared Sub PrepareMeasurementPaint(paint As SKPaint)
            If paint Is Nothing Then Return

            paint.IsAntialias = True
            paint.IsDither = True
        End Sub

        Private Shared Function ResolveDpi(ctx As MASThemeContext) As Single
            If ctx Is Nothing Then Return 1.0F
            Return PixelSnap.SafeDpi(ctx.Dpi)
        End Function

    End Class

End Namespace
