Option Strict On
Option Explicit On

Imports System
Imports System.Globalization
Imports System.Windows.Forms
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Components.ListPopup
Imports Nexamas.UI.Controls
Imports Nexamas.UI.FloatRuntime
Imports Nexamas.UI.General
Imports Nexamas.UI.Layout
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.Theming
Imports Nexamas.UI.TextRendering
Imports Nexamas.UI.Composition
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.Components

    Partial Public NotInheritable Class MASTimePicker
#Region "Popup Geometry"

        Private Function ResolveSurfaceSizeProvider() As Func(Of SKSizeI)
            Return Function() ResolveSurfaceSizePx()
        End Function

        Private Function ResolveSurfaceSizePx() As SKSizeI
            If SurfaceSizeProvider IsNot Nothing Then
                Try
                    Dim provided As SKSizeI = SurfaceSizeProvider.Invoke()
                    If provided.Width > 1 AndAlso provided.Height > 1 Then Return provided
                Catch masCaughtException4 As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowInputBoundary(masCaughtException4, "MASTimePicker.SurfaceSizeProvider")
                End Try
            End If

            If _runtimeServices IsNot Nothing Then Return _runtimeServices.TryGetSurfaceSizePx()
            Return SKSizeI.Empty
        End Function

        Private Function CalculateClockPopupSizePx(ctx As MASThemeContext,
                                                   anchorBoundsPx As SKRect) As SKSize
            Dim dpi As Single = PixelSnap.SafeDpi(If(ctx IsNot Nothing, ctx.Dpi, 1.0F))
            Dim probeBounds As New SKRect(
                0.0F,
                0.0F,
                Math.Max(anchorBoundsPx.Width, TimePickerTokens.ClockPopupWidthDip * dpi),
                TimePickerTokens.ClockPopupHeightDip * dpi)
            Dim plan As MASTimePickerClockLayoutPlan = MASTimePickerClockLayoutPlan.Create(ctx, probeBounds, SizeIntent)
            Dim profile As MASResponsiveLayoutProfile = plan.ResponsiveProfile

            Dim widthPx As Single = Math.Max(
                profile.ContentDipToPx(TimePickerTokens.ClockPopupMinWidthDip),
                Math.Max(anchorBoundsPx.Width, profile.ContentDipToPx(TimePickerTokens.ClockPopupWidthDip)))
            Dim heightPx As Single = profile.ContentDipToPx(TimePickerTokens.ClockPopupHeightDip)

            If plan.IsCollapsed Then
                heightPx = Math.Min(heightPx, profile.ContentDipToPx(TimePickerTokens.ClockPopupHeightDip * 0.82F))
            End If

            Return New SKSize(Math.Max(1.0F, widthPx), Math.Max(1.0F, heightPx))
        End Function

        Private Function ResolveClockPopupGapPx(ctx As MASThemeContext,
                                                popupSizePx As SKSize) As Single
            Dim bounds As New SKRect(0.0F, 0.0F, Math.Max(1.0F, popupSizePx.Width), Math.Max(1.0F, popupSizePx.Height))
            Dim profile As MASResponsiveLayoutProfile = MASResponsiveLayoutResolver.FromTheme(ctx, bounds, SizeIntent, MASTimePickerClockLayoutPlan.Thresholds)
            Return Math.Max(1.0F, profile.GapDipToPx(TimePickerTokens.ClockPopupGapDip))
        End Function

        Private Function ResolveClockAnchorBoundsPx(fallbackBoundsPx As SKRect) As SKRect
            Dim dpi As Single = 1.0F
            If Host IsNot Nothing Then
                Try
                    dpi = PixelSnap.SafeDpi(Host.Dpi)
                Catch masCaughtException6 As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowInputBoundary(masCaughtException6, "MASTimePicker.ResolveAnchorDpi")
                    dpi = 1.0F
                End Try
            End If

            Dim slotAnchorPx As SKRect = GetLayoutFloatAnchorBoundsPxInternal(dpi)
            If slotAnchorPx.Width > 1.0F AndAlso slotAnchorPx.Height > 1.0F Then Return slotAnchorPx
            If _lastPixelBounds.Width > 1.0F AndAlso _lastPixelBounds.Height > 1.0F Then Return _lastPixelBounds
            Return fallbackBoundsPx
        End Function

        Private Function NormalizeClockFloatBoundsPx(boundsPx As SKRect) As SKRect
            If boundsPx.IsEmpty Then Return SKRect.Empty

            Dim left As Single = Math.Min(boundsPx.Left, boundsPx.Right)
            Dim top As Single = Math.Min(boundsPx.Top, boundsPx.Bottom)
            Dim right As Single = Math.Max(boundsPx.Left, boundsPx.Right)
            Dim bottom As Single = Math.Max(boundsPx.Top, boundsPx.Bottom)

            If Single.IsNaN(left) OrElse Single.IsInfinity(left) Then Return SKRect.Empty
            If Single.IsNaN(top) OrElse Single.IsInfinity(top) Then Return SKRect.Empty
            If Single.IsNaN(right) OrElse Single.IsInfinity(right) Then Return SKRect.Empty
            If Single.IsNaN(bottom) OrElse Single.IsInfinity(bottom) Then Return SKRect.Empty

            Dim normalized As New SKRect(left, top, right, bottom)
            If normalized.Width <= 1.0F OrElse normalized.Height <= 1.0F Then Return SKRect.Empty

            Dim available As SKRect = ResolveAvailableBoundsPx(normalized)
            If available.Width <= 1.0F OrElse available.Height <= 1.0F Then Return PixelSnap.SnapRectHalf(normalized)

            Dim width As Single = Math.Min(normalized.Width, available.Width)
            Dim height As Single = Math.Min(normalized.Height, available.Height)
            Dim x As Single = normalized.Left
            Dim y As Single = normalized.Top

            If x < available.Left Then x = available.Left
            If y < available.Top Then y = available.Top
            If x + width > available.Right Then x = available.Right - width
            If y + height > available.Bottom Then y = available.Bottom - height

            Return PixelSnap.SnapRectHalf(New SKRect(x, y, x + width, y + height))
        End Function

        Private Function ResolveAvailableBoundsPx(fallbackBoundsPx As SKRect) As SKRect
            Dim s As SKSizeI = ResolveSurfaceSizePx()
            If s.Width > 1 AndAlso s.Height > 1 Then Return New SKRect(0.0F, 0.0F, CSng(s.Width), CSng(s.Height))

            Return New SKRect(
                0.0F,
                0.0F,
                Math.Max(fallbackBoundsPx.Right + 4.0F, fallbackBoundsPx.Width),
                Math.Max(fallbackBoundsPx.Bottom + 4.0F, fallbackBoundsPx.Height))
        End Function

#End Region

    End Class

End Namespace
