Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.Globalization
Imports System.Windows.Forms
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Composition
Imports Nexamas.UI.Controls
Imports Nexamas.UI.General
Imports Nexamas.UI.Layout
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.TextRendering
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.Components

    Partial Public NotInheritable Class MASCalendarView

#Region "Helpers"

        Private Function HitTestCell(pt As SKPoint) As Integer
            For i As Integer = 0 To _cells.Count - 1
                Dim cell As CalendarDayCell = _cells(i)
                If cell.Enabled AndAlso cell.Rect.Contains(pt.X, pt.Y) Then Return i
            Next
            Return -1
        End Function

        Private Sub ResetPointerState()
            If Not _hoverPrevious AndAlso Not _hoverNext AndAlso Not _pressedPrevious AndAlso Not _pressedNext AndAlso _hoverCellIndex < 0 AndAlso _pressedCellIndex < 0 Then Return
            _hoverPrevious = False
            _hoverNext = False
            _pressedPrevious = False
            _pressedNext = False
            _hoverCellIndex = -1
            _pressedCellIndex = -1
            InvalidateVisual()
        End Sub

        Private Sub MoveKeyboardSelection(value As Date)
            Dim clamped As Date = MASCalendarViewPolicy.ClampToRange(value, _minimumDate, _maximumDate)
            SetDisplayMonthInternal(clamped, True)
            SetSelectedDateInternal(clamped, True)
        End Sub

        Private Sub SetSelectedDateInternal(value As Nullable(Of Date), shouldRaiseEvent As Boolean)
            Dim normalized As Nullable(Of Date) = MASCalendarViewPolicy.NormalizeNullableDate(value)
            If normalized.HasValue Then normalized = MASCalendarViewPolicy.ClampToRange(normalized.Value, _minimumDate, _maximumDate)
            If NullableDatesEqual(_selectedDate, normalized) Then
                If normalized.HasValue Then SetDisplayMonthInternal(normalized.Value, False)
                Return
            End If

            Dim previousDate As Nullable(Of Date) = _selectedDate
            _selectedDate = normalized
            If normalized.HasValue Then SetDisplayMonthInternal(normalized.Value, False)
            StartCalendarViewSelectionMotion(previousDate, normalized)
            InvalidateVisual()
            If shouldRaiseEvent Then RaiseSelectedDateChangedSafe()
        End Sub

        Private Sub SetDisplayMonthInternal(value As Date,
                                            shouldRaiseEvent As Boolean)
            Dim normalized As Date = MASCalendarViewPolicy.NormalizeDisplayMonth(value)
            If _displayMonth = normalized Then Return
            _displayMonth = normalized
            ResetPointerState()
            InvalidateVisual()
            If shouldRaiseEvent Then RaiseDisplayMonthChangedSafe()
        End Sub

        Private Shared Function NullableDatesEqual(left As Nullable(Of Date),
                                                   right As Nullable(Of Date)) As Boolean
            If left.HasValue <> right.HasValue Then Return False
            If Not left.HasValue Then Return True
            Return left.Value = right.Value
        End Function

        Private Shared Function Inset(rect As SKRect,
                                      insetPx As Single) As SKRect
            Return New SKRect(rect.Left + insetPx,
                              rect.Top + insetPx,
                              rect.Right - insetPx,
                              rect.Bottom - insetPx)
        End Function

        Private Sub RaiseSelectedDateChangedSafe()
            Try
                RaiseEvent SelectedDateChanged(Me, EventArgs.Empty)
            Catch masCaughtException1 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowInputBoundary(masCaughtException1, "MASCalendarView.SelectedDateChanged")
            End Try
        End Sub

        Private Sub RaiseDisplayMonthChangedSafe()
            Try
                RaiseEvent DisplayMonthChanged(Me, EventArgs.Empty)
            Catch masCaughtException2 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowInputBoundary(masCaughtException2, "MASCalendarView.DisplayMonthChanged")
            End Try
        End Sub

#End Region

    End Class

End Namespace
