Option Strict On
Option Explicit On

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

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

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

            _cellHits.Clear()
            Dim isRtl As Boolean = MASPivotTablePolicy.ResolveRightToLeft()
            Dim accent As SKColor = MASPivotTablePolicy.ResolveAccentColor(ctx)
            If plan.ShowHeader Then DrawHeader(canvas, ctx, _contentRect, plan, isRtl)
            DrawGrid(canvas, ctx, _contentRect, 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 MASPivotTableLayoutPlan,
                               isRtl As Boolean)
            Dim dpi As Single = plan.Dpi
            Dim header As New SKRect(content.Left, content.Top, content.Right, content.Top + plan.HeaderHeightPx)
            TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, _title, header, dpi, MASTypography.MASTextStyle.Title, MASPivotTablePolicy.ResolvePrimaryTextColor(ctx), If(isRtl, TypographyTextPrimitives.TextAlign.Right, TypographyTextPrimitives.TextAlign.Left), False)

            If Not plan.ShowHeaderSummary Then Return
            Dim summaryWidth As Single = Math.Min(plan.SummaryWidthPx, header.Width * 0.5F)
            Dim summaryRect As SKRect = If(isRtl,
                                           New SKRect(header.Left, header.Top, header.Left + summaryWidth, header.Bottom),
                                           New SKRect(header.Right - summaryWidth, header.Top, header.Right, header.Bottom))
            TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, SummaryText, summaryRect, dpi, MASTypography.MASTextStyle.Micro, MASPivotTablePolicy.ResolveMutedTextColor(ctx), If(isRtl, TypographyTextPrimitives.TextAlign.Left, TypographyTextPrimitives.TextAlign.Right), False)
        End Sub

        Private Sub DrawGrid(canvas As SKCanvas,
                             ctx As MASThemeContext,
                             content As SKRect,
                             plan As MASPivotTableLayoutPlan,
                             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
            _gridRect = PixelSnap.SnapRect(New SKRect(content.Left, top, content.Right, bottom), dpi)
            If _gridRect.Width <= 1.0F OrElse _gridRect.Height <= 1.0F Then Return

            Using fill As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = accent.WithAlpha(PivotTableTokens.GridFillAlpha)}
                canvas.DrawRoundRect(_gridRect, PivotTableTokens.GridRadiusDip * dpi, PivotTableTokens.GridRadiusDip * dpi, fill)
            End Using
            Using stroke As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Stroke, .StrokeWidth = Math.Max(1.0F, dpi), .Color = accent.WithAlpha(PivotTableTokens.GridStrokeAlpha)}
                canvas.DrawRoundRect(_gridRect, PivotTableTokens.GridRadiusDip * dpi, PivotTableTokens.GridRadiusDip * dpi, stroke)
            End Using

            Dim headerRect As New SKRect(_gridRect.Left, _gridRect.Top, _gridRect.Right, _gridRect.Top + plan.GridHeaderHeightPx)
            DrawColumnHeaders(canvas, ctx, headerRect, plan, accent, isRtl)
            Dim rowsRect As New SKRect(_gridRect.Left + plan.GridInnerInsetPx,
                                       headerRect.Bottom + plan.GridInnerInsetPx,
                                       _gridRect.Right - plan.GridInnerInsetPx,
                                       _gridRect.Bottom - plan.GridInnerInsetPx)
            DrawRows(canvas, ctx, rowsRect, plan, accent, isRtl)
        End Sub

        Private Sub DrawColumnHeaders(canvas As SKCanvas,
                                      ctx As MASThemeContext,
                                      headerRect As SKRect,
                                      plan As MASPivotTableLayoutPlan,
                                      accent As SKColor,
                                      isRtl As Boolean)
            Dim dpi As Single = plan.Dpi
            Using fill As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = accent.WithAlpha(PivotTableTokens.HeaderFillAlpha)}
                canvas.DrawRoundRect(headerRect, PivotTableTokens.GridRadiusDip * dpi, PivotTableTokens.GridRadiusDip * dpi, fill)
            End Using

            Dim rowHeaderRect As SKRect = GetRowHeaderRect(headerRect, dpi, isRtl)
            Dim totalRect As SKRect = GetTotalColumnRect(headerRect, rowHeaderRect, dpi, isRtl)
            Dim valueRects As List(Of SKRect) = GetVisibleValueColumnRects(headerRect, rowHeaderRect, totalRect, dpi, isRtl, plan.MaxVisibleColumns)
            Dim align As TypographyTextPrimitives.TextAlign = If(isRtl, TypographyTextPrimitives.TextAlign.Right, TypographyTextPrimitives.TextAlign.Left)
            TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, PivotTableTokens.RowHeaderText, InsetHorizontal(rowHeaderRect, PivotTableTokens.CellPaddingDip * dpi), dpi, MASTypography.MASTextStyle.Micro, MASPivotTablePolicy.ResolveMutedTextColor(ctx), align, False)
            For i As Integer = 0 To valueRects.Count - 1
                Dim columnIndex As Integer = _leftColumnIndex + i
                If columnIndex < _columns.Count Then
                    Dim headerFill As Boolean = String.Equals(_columns(columnIndex).Key, _selectedColumnKey, StringComparison.Ordinal) OrElse String.Equals(_columns(columnIndex).Key, _hoverColumnKey, StringComparison.Ordinal)
                    If headerFill Then
                        Using fill As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = accent.WithAlpha(34)}
                            canvas.DrawRoundRect(valueRects(i), PivotTableTokens.CellRadiusDip * dpi, PivotTableTokens.CellRadiusDip * dpi, fill)
                        End Using
                    End If
                    TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, _columns(columnIndex).Header, InsetHorizontal(valueRects(i), PivotTableTokens.CellPaddingDip * dpi), dpi, MASTypography.MASTextStyle.Micro, If(headerFill, MASPivotTablePolicy.ResolvePrimaryTextColor(ctx), MASPivotTablePolicy.ResolveMutedTextColor(ctx)), align, False)
                    DrawVerticalDivider(canvas, valueRects(i), dpi, accent)
                End If
            Next
            TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, PivotTableTokens.TotalHeaderText, InsetHorizontal(totalRect, PivotTableTokens.CellPaddingDip * dpi), dpi, MASTypography.MASTextStyle.Micro, MASPivotTablePolicy.ResolvePrimaryTextColor(ctx), If(isRtl, TypographyTextPrimitives.TextAlign.Right, TypographyTextPrimitives.TextAlign.Left), False)
            DrawVerticalDivider(canvas, totalRect, dpi, accent)
        End Sub

        Private Sub DrawRows(canvas As SKCanvas,
                             ctx As MASThemeContext,
                             rowsRect As SKRect,
                             plan As MASPivotTableLayoutPlan,
                             accent As SKColor,
                             isRtl As Boolean)
            Dim dpi As Single = plan.Dpi
            If _rows.Count = 0 OrElse _columns.Count = 0 Then
                TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, PivotTableTokens.EmptyPivotText, rowsRect, dpi, MASTypography.MASTextStyle.Body, MASPivotTablePolicy.ResolveMutedTextColor(ctx).WithAlpha(PivotTableTokens.EmptyTextAlpha), TypographyTextPrimitives.TextAlign.Center, False)
                Return
            End If

            NormalizeViewState()
            Dim rowHeight As Single = plan.RowHeightPx
            Dim rowGap As Single = plan.RowGapPx
            Dim rowHeaderTemplate As SKRect = GetRowHeaderRect(rowsRect, dpi, isRtl)
            Dim totalTemplate As SKRect = GetTotalColumnRect(rowsRect, rowHeaderTemplate, dpi, isRtl)
            Dim valueTemplates As List(Of SKRect) = GetVisibleValueColumnRects(rowsRect, rowHeaderTemplate, totalTemplate, dpi, isRtl, plan.MaxVisibleColumns)
            Dim maxRowsFromSpace As Integer = Math.Max(1, CInt(Math.Floor((rowsRect.Height + rowGap) / (rowHeight + rowGap))))
            _rowViewportCapacity = Math.Max(1, Math.Min(plan.MaxVisibleRows, maxRowsFromSpace))
            _topRowIndex = MASPivotTablePolicy.EnsureVisibleIndex(GetSelectedRowIndex(), _topRowIndex, _rows.Count, _rowViewportCapacity)
            Dim maxRows As Integer = Math.Min(_rowViewportCapacity, _rows.Count - _topRowIndex)
            If maxRows <= 0 Then Return

            For visualRow As Integer = 0 To maxRows - 1
                Dim rowIndex As Integer = _topRowIndex + visualRow
                Dim top As Single = rowsRect.Top + CSng(visualRow) * (rowHeight + rowGap)
                Dim rowRect As SKRect = PixelSnap.SnapRect(New SKRect(rowsRect.Left, top, rowsRect.Right, top + rowHeight), dpi)
                DrawRow(canvas, ctx, rowRect, rowHeaderTemplate, valueTemplates, totalTemplate, rowIndex, dpi, accent, isRtl)
            Next

            DrawSelectionMotionOverlay(canvas, rowsRect, valueTemplates, rowHeight, rowGap, dpi, accent)
        End Sub

        Private Sub DrawRow(canvas As SKCanvas,
                            ctx As MASThemeContext,
                            rowRect As SKRect,
                            rowHeaderTemplate As SKRect,
                            valueTemplates As List(Of SKRect),
                            totalTemplate As SKRect,
                            rowIndex As Integer,
                            dpi As Single,
                            accent As SKColor,
                            isRtl As Boolean)
            Dim row As PivotRowState = _rows(rowIndex)
            Dim rowHeaderRect As New SKRect(rowHeaderTemplate.Left, rowRect.Top, rowHeaderTemplate.Right, rowRect.Bottom)
            Dim totalRect As New SKRect(totalTemplate.Left, rowRect.Top, totalTemplate.Right, rowRect.Bottom)
            Dim rowActive As Boolean = String.Equals(row.Key, _selectedRowKey, StringComparison.Ordinal) OrElse String.Equals(row.Key, _hoverRowKey, StringComparison.Ordinal)
            If rowActive Then
                Using fill As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = accent.WithAlpha(30)}
                    canvas.DrawRoundRect(rowHeaderRect, PivotTableTokens.CellRadiusDip * dpi, PivotTableTokens.CellRadiusDip * dpi, fill)
                End Using
            End If
            TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, row.Label, InsetHorizontal(rowHeaderRect, PivotTableTokens.CellPaddingDip * dpi), dpi, MASTypography.MASTextStyle.Body, If(rowActive, MASPivotTablePolicy.ResolvePrimaryTextColor(ctx), MASPivotTablePolicy.ResolveMutedTextColor(ctx)), If(isRtl, TypographyTextPrimitives.TextAlign.Right, TypographyTextPrimitives.TextAlign.Left), False)

            For i As Integer = 0 To valueTemplates.Count - 1
                Dim columnIndex As Integer = _leftColumnIndex + i
                If columnIndex >= _columns.Count Then Continue For
                Dim column As PivotColumnState = _columns(columnIndex)
                Dim cellRect As New SKRect(valueTemplates(i).Left, rowRect.Top, valueTemplates(i).Right, rowRect.Bottom)
                DrawValueCell(canvas, ctx, cellRect, row, column, rowIndex, columnIndex, dpi, accent, isRtl)
                DrawVerticalDivider(canvas, cellRect, dpi, accent)
            Next

            DrawTotalCell(canvas, ctx, totalRect, MASPivotTablePolicy.FormatValue(GetRowTotal(row.Key)), dpi, accent, isRtl)
            DrawVerticalDivider(canvas, totalRect, dpi, accent)
        End Sub

        Private Sub DrawValueCell(canvas As SKCanvas,
                                  ctx As MASThemeContext,
                                  rect As SKRect,
                                  row As PivotRowState,
                                  column As PivotColumnState,
                                  rowIndex As Integer,
                                  columnIndex As Integer,
                                  dpi As Single,
                                  accent As SKColor,
                                  isRtl As Boolean)
            Dim selected As Boolean = String.Equals(row.Key, _selectedRowKey, StringComparison.Ordinal) AndAlso String.Equals(column.Key, _selectedColumnKey, StringComparison.Ordinal)
            If selected AndAlso IsSelectionMotionEndpoint(row.Key, column.Key) Then selected = False
            Dim hovered As Boolean = String.Equals(row.Key, _hoverRowKey, StringComparison.Ordinal) AndAlso String.Equals(column.Key, _hoverColumnKey, StringComparison.Ordinal)
            Dim pressed As Boolean = String.Equals(row.Key, _pressedRowKey, StringComparison.Ordinal) AndAlso String.Equals(column.Key, _pressedColumnKey, StringComparison.Ordinal)
            Dim rowRelated As Boolean = String.Equals(row.Key, _selectedRowKey, StringComparison.Ordinal) OrElse String.Equals(row.Key, _hoverRowKey, StringComparison.Ordinal)
            Dim columnRelated As Boolean = String.Equals(column.Key, _selectedColumnKey, StringComparison.Ordinal) OrElse String.Equals(column.Key, _hoverColumnKey, StringComparison.Ordinal)
            Dim fillColor As SKColor = SKColors.Transparent
            If rowRelated OrElse columnRelated Then fillColor = accent.WithAlpha(16)
            If selected Then fillColor = accent.WithAlpha(PivotTableTokens.CellSelectedAlpha)
            If hovered Then fillColor = accent.WithAlpha(PivotTableTokens.CellHoverAlpha)
            If pressed Then fillColor = accent.WithAlpha(PivotTableTokens.CellPressedAlpha)
            If fillColor.Alpha > 0 Then
                Using fill As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = fillColor}
                    canvas.DrawRoundRect(rect, PivotTableTokens.CellRadiusDip * dpi, PivotTableTokens.CellRadiusDip * dpi, fill)
                End Using
            End If
            If selected Then
                Using stroke As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Stroke, .StrokeWidth = Math.Max(1.0F, dpi), .Color = accent.WithAlpha(150)}
                    canvas.DrawRoundRect(rect, PivotTableTokens.CellRadiusDip * dpi, PivotTableTokens.CellRadiusDip * dpi, stroke)
                End Using
            End If
            TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, MASPivotTablePolicy.FormatValue(GetCellValue(row.Key, column.Key)), InsetHorizontal(rect, PivotTableTokens.CellPaddingDip * dpi), dpi, MASTypography.MASTextStyle.Body, MASPivotTablePolicy.ResolvePrimaryTextColor(ctx), If(isRtl, TypographyTextPrimitives.TextAlign.Left, TypographyTextPrimitives.TextAlign.Right), False)
            _cellHits.Add(New PivotCellHit(row.Key, column.Key, rect))
        End Sub


        Private Function ResolveSelectionMotionCellRect(rowKey As String,
                                                        columnKey As String,
                                                        rowsRect As SKRect,
                                                        valueTemplates As List(Of SKRect),
                                                        rowHeight As Single,
                                                        rowGap As Single,
                                                        dpi As Single) As SKRect
            If String.IsNullOrWhiteSpace(rowKey) OrElse String.IsNullOrWhiteSpace(columnKey) Then Return SKRect.Empty
            Dim rowIndex As Integer = -1
            Dim columnIndex As Integer = -1
            If Not _rowIndexByKey.TryGetValue(rowKey, rowIndex) Then Return SKRect.Empty
            If Not _columnIndexByKey.TryGetValue(columnKey, columnIndex) Then Return SKRect.Empty
            If rowIndex < _topRowIndex OrElse rowIndex >= _topRowIndex + Math.Max(1, _rowViewportCapacity) Then Return SKRect.Empty
            If columnIndex < _leftColumnIndex OrElse columnIndex >= _leftColumnIndex + Math.Max(1, _columnViewportCapacity) Then Return SKRect.Empty
            Dim visualRow As Integer = rowIndex - _topRowIndex
            Dim visualColumn As Integer = columnIndex - _leftColumnIndex
            If valueTemplates Is Nothing OrElse visualColumn < 0 OrElse visualColumn >= valueTemplates.Count Then Return SKRect.Empty
            Dim template As SKRect = valueTemplates(visualColumn)
            Dim top As Single = rowsRect.Top + CSng(visualRow) * (rowHeight + rowGap)
            Return PixelSnap.SnapRect(New SKRect(template.Left, top, template.Right, top + rowHeight), dpi)
        End Function

        Private Sub DrawSelectionMotionOverlay(canvas As SKCanvas,
                                               rowsRect As SKRect,
                                               valueTemplates As List(Of SKRect),
                                               rowHeight As Single,
                                               rowGap As Single,
                                               dpi As Single,
                                               accent As SKColor)
            If canvas Is Nothing OrElse Not IsSelectionMotionActive() Then Return
            Dim fromRect As SKRect = ResolveSelectionMotionCellRect(_selectionMotionFromRowKey, _selectionMotionFromColumnKey, rowsRect, valueTemplates, rowHeight, rowGap, dpi)
            Dim toRect As SKRect = ResolveSelectionMotionCellRect(_selectionMotionToRowKey, _selectionMotionToColumnKey, rowsRect, valueTemplates, rowHeight, rowGap, dpi)
            If fromRect.IsEmpty OrElse toRect.IsEmpty Then Return

            Dim progress As Single = Math.Max(0.0F, Math.Min(1.0F, _selectionMotionProgress))
            Dim rect As New SKRect(fromRect.Left + (toRect.Left - fromRect.Left) * progress,
                                   fromRect.Top + (toRect.Top - fromRect.Top) * progress,
                                   fromRect.Right + (toRect.Right - fromRect.Right) * progress,
                                   fromRect.Bottom + (toRect.Bottom - fromRect.Bottom) * progress)
            rect = PixelSnap.SnapRect(rect, dpi)

            Using fill As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = accent.WithAlpha(PivotTableTokens.CellSelectedAlpha)},
                  stroke As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Stroke, .StrokeWidth = Math.Max(1.0F, dpi), .Color = accent.WithAlpha(150)}
                canvas.DrawRoundRect(rect, PivotTableTokens.CellRadiusDip * dpi, PivotTableTokens.CellRadiusDip * dpi, fill)
                canvas.DrawRoundRect(rect, PivotTableTokens.CellRadiusDip * dpi, PivotTableTokens.CellRadiusDip * dpi, stroke)
            End Using
        End Sub

        Private Sub DrawTotalCell(canvas As SKCanvas,
                                  ctx As MASThemeContext,
                                  rect As SKRect,
                                  text As String,
                                  dpi As Single,
                                  accent As SKColor,
                                  isRtl As Boolean)
            Using fill As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Fill, .Color = accent.WithAlpha(CByte(Math.Min(255, CInt(PivotTableTokens.TotalFillAlpha) + 18)))}
                canvas.DrawRoundRect(rect, PivotTableTokens.CellRadiusDip * dpi, PivotTableTokens.CellRadiusDip * dpi, fill)
            End Using
            TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, text, InsetHorizontal(rect, PivotTableTokens.CellPaddingDip * dpi), dpi, MASTypography.MASTextStyle.Body, MASPivotTablePolicy.ResolvePrimaryTextColor(ctx), If(isRtl, TypographyTextPrimitives.TextAlign.Left, TypographyTextPrimitives.TextAlign.Right), False)
        End Sub

        Private Sub DrawFooter(canvas As SKCanvas,
                               ctx As MASThemeContext,
                               content As SKRect,
                               plan As MASPivotTableLayoutPlan,
                               isRtl As Boolean)
            Dim dpi As Single = plan.Dpi
            Dim footer As New SKRect(content.Left, content.Bottom - plan.FooterHeightPx, content.Right, content.Bottom)
            Dim text As String = If(_selectedRowKey.Length = 0 OrElse _selectedColumnKey.Length = 0,
                                    "No cell selected",
                                    "Selected: " & _selectedRowKey & " / " & _selectedColumnKey & " = " & MASPivotTablePolicy.FormatValue(GetCellValue(_selectedRowKey, _selectedColumnKey)))
            TypographyTextPrimitives.DrawSingleLineText(canvas, ctx, text, footer, dpi, MASTypography.MASTextStyle.Micro, MASPivotTablePolicy.ResolveMutedTextColor(ctx), If(isRtl, TypographyTextPrimitives.TextAlign.Right, TypographyTextPrimitives.TextAlign.Left), False)
        End Sub

#End Region

    End Class

End Namespace
