Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
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 MASKanbanBoard

#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)
            _lastDpi = 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, MASKanbanBoardLayoutPlan.Thresholds)
            Dim plan As MASKanbanBoardLayoutPlan = MASKanbanBoardLayoutPlan.Create(responsiveProfile)
            _lastLayoutPlan = plan
            dpi = plan.Dpi
            _lastDpi = dpi

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

            _columnRects.Clear()
            _cardHitRects.Clear()

            Dim isRtl As Boolean = MASKanbanBoardPolicy.ResolveRightToLeft()
            Dim accent As SKColor = MASKanbanBoardPolicy.ResolveAccentColor(ctx)
            If plan.ShowHeader Then DrawHeader(canvas, ctx, _contentRect, plan, accent, isRtl)
            DrawBoard(canvas, ctx, _contentRect, plan, accent, isRtl)
            DrawKanbanMotionOverlays(canvas, ctx, plan, accent, isRtl)
            If plan.ShowFooter Then DrawFooter(canvas, ctx, _contentRect, plan, isRtl)
        End Sub

        Private Sub DrawHeader(canvas As SKCanvas,
                               ctx As MASThemeContext,
                               content As SKRect,
                               plan As MASKanbanBoardLayoutPlan,
                               accent As SKColor,
                               isRtl As Boolean)
            If plan.HeaderHeightPx <= 1.0F Then Return
            Dim dpi As Single = plan.Dpi
            Dim header As New SKRect(content.Left, content.Top, content.Right, Math.Min(content.Bottom, content.Top + plan.HeaderHeightPx))
            If header.Width <= 1.0F OrElse header.Height <= 1.0F Then Return

            Dim align As TypographyTextPrimitives.TextAlign = If(isRtl, TypographyTextPrimitives.TextAlign.Right, TypographyTextPrimitives.TextAlign.Left)
            Dim titleRect As SKRect = header
            If plan.ShowHeaderSummary Then
                Dim summaryWidth As Single = Math.Min(plan.SummaryWidthPx, header.Width * 0.45F)
                Dim summaryRect As SKRect
                If isRtl Then
                    summaryRect = New SKRect(header.Left, header.Top, header.Left + summaryWidth, header.Bottom)
                    titleRect = New SKRect(summaryRect.Right + plan.BadgeGapPx, header.Top, header.Right, header.Bottom)
                Else
                    summaryRect = New SKRect(header.Right - summaryWidth, header.Top, header.Right, header.Bottom)
                    titleRect = New SKRect(header.Left, header.Top, summaryRect.Left - plan.BadgeGapPx, header.Bottom)
                End If
                If summaryRect.Width > 12.0F AndAlso summaryRect.Height > 1.0F Then
                    TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, SummaryText, summaryRect, dpi, MASTypography.MASTextStyle.Micro, MASKanbanBoardPolicy.ResolveMutedTextColor(ctx), If(isRtl, TypographyTextPrimitives.TextAlign.Left, TypographyTextPrimitives.TextAlign.Right), False)
                End If
            End If

            If titleRect.Width > 12.0F Then
                TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, _title, titleRect, dpi, MASTypography.MASTextStyle.Title, MASKanbanBoardPolicy.ResolvePrimaryTextColor(ctx), align, False)
            End If
        End Sub

        Private Sub DrawBoard(canvas As SKCanvas,
                              ctx As MASThemeContext,
                              content As SKRect,
                              plan As MASKanbanBoardLayoutPlan,
                              accent As SKColor,
                              isRtl As Boolean)
            Dim dpi As Single = plan.Dpi
            Dim top As Single = content.Top + plan.HeaderHeightPx + plan.HeaderGapPx
            Dim bottom As Single = content.Bottom - plan.FooterHeightPx - plan.FooterGapPx
            _boardRect = PixelSnap.SnapRect(New SKRect(content.Left, top, content.Right, bottom), dpi)
            If _boardRect.Width <= 1.0F OrElse _boardRect.Height <= 1.0F Then
                _boardRect = SKRect.Empty
                Return
            End If

            If _columns.Count = 0 Then
                TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, KanbanBoardTokens.EmptyBoardText, _boardRect, dpi, MASTypography.MASTextStyle.Body, MASKanbanBoardPolicy.ResolveMutedTextColor(ctx).WithAlpha(KanbanBoardTokens.EmptyTextAlpha), TypographyTextPrimitives.TextAlign.Center, False)
                Return
            End If

            NormalizeSelection()
            _leftColumnIndex = plan.EnsureVisibleColumnIndex(_selectedColumnIndex, _leftColumnIndex, _columns.Count)
            Dim visibleCount As Integer = Math.Max(1, Math.Min(plan.MaxVisibleColumns, _columns.Count - _leftColumnIndex))
            Dim gap As Single = plan.ColumnGapPx
            Dim columnWidth As Single = (_boardRect.Width - gap * CSng(visibleCount - 1)) / CSng(visibleCount)
            If columnWidth <= 1.0F Then Return

            For visualIndex As Integer = 0 To visibleCount - 1
                Dim columnIndex As Integer = _leftColumnIndex + visualIndex
                Dim actualVisualIndex As Integer = If(isRtl, visibleCount - 1 - visualIndex, visualIndex)
                Dim left As Single = _boardRect.Left + CSng(actualVisualIndex) * (columnWidth + gap)
                Dim rect As SKRect = PixelSnap.SnapRect(New SKRect(left, _boardRect.Top, left + columnWidth, _boardRect.Bottom), dpi)
                _columnRects.Add(rect)
                DrawColumn(canvas, ctx, rect, plan, accent, isRtl, columnIndex)
            Next
        End Sub

        Private Sub DrawColumn(canvas As SKCanvas,
                               ctx As MASThemeContext,
                               rect As SKRect,
                               plan As MASKanbanBoardLayoutPlan,
                               accent As SKColor,
                               isRtl As Boolean,
                               columnIndex As Integer)
            If columnIndex < 0 OrElse columnIndex >= _columns.Count Then Return
            Dim dpi As Single = plan.Dpi
            Dim column As KanbanColumnState = _columns(columnIndex)
            Dim selectedColumn As Boolean = (columnIndex = _selectedColumnIndex)
            Dim radius As Single = plan.ColumnRadiusPx
            Dim dropTarget As Boolean = (_draggingCard AndAlso columnIndex = _dragTargetColumnIndex AndAlso columnIndex <> _pressedColumnIndex)
            Dim fillAlpha As Byte = If(selectedColumn, KanbanBoardTokens.ColumnSelectedAlpha, KanbanBoardTokens.ColumnFillAlpha)
            If dropTarget Then fillAlpha = CByte(Math.Min(255, CInt(KanbanBoardTokens.ColumnSelectedAlpha) + 34))
            Using fill As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = accent.WithAlpha(fillAlpha)}
                canvas.DrawRoundRect(rect, radius, radius, fill)
            End Using
            Using stroke As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Stroke, .StrokeWidth = Math.Max(1.0F, dpi), .Color = accent.WithAlpha(KanbanBoardTokens.ColumnStrokeAlpha)}
                canvas.DrawRoundRect(Inset(rect, 0.5F * dpi), Math.Max(1.0F, radius - 0.5F * dpi), Math.Max(1.0F, radius - 0.5F * dpi), stroke)
            End Using

            Dim inner As SKRect = Inset(rect, plan.ColumnPaddingPx)
            If inner.Width <= 1.0F OrElse inner.Height <= 1.0F Then Return

            Dim header As New SKRect(inner.Left, inner.Top, inner.Right, Math.Min(inner.Bottom, inner.Top + plan.ColumnHeaderHeightPx))
            If header.Height > 1.0F Then
                Dim titleRect As SKRect = header
                If plan.ShowColumnCountPill Then
                    Dim countText As String = column.Cards.Count.ToString(CultureInfo.CurrentCulture)
                    Dim countWidth As Single = Math.Min(Math.Max(plan.BadgeWidthPx * 0.74F, 24.0F * dpi), Math.Max(18.0F * dpi, header.Width * 0.34F))
                    Dim countRect As SKRect
                    If isRtl Then
                        countRect = New SKRect(header.Left, header.Top + 5.0F * dpi, header.Left + countWidth, header.Bottom - 5.0F * dpi)
                        titleRect = New SKRect(countRect.Right + plan.BadgeGapPx, header.Top, header.Right, header.Bottom)
                    Else
                        countRect = New SKRect(header.Right - countWidth, header.Top + 5.0F * dpi, header.Right, header.Bottom - 5.0F * dpi)
                        titleRect = New SKRect(header.Left, header.Top, countRect.Left - plan.BadgeGapPx, header.Bottom)
                    End If
                    If countRect.Width > 6.0F AndAlso countRect.Height > 4.0F Then DrawPill(canvas, ctx, countRect, plan, countText, accent)
                End If

                If titleRect.Width > 8.0F Then
                    TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, column.Title, titleRect, dpi, MASTypography.MASTextStyle.Body, MASKanbanBoardPolicy.ResolvePrimaryTextColor(ctx), If(isRtl, TypographyTextPrimitives.TextAlign.Right, TypographyTextPrimitives.TextAlign.Left), False)
                End If
            End If

            Dim listTop As Single = If(header.Height > 1.0F, header.Bottom + plan.CardGapPx, inner.Top)
            Dim listRect As New SKRect(inner.Left, listTop, inner.Right, inner.Bottom)
            If listRect.Width <= 1.0F OrElse listRect.Height <= 1.0F Then Return
            If column.Cards.Count = 0 Then
                TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, KanbanBoardTokens.EmptyColumnText, listRect, dpi, MASTypography.MASTextStyle.Micro, MASKanbanBoardPolicy.ResolveMutedTextColor(ctx).WithAlpha(KanbanBoardTokens.EmptyTextAlpha), TypographyTextPrimitives.TextAlign.Center, False)
                Return
            End If

            column.TopCardIndex = plan.EnsureVisibleCardIndex(If(columnIndex = _selectedColumnIndex, _selectedCardIndex, -1), column.TopCardIndex, column.Cards.Count)
            Dim rowHeight As Single = plan.CardHeightPx
            Dim rowGap As Single = plan.CardGapPx
            Dim maxRowsFromSpace As Integer = Math.Max(1, CInt(Math.Floor((listRect.Height + rowGap) / (rowHeight + rowGap))))
            Dim visibleRows As Integer = Math.Max(1, Math.Min(plan.MaxVisibleCardsPerColumn, Math.Min(maxRowsFromSpace, column.Cards.Count - column.TopCardIndex)))
            For i As Integer = 0 To visibleRows - 1
                Dim cardIndex As Integer = column.TopCardIndex + i
                Dim rowTop As Single = listRect.Top + CSng(i) * (rowHeight + rowGap)
                Dim cardRect As SKRect = PixelSnap.SnapRect(New SKRect(listRect.Left, rowTop, listRect.Right, Math.Min(listRect.Bottom, rowTop + rowHeight)), dpi)
                If cardRect.Width <= 1.0F OrElse cardRect.Height <= 1.0F Then Continue For
                _cardHitRects.Add(New KanbanCardHit(columnIndex, cardIndex, cardRect))
                DrawCard(canvas, ctx, cardRect, plan, accent, isRtl, columnIndex, cardIndex)
            Next
        End Sub

        Private Sub DrawCard(canvas As SKCanvas,
                             ctx As MASThemeContext,
                             rect As SKRect,
                             plan As MASKanbanBoardLayoutPlan,
                             accent As SKColor,
                             isRtl As Boolean,
                             columnIndex As Integer,
                             cardIndex As Integer)
            Dim dpi As Single = plan.Dpi
            Dim column As KanbanColumnState = _columns(columnIndex)
            Dim card As KanbanCardState = column.Cards(cardIndex)
            Dim selected As Boolean = (columnIndex = _selectedColumnIndex AndAlso cardIndex = _selectedCardIndex) AndAlso Not ShouldSuppressKanbanCardSelectionFill(columnIndex, cardIndex)
            Dim hover As Boolean = (columnIndex = _hoverColumnIndex AndAlso cardIndex = _hoverCardIndex)
            Dim pressed As Boolean = (columnIndex = _pressedColumnIndex AndAlso cardIndex = _pressedCardIndex)
            Dim alpha As Byte = KanbanBoardTokens.CardFillAlpha
            If selected Then alpha = KanbanBoardTokens.CardSelectedAlpha
            If hover Then alpha = KanbanBoardTokens.CardHoverAlpha
            If pressed Then alpha = KanbanBoardTokens.CardPressedAlpha
            Dim radius As Single = plan.CardRadiusPx
            Using fill As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = accent.WithAlpha(alpha)}
                canvas.DrawRoundRect(rect, radius, radius, fill)
            End Using
            Using stroke As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Stroke, .StrokeWidth = Math.Max(1.0F, dpi), .Color = accent.WithAlpha(KanbanBoardTokens.CardStrokeAlpha)}
                canvas.DrawRoundRect(Inset(rect, 0.5F * dpi), Math.Max(1.0F, radius - 0.5F * dpi), Math.Max(1.0F, radius - 0.5F * dpi), stroke)
            End Using
            If selected Then
                Dim marker As SKRect = If(isRtl,
                                          New SKRect(rect.Right - plan.MarkerWidthPx - 2.0F * dpi, rect.Top + plan.MarkerInsetPx, rect.Right - 2.0F * dpi, rect.Bottom - plan.MarkerInsetPx),
                                          New SKRect(rect.Left + 2.0F * dpi, rect.Top + plan.MarkerInsetPx, rect.Left + 2.0F * dpi + plan.MarkerWidthPx, rect.Bottom - plan.MarkerInsetPx))
                If marker.Width > 0.0F AndAlso marker.Height > 0.0F Then
                    Using markerPaint As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = accent.WithAlpha(220)}
                        canvas.DrawRoundRect(marker, 2.0F * dpi, 2.0F * dpi, markerPaint)
                    End Using
                End If
            End If

            Dim inner As SKRect = Inset(rect, plan.CardInnerPaddingPx)
            If inner.Width <= 1.0F OrElse inner.Height <= 1.0F Then Return

            Dim badgeRect As SKRect = SKRect.Empty
            Dim hasBadge As Boolean = plan.ShowCardBadge AndAlso card.BadgeText.Length > 0 AndAlso inner.Width > plan.BadgeWidthPx + plan.BadgeGapPx + 40.0F * dpi
            If hasBadge Then
                If isRtl Then
                    badgeRect = New SKRect(inner.Left, inner.Top, inner.Left + plan.BadgeWidthPx, inner.Top + plan.BadgeHeightPx)
                    inner.Left = badgeRect.Right + plan.BadgeGapPx
                Else
                    badgeRect = New SKRect(inner.Right - plan.BadgeWidthPx, inner.Top, inner.Right, inner.Top + plan.BadgeHeightPx)
                    inner.Right = badgeRect.Left - plan.BadgeGapPx
                End If
            End If

            Dim titleHeight As Single = Math.Min(inner.Height, If(plan.ShowCardDetail, 24.0F * dpi * plan.ResponsiveProfile.ContentScale, inner.Height))
            Dim titleRect As New SKRect(inner.Left, inner.Top, inner.Right, inner.Top + titleHeight)
            Dim detailRect As New SKRect(inner.Left, titleRect.Bottom + 2.0F * dpi, inner.Right, inner.Bottom)
            If titleRect.Width > 1.0F AndAlso titleRect.Height > 1.0F Then
                TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, card.Title, titleRect, dpi, MASTypography.MASTextStyle.Body, MASKanbanBoardPolicy.ResolvePrimaryTextColor(ctx), If(isRtl, TypographyTextPrimitives.TextAlign.Right, TypographyTextPrimitives.TextAlign.Left), False)
            End If
            If plan.ShowCardDetail AndAlso card.Detail.Length > 0 AndAlso detailRect.Width > 1.0F AndAlso detailRect.Height > 1.0F Then
                TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, card.Detail, detailRect, dpi, MASTypography.MASTextStyle.Micro, MASKanbanBoardPolicy.ResolveMutedTextColor(ctx), If(isRtl, TypographyTextPrimitives.TextAlign.Right, TypographyTextPrimitives.TextAlign.Left), False)
            End If
            If hasBadge Then DrawPill(canvas, ctx, badgeRect, plan, card.BadgeText, accent)
        End Sub

        Private Sub DrawPill(canvas As SKCanvas,
                             ctx As MASThemeContext,
                             rect As SKRect,
                             plan As MASKanbanBoardLayoutPlan,
                             text As String,
                             accent As SKColor)
            If rect.Width <= 1.0F OrElse rect.Height <= 1.0F Then Return
            Dim dpi As Single = plan.Dpi
            Dim radius As Single = Math.Min(plan.BadgeRadiusPx, rect.Height * 0.5F)
            Using fill As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = accent.WithAlpha(KanbanBoardTokens.BadgeFillAlpha)}
                canvas.DrawRoundRect(rect, radius, radius, fill)
            End Using
            TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, text, Inset(rect, 3.0F * dpi), dpi, MASTypography.MASTextStyle.Micro, MASKanbanBoardPolicy.ResolvePrimaryTextColor(ctx), TypographyTextPrimitives.TextAlign.Center, False)
        End Sub

        Private Sub DrawFooter(canvas As SKCanvas,
                               ctx As MASThemeContext,
                               content As SKRect,
                               plan As MASKanbanBoardLayoutPlan,
                               isRtl As Boolean)
            If plan.FooterHeightPx <= 1.0F Then Return
            Dim dpi As Single = plan.Dpi
            Dim footer As New SKRect(content.Left, Math.Max(content.Top, content.Bottom - plan.FooterHeightPx), content.Right, content.Bottom)
            If footer.Width <= 1.0F OrElse footer.Height <= 1.0F Then Return
            Dim selectedKey As String = SelectedCardKey
            Dim text As String = If(selectedKey.Length = 0 OrElse plan.Mode = MASResponsiveLayoutMode.Compact,
                                    SummaryText,
                                    SummaryText & " • selected " & selectedKey)
            TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, text, footer, dpi, MASTypography.MASTextStyle.Micro, MASKanbanBoardPolicy.ResolveMutedTextColor(ctx), If(isRtl, TypographyTextPrimitives.TextAlign.Right, TypographyTextPrimitives.TextAlign.Left), False)
        End Sub

#End Region

    End Class

End Namespace
