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 "Rendering"

        Protected Overrides Sub Render(canvas As SKCanvas,
                                       ctx As MASThemeContext,
                                       pixelBounds As SKRect)
            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If pixelBounds.Width <= 1.0F OrElse pixelBounds.Height <= 1.0F Then Return

            Dim dpi As Single = PixelSnap.SafeDpi(ctx.Dpi)
            Dim bounds As SKRect = PixelSnap.SnapRect(pixelBounds, dpi)
            If bounds.Width <= 1.0F OrElse bounds.Height <= 1.0F Then Return

            Dim responsiveProfile As MASResponsiveLayoutProfile = MASResponsiveLayoutResolver.FromTheme(ctx, bounds, SizeIntent, MASCalendarViewLayoutPlan.Thresholds)
            Dim plan As MASCalendarViewLayoutPlan = MASCalendarViewLayoutPlan.Create(responsiveProfile)

            _primitives.DrawCardSurface(canvas, ctx, bounds, dpi, _contract, MASCardVisualStyle.Secondary)
            _contentRect = PixelSnap.SnapRect(Inset(bounds, plan.PaddingPx), dpi)
            If _contentRect.Width <= 1.0F OrElse _contentRect.Height <= 1.0F Then Return

            Dim isRtl As Boolean = MASCalendarViewPolicy.ResolveRightToLeft()
            DrawHeader(canvas, ctx, _contentRect, dpi, isRtl, plan)
            DrawWeekdaysAndDays(canvas, ctx, _contentRect, dpi, isRtl, plan)
        End Sub

        Private Sub DrawHeader(canvas As SKCanvas,
                               ctx As MASThemeContext,
                               content As SKRect,
                               dpi As Single,
                               isRtl As Boolean,
                               plan As MASCalendarViewLayoutPlan)
            If plan Is Nothing OrElse Not plan.ShowHeader Then
                _previousRect = SKRect.Empty
                _nextRect = SKRect.Empty
                Return
            End If
            Dim headerHeight As Single = plan.HeaderHeightPx
            Dim header As New SKRect(content.Left, content.Top, content.Right, content.Top + headerHeight)
            Dim buttonSize As Single = Math.Min(plan.NavButtonSizePx, Math.Max(18.0F * dpi, header.Height - 4.0F * dpi))
            Dim buttonTop As Single = header.Top + (header.Height - buttonSize) * 0.5F
            Dim leftButton As New SKRect(header.Left, buttonTop, header.Left + buttonSize, buttonTop + buttonSize)
            Dim rightButton As New SKRect(header.Right - buttonSize, buttonTop, header.Right, buttonTop + buttonSize)
            _previousRect = If(isRtl, rightButton, leftButton)
            _nextRect = If(isRtl, leftButton, rightButton)

            DrawNavButton(canvas, ctx, _previousRect, dpi, isPrevious:=True, isRtl:=isRtl)
            DrawNavButton(canvas, ctx, _nextRect, dpi, isPrevious:=False, isRtl:=isRtl)

            If plan.ShowHeaderTitle Then
                Dim titleRect As New SKRect(header.Left + buttonSize + plan.GridGapPx,
                                            header.Top,
                                            header.Right - buttonSize - plan.GridGapPx,
                                            header.Bottom)
                Dim titleText As String = If(String.Equals(_title, CalendarViewTokens.DefaultTitle, StringComparison.Ordinal), MonthTitle, _title & " · " & MonthTitle)
                TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, titleText, titleRect, dpi, MASTypography.MASTextStyle.Body, MASCalendarViewPolicy.ResolvePrimaryTextColor(ctx), TypographyTextPrimitives.TextAlign.Center, False)
            End If

            Using p As New SKPaint With {
                .IsAntialias = True,
                .Style = SKPaintStyle.Stroke,
                .StrokeWidth = Math.Max(1.0F, dpi),
                .Color = MASCalendarViewPolicy.ResolveSecondaryTextColor(ctx).WithAlpha(CalendarViewTokens.HeaderDividerAlpha)
            }
                canvas.DrawLine(header.Left, header.Bottom, header.Right, header.Bottom, p)
            End Using
        End Sub

        Private Sub DrawNavButton(canvas As SKCanvas,
                                  ctx As MASThemeContext,
                                  rect As SKRect,
                                  dpi As Single,
                                  isPrevious As Boolean,
                                  isRtl As Boolean)
            Dim activeHover As Boolean = If(isPrevious, _hoverPrevious, _hoverNext)
            Dim activePressed As Boolean = If(isPrevious, _pressedPrevious, _pressedNext)
            Dim accent As SKColor = MASCalendarViewPolicy.ResolveAccentColor(ctx)
            Dim radius As Single = rect.Height * 0.42F
            Dim alpha As Byte = If(activePressed, CalendarViewTokens.NavPressedAlpha, If(activeHover, CalendarViewTokens.NavHoverAlpha, CByte(0)))
            If alpha > 0 Then
                Using fill As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = accent.WithAlpha(alpha)}
                    canvas.DrawRoundRect(rect, radius, radius, fill)
                End Using
            End If

            Dim drawsLeftArrow As Boolean = If(isPrevious, Not isRtl, isRtl)
            Dim cx As Single = rect.MidX
            Dim cy As Single = rect.MidY
            Dim w As Single = rect.Width * 0.22F
            Dim h As Single = rect.Height * 0.28F
            Using path As New SKPath()
                If drawsLeftArrow Then
                    path.MoveTo(cx + w * 0.45F, cy - h)
                    path.LineTo(cx - w * 0.45F, cy)
                    path.LineTo(cx + w * 0.45F, cy + h)
                Else
                    path.MoveTo(cx - w * 0.45F, cy - h)
                    path.LineTo(cx + w * 0.45F, cy)
                    path.LineTo(cx - w * 0.45F, cy + h)
                End If
                Using paint As New SKPaint With {
                    .IsAntialias = True,
                    .Style = SKPaintStyle.Stroke,
                    .StrokeWidth = Math.Max(1.4F * dpi, 1.0F),
                    .StrokeCap = SKStrokeCap.Round,
                    .StrokeJoin = SKStrokeJoin.Round,
                    .Color = MASCalendarViewPolicy.ResolvePrimaryTextColor(ctx)
                }
                    canvas.DrawPath(path, paint)
                End Using
            End Using
        End Sub

        Private Sub DrawWeekdaysAndDays(canvas As SKCanvas,
                                        ctx As MASThemeContext,
                                        content As SKRect,
                                        dpi As Single,
                                        isRtl As Boolean,
                                        plan As MASCalendarViewLayoutPlan)
            If ctx.Typography Is Nothing Then Return
            _cells.Clear()

            Dim firstDay As DayOfWeek = MASCalendarViewPolicy.ResolveFirstDayOfWeek()
            Dim weekdayLabels As String() = MASCalendarViewPolicy.BuildWeekdayLabels(firstDay)
            Dim dates As Date() = MASCalendarViewPolicy.BuildVisibleDates(_displayMonth, firstDay)
            Dim headerBottom As Single = content.Top + plan.HeaderHeightPx
            Dim gap As Single = plan.GridGapPx
            Dim weekdayHeight As Single = plan.WeekdayHeightPx
            Dim weekdaysTop As Single = headerBottom + If(plan.ShowWeekdays, gap, 0.0F)
            Dim gridTop As Single = If(plan.ShowWeekdays, weekdaysTop + weekdayHeight + gap, headerBottom + gap)
            Dim gridHeight As Single = Math.Max(42.0F * dpi, content.Bottom - gridTop)
            Dim cellW As Single = Math.Max(18.0F * dpi, (content.Width - gap * 6.0F) / 7.0F)
            Dim cellH As Single = Math.Max(18.0F * dpi, (gridHeight - gap * 5.0F) / 6.0F)
            Dim textPrimary As SKColor = MASCalendarViewPolicy.ResolvePrimaryTextColor(ctx)
            Dim textSecondary As SKColor = MASCalendarViewPolicy.ResolveSecondaryTextColor(ctx)
            Dim disabled As SKColor = MASCalendarViewPolicy.ResolveDisabledTextColor(ctx)

            If plan.ShowWeekdays Then
                For col As Integer = 0 To 6
                    Dim visualCol As Integer = If(isRtl, 6 - col, col)
                    Dim left As Single = content.Left + CSng(visualCol) * (cellW + gap)
                    Dim rect As New SKRect(left, weekdaysTop, left + cellW, weekdaysTop + weekdayHeight)
                    Dim weekdayText As String = If(plan.UseShortWeekdayLabels AndAlso weekdayLabels(col).Length > 0, weekdayLabels(col).Substring(0, 1), weekdayLabels(col))
                    TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, weekdayText, rect, dpi, MASTypography.MASTextStyle.Micro, textSecondary, TypographyTextPrimitives.TextAlign.Center, False)
                Next
            End If

            Dim today As Date = DateTime.Today.Date
            Dim accent As SKColor = MASCalendarViewPolicy.ResolveAccentColor(ctx)
            For row As Integer = 0 To 5
                For col As Integer = 0 To 6
                    Dim index As Integer = row * 7 + col
                    Dim value As Date = dates(index)
                    Dim visualCol As Integer = If(isRtl, 6 - col, col)
                    Dim left As Single = content.Left + CSng(visualCol) * (cellW + gap)
                    Dim top As Single = gridTop + CSng(row) * (cellH + gap)
                    Dim rect As SKRect = PixelSnap.SnapRect(New SKRect(left, top, left + cellW, top + cellH), dpi)
                    Dim inMonth As Boolean = (value.Month = _displayMonth.Month AndAlso value.Year = _displayMonth.Year)
                    Dim enabled As Boolean = MASCalendarViewPolicy.IsDateEnabled(value, _minimumDate, _maximumDate) AndAlso (_showOutsideDays OrElse inMonth)
                    Dim weekend As Boolean = (value.DayOfWeek = DayOfWeek.Saturday OrElse value.DayOfWeek = DayOfWeek.Sunday)
                    _cells.Add(New CalendarDayCell(value, rect, inMonth, enabled, weekend))
                    DrawDayCell(canvas, ctx, rect, dpi, value, inMonth, enabled, weekend, index, today, textPrimary, textSecondary, disabled, plan)
                Next
            Next

            DrawCalendarViewSelectionMotionOverlay(canvas, dpi, accent, accent, plan.CellRadiusPx)
        End Sub

        Private Sub DrawDayCell(canvas As SKCanvas,
                                ctx As MASThemeContext,
                                rect As SKRect,
                                dpi As Single,
                                value As Date,
                                inMonth As Boolean,
                                enabled As Boolean,
                                weekend As Boolean,
                                index As Integer,
                                today As Date,
                                textPrimary As SKColor,
                                textSecondary As SKColor,
                                disabled As SKColor,
                                plan As MASCalendarViewLayoutPlan)
            Dim accent As SKColor = MASCalendarViewPolicy.ResolveAccentColor(ctx)
            Dim radius As Single = Math.Max(4.0F * dpi, plan.CellRadiusPx)
            Dim selected As Boolean = _selectedDate.HasValue AndAlso _selectedDate.Value = value
            If selected AndAlso ShouldSuppressCalendarViewSelectionFill(value) Then selected = False
            Dim isToday As Boolean = (value = today)
            Dim hover As Boolean = (_hoverCellIndex = index)
            Dim pressed As Boolean = (_pressedCellIndex = index)

            If enabled AndAlso weekend AndAlso inMonth AndAlso Not selected Then
                Using wash As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = accent.WithAlpha(CalendarViewTokens.WeekendWashAlpha)}
                    canvas.DrawRoundRect(rect, radius, radius, wash)
                End Using
            End If

            If selected Then
                Using fill As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = accent.WithAlpha(CalendarViewTokens.SelectedFillAlpha)}
                    canvas.DrawRoundRect(rect, radius, radius, fill)
                End Using
            ElseIf enabled AndAlso (pressed OrElse hover) Then
                Using fill As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = accent.WithAlpha(If(pressed, CalendarViewTokens.CellPressedAlpha, CalendarViewTokens.CellHoverAlpha))}
                    canvas.DrawRoundRect(rect, radius, radius, fill)
                End Using
            End If

            If isToday Then
                Using stroke As New SKPaint With {
                    .IsAntialias = True,
                    .Style = SKPaintStyle.Stroke,
                    .StrokeWidth = Math.Max(1.0F, dpi),
                    .Color = accent.WithAlpha(CalendarViewTokens.TodayStrokeAlpha)
                }
                    canvas.DrawRoundRect(Inset(rect, 1.5F * dpi), Math.Max(2.0F * dpi, radius - 1.5F * dpi), Math.Max(2.0F * dpi, radius - 1.5F * dpi), stroke)
                End Using
            End If

            Dim color As SKColor
            If Not enabled Then
                color = disabled
            ElseIf selected Then
                color = SKColors.White.WithAlpha(CalendarViewTokens.PrimaryTextAlpha)
            ElseIf inMonth Then
                color = textPrimary
            Else
                color = textSecondary.WithAlpha(CalendarViewTokens.OutsideDayTextAlpha)
            End If

            Dim label As String = value.Day.ToString(CultureInfo.CurrentCulture)
            TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, label, rect, dpi, MASTypography.MASTextStyle.Body, color, TypographyTextPrimitives.TextAlign.Center, False)
        End Sub

#End Region

    End Class

End Namespace
