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

    ''' <summary>
    ''' Official Nexamas UI month-view calendar surface. It complements DatePicker
    ''' and DateRangePicker by displaying a navigable month grid with selection,
    ''' culture-aware weekday order, RTL-aware navigation, date-range disablement,
    ''' today/selected states, and MAS-owned premium surface drawing. Agenda storage,
    ''' recurrence, reminders, provider sync, and scheduler engines remain outside it.
    ''' </summary>
    Partial Public NotInheritable Class MASCalendarView
        Inherits MASControlBase
        Implements IMASLayoutParticipant
        Implements IMASIntrinsicSizeContract

#Region "Fields"

        Private Shared ReadOnly _contract As MASResolvedComponentContract =
            MASArchitectureRuntime.ResolveContractOrThrow(GetType(MASCalendarView))

        Private Structure CalendarDayCell
            Friend Sub New(value As Date,
                           rect As SKRect,
                           inDisplayMonth As Boolean,
                           enabled As Boolean,
                           weekend As Boolean)
                Me.Value = value
                Me.Rect = rect
                Me.InDisplayMonth = inDisplayMonth
                Me.Enabled = enabled
                Me.Weekend = weekend
            End Sub

            Friend Value As Date
            Friend Rect As SKRect
            Friend InDisplayMonth As Boolean
            Friend Enabled As Boolean
            Friend Weekend As Boolean
        End Structure

        Private ReadOnly _primitives As New MASVisualPrimitivesPainter()
        Private ReadOnly _cells As New List(Of CalendarDayCell)()
        Private _title As String = CalendarViewTokens.DefaultTitle
        Private _displayMonth As Date = MASCalendarViewPolicy.FirstOfMonth(DateTime.Today)
        Private _selectedDate As Nullable(Of Date) = Nothing
        Private _minimumDate As Nullable(Of Date) = Nothing
        Private _maximumDate As Nullable(Of Date) = Nothing
        Private _showOutsideDays As Boolean = True
        Private _hoverCellIndex As Integer = -1
        Private _pressedCellIndex As Integer = -1
        Private _hoverPrevious As Boolean
        Private _hoverNext As Boolean
        Private _pressedPrevious As Boolean
        Private _pressedNext As Boolean
        Private _previousRect As SKRect = SKRect.Empty
        Private _nextRect As SKRect = SKRect.Empty
        Private _contentRect As SKRect = SKRect.Empty

#End Region

#Region "Constructor / Factory"

        Public Sub New()
            MyBase.New()
        End Sub

        Public Shared Function Create(Optional selectedDate As Nullable(Of Date) = Nothing,
                                      Optional displayMonth As Nullable(Of Date) = Nothing) As MASCalendarView
            Dim control As New MASCalendarView()
            If displayMonth.HasValue Then control.DisplayMonth = displayMonth.Value
            If selectedDate.HasValue Then control.SelectedDate = selectedDate
            Return control
        End Function

#End Region

#Region "Public API"

        Public Event SelectedDateChanged As EventHandler
        Public Event DisplayMonthChanged As EventHandler

        Public Property Title As String
            Get
                Return _title
            End Get
            Set(value As String)
                Dim normalized As String = MASCalendarViewPolicy.NormalizeTitle(value)
                If String.Equals(_title, normalized, StringComparison.Ordinal) Then Return
                _title = normalized
                RequestSizeLayoutRefreshForSizeAffectingChange("MASCalendarView.Title")
                InvalidateVisual()
            End Set
        End Property

        Public Property DisplayMonth As Date
            Get
                Return _displayMonth
            End Get
            Set(value As Date)
                SetDisplayMonthInternal(value, True)
            End Set
        End Property

        Public Property SelectedDate As Nullable(Of Date)
            Get
                Return _selectedDate
            End Get
            Set(value As Nullable(Of Date))
                SetSelectedDateInternal(value, True)
            End Set
        End Property

        Public Property MinimumDate As Nullable(Of Date)
            Get
                Return _minimumDate
            End Get
            Set(value As Nullable(Of Date))
                Dim normalized As Nullable(Of Date) = MASCalendarViewPolicy.NormalizeNullableDate(value)
                If NullableDatesEqual(_minimumDate, normalized) Then Return
                _minimumDate = normalized
                If _maximumDate.HasValue AndAlso _minimumDate.HasValue AndAlso _minimumDate.Value > _maximumDate.Value Then _maximumDate = _minimumDate
                If _selectedDate.HasValue Then SetSelectedDateInternal(_selectedDate, False)
                InvalidateVisual()
            End Set
        End Property

        Public Property MaximumDate As Nullable(Of Date)
            Get
                Return _maximumDate
            End Get
            Set(value As Nullable(Of Date))
                Dim normalized As Nullable(Of Date) = MASCalendarViewPolicy.NormalizeNullableDate(value)
                If NullableDatesEqual(_maximumDate, normalized) Then Return
                _maximumDate = normalized
                If _minimumDate.HasValue AndAlso _maximumDate.HasValue AndAlso _maximumDate.Value < _minimumDate.Value Then _minimumDate = _maximumDate
                If _selectedDate.HasValue Then SetSelectedDateInternal(_selectedDate, False)
                InvalidateVisual()
            End Set
        End Property

        Public Property ShowOutsideDays As Boolean
            Get
                Return _showOutsideDays
            End Get
            Set(value As Boolean)
                If _showOutsideDays = value Then Return
                _showOutsideDays = value
                InvalidateVisual()
            End Set
        End Property

        Public ReadOnly Property MonthTitle As String
            Get
                Return MASCalendarViewPolicy.BuildMonthTitle(_displayMonth)
            End Get
        End Property

        Public Function ClearDate() As MASCalendarView
            SetSelectedDateInternal(Nothing, True)
            Return Me
        End Function

        Public Function SelectDate(value As Date) As MASCalendarView
            SelectedDate = value
            Return Me
        End Function

        Public Function MovePreviousMonth() As MASCalendarView
            SetDisplayMonthInternal(MASCalendarViewPolicy.AddMonthsSafe(_displayMonth, -1), True)
            Return Me
        End Function

        Public Function MoveNextMonth() As MASCalendarView
            SetDisplayMonthInternal(MASCalendarViewPolicy.AddMonthsSafe(_displayMonth, 1), True)
            Return Me
        End Function

        Public Function MoveToday() As MASCalendarView
            Dim today As Date = DateTime.Today.Date
            SetDisplayMonthInternal(today, True)
            If MASCalendarViewPolicy.IsDateEnabled(today, _minimumDate, _maximumDate) Then SetSelectedDateInternal(today, True)
            Return Me
        End Function

        Public Function WithSelectedDate(value As Date) As MASCalendarView
            SelectedDate = value
            Return Me
        End Function

        Public Function WithDisplayMonth(value As Date) As MASCalendarView
            DisplayMonth = value
            Return Me
        End Function

        Public Function WithDateRange(minimumDate As Nullable(Of Date),
                                      maximumDate As Nullable(Of Date)) As MASCalendarView
            _minimumDate = MASCalendarViewPolicy.NormalizeNullableDate(minimumDate)
            _maximumDate = MASCalendarViewPolicy.NormalizeNullableDate(maximumDate)
            If _minimumDate.HasValue AndAlso _maximumDate.HasValue AndAlso _minimumDate.Value > _maximumDate.Value Then
                Dim swap As Date = _minimumDate.Value
                _minimumDate = _maximumDate
                _maximumDate = swap
            End If
            If _selectedDate.HasValue Then SetSelectedDateInternal(_selectedDate, False)
            InvalidateVisual()
            Return Me
        End Function

        Public Function WithTitle(value As String) As MASCalendarView
            Title = value
            Return Me
        End Function

        Public Function WithOutsideDays(value As Boolean) As MASCalendarView
            ShowOutsideDays = value
            Return Me
        End Function

        Public Shadows Function WithSize(sizeIntent As MASSize) As MASCalendarView
            MyBase.SetSize(sizeIntent)
            Return Me
        End Function

#End Region

    End Class

End Namespace
