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 MASDatePicker
#Region "Calendar Popup Runtime"

        Private Sub ToggleCalendar(ctx As MASThemeContext)
            If ctx Is Nothing Then Return
            If ResolveFloatRuntime() Is Nothing Then Return
            If Me.Host Is Nothing Then Return
            If Not Me.Enabled OrElse Not Me.Visible Then Return

            If _isCalendarOpen Then
                CloseCalendarSafe()
                Return
            End If

            Dim preAnchorBoundsPx As SKRect = ResolveCalendarAnchorBoundsPx(_lastPixelBounds)
            If preAnchorBoundsPx.Width <= 1.0F OrElse preAnchorBoundsPx.Height <= 1.0F Then Return

            _popupService.CloseAny()
            _isCalendarOpen = True
            _pressed = False
            InvalidateVisual()

            Dim dpi As Single = PixelSnap.SafeDpi(ctx.Dpi)
            Dim popupSize As SKSize = CalculateCalendarPopupSizePx(ctx, preAnchorBoundsPx)
            Dim boundsPx As SKRect = DropDownPositionPolicy.Calculate(
                preAnchorBoundsPx,
                popupSize,
                dpi,
                ResolveCalendarPopupGapPx(ctx, popupSize),
                ResolveSurfaceSizeProvider())

            Dim view As New MASDatePickerCalendarView(
                owner:=Me,
                selectedDate:=_selectedDate,
                displayMonth:=ResolveDisplayMonthForOpen(),
                minimumDate:=_minimumDate,
                maximumDate:=_maximumDate,
                onDateSelected:=
                    Sub(value As Date)
                        Me.SelectedDate = value
                        CloseCalendarSafe()
                    End Sub,
                onDisplayMonthChanged:=
                    Sub(value As Date)
                        _displayMonth = FirstOfMonth(value)
                    End Sub)

            Dim content As New MASFloatControlsContent(
                DatePickerTokens.CalendarPopupOwnerKey,
                Me.Host,
                Sub()
                    Me.InvalidateVisual()
                End Sub)

            content.CloseOnEscape = True
            content.CloseOnOutsidePointer = True
            content.AcceptsKeyboardInput = True
            content.AcceptsPointerInput = True
            content.Add(view)

            Try
                _popupService.ShowOwned(Me, content, NormalizeCalendarFloatBoundsPx(boundsPx))

                If Not IsCurrentCalendarHandleOpen() Then
                    _popupService.CloseAny()
                    _isCalendarOpen = False
                    ResetInputAll()
                    InvalidateVisual()
                End If
            Catch masDatePickerShowException As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowInputBoundary(masDatePickerShowException, "MASDatePicker.ToggleCalendar.ShowOwned")
                _isCalendarOpen = False
                ResetInputAll()
                InvalidateVisual()
            End Try
        End Sub

        Private Sub CloseCalendarSafe()
            Dim wasOpen As Boolean = _isCalendarOpen
            Dim hadOwnedState As Boolean =
                (_popupService IsNot Nothing AndAlso
                 (_popupService.CurrentHandle IsNot Nothing OrElse _popupService.CurrentContent IsNot Nothing))

            _isCalendarOpen = False
            ResetInputAll()

            If wasOpen OrElse hadOwnedState Then
                _popupService.CloseIfOwnedBy(Me)
            End If

            InvalidateVisual()
        End Sub

        Private Function ShowCalendarFloat(ownerKey As String,
                                           content As IMASFloatContent,
                                           boundsPx As SKRect) As MASFloatHandle
            Dim runtime As MASFloatRuntime = ResolveFloatRuntime()
            If runtime Is Nothing OrElse content Is Nothing Then Return Nothing

            boundsPx = NormalizeCalendarFloatBoundsPx(boundsPx)
            If boundsPx.Width <= 1.0F OrElse boundsPx.Height <= 1.0F Then Return Nothing

            If String.IsNullOrWhiteSpace(ownerKey) Then ownerKey = DatePickerTokens.CalendarPopupOwnerKey

            Dim request As New MASFloatRequest() With {
                .FloatKey = MASFloatKeys.DatePickerCalendar,
                .OwnerKey = ownerKey,
                .Content = content,
                .RequestedSlot = MASFloatSlot.Popup,
                .RequestedScope = MASFloatScope.Owner,
                .RequestedLayer = MASFloatLayerLevel.Interactive,
                .ShowPolicy = MASFloatShowPolicy.CloseSameOwnerThenShow,
                .AnchorRectPx = ResolveCalendarAnchorBoundsPx(boundsPx),
                .InitialBoundsPx = boundsPx,
                .AvailableBoundsPx = ResolveAvailableBoundsPx(boundsPx),
                .RequestedPresentation = MASFloatPresentationProfiles.DropDownList()
            }

            Return runtime.Show(request)
        End Function

        Private Function ResolveFloatRuntime() As MASFloatRuntime
            If _floatRuntime IsNot Nothing Then Return _floatRuntime
            If _runtimeServices IsNot Nothing Then Return _runtimeServices.TryGetFloatRuntime()
            Return Nothing
        End Function

        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, "MASDatePicker.SurfaceSizeProvider")
                End Try
            End If

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

        Private Function IsCurrentCalendarHandleOpen() As Boolean
            Dim runtime As MASFloatRuntime = ResolveFloatRuntime()
            If runtime Is Nothing OrElse _popupService Is Nothing OrElse _popupService.CurrentHandle Is Nothing Then Return False

            Try
                Return runtime.IsOpen(_popupService.CurrentHandle)
            Catch masCaughtException5 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowInputBoundary(masCaughtException5, "MASDatePicker.VerifyCalendarHandle")
                Return False
            End Try
        End Function

        Private Function ResolveDisplayMonthForOpen() As Date
            If _selectedDate.HasValue Then Return FirstOfMonth(_selectedDate.Value)
            If IsDateAllowed(DateTime.Today) Then Return FirstOfMonth(DateTime.Today)
            If _minimumDate.HasValue Then Return FirstOfMonth(_minimumDate.Value)
            If _maximumDate.HasValue Then Return FirstOfMonth(_maximumDate.Value)
            Return FirstOfMonth(_displayMonth)
        End Function

#End Region

    End Class

End Namespace
