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

    ''' <summary>
    ''' Official Nexamas UI premium cross-tab summary surface. It owns visual
    ''' pivot row/column presentation, totals, selection, keyboard navigation,
    ''' RTL-aware layout, and readiness evidence. Query execution, OLAP/Excel
    ''' engines, external providers, persistence, chart coupling, DataGrid wrapper
    ''' composition, adapters, bridges, and background refresh remain outside this control.
    ''' </summary>
    Partial Public NotInheritable Class MASPivotTable
        Inherits MASControlBase
        Implements IMASLayoutParticipant
        Implements IMASIntrinsicSizeContract

#Region "Nested state"

        Private NotInheritable Class PivotColumnState
            Friend Sub New(key As String, header As String)
                Me.Key = key
                Me.Header = header
            End Sub

            Friend ReadOnly Property Key As String
            Friend Property Header As String
        End Class

        Private NotInheritable Class PivotRowState
            Friend Sub New(key As String, label As String)
                Me.Key = key
                Me.Label = label
            End Sub

            Friend ReadOnly Property Key As String
            Friend Property Label As String
        End Class

        Private NotInheritable Class PivotCellHit
            Friend Sub New(rowKey As String, columnKey As String, rect As SKRect)
                Me.RowKey = rowKey
                Me.ColumnKey = columnKey
                Me.Rect = rect
            End Sub

            Friend ReadOnly Property RowKey As String
            Friend ReadOnly Property ColumnKey As String
            Friend ReadOnly Property Rect As SKRect
        End Class

#End Region

#Region "Fields"
        Private ReadOnly _primitives As New MASVisualPrimitivesPainter()
        Private ReadOnly _columns As New List(Of PivotColumnState)()
        Private ReadOnly _rows As New List(Of PivotRowState)()
        Private ReadOnly _rowLookup As New Dictionary(Of String, PivotRowState)(StringComparer.Ordinal)
        Private ReadOnly _columnLookup As New Dictionary(Of String, PivotColumnState)(StringComparer.Ordinal)
        Private ReadOnly _rowIndexByKey As New Dictionary(Of String, Integer)(StringComparer.Ordinal)
        Private ReadOnly _columnIndexByKey As New Dictionary(Of String, Integer)(StringComparer.Ordinal)
        Private ReadOnly _values As New Dictionary(Of String, Decimal)(StringComparer.Ordinal)
        Private ReadOnly _rowTotals As New Dictionary(Of String, Decimal)(StringComparer.Ordinal)
        Private ReadOnly _columnTotals As New Dictionary(Of String, Decimal)(StringComparer.Ordinal)
        Private ReadOnly _cellHits As New List(Of PivotCellHit)()
        Private _grandTotal As Decimal
        Private _summaryRevision As Integer
        Private _summaryCacheRevision As Integer = -1
        Private _title As String = PivotTableTokens.DefaultTitle
        Private _selectedRowKey As String = String.Empty
        Private _selectedColumnKey As String = String.Empty
        Private _hoverRowKey As String = String.Empty
        Private _hoverColumnKey As String = String.Empty
        Private _pressedRowKey As String = String.Empty
        Private _pressedColumnKey As String = String.Empty
        Private _topRowIndex As Integer
        Private _leftColumnIndex As Integer
        Private _rowViewportCapacity As Integer = PivotTableTokens.MaxVisibleRows
        Private _columnViewportCapacity As Integer = PivotTableTokens.MaxVisibleColumns
        Private _contentRect As SKRect = SKRect.Empty
        Private _gridRect As SKRect = SKRect.Empty
        Private _lastDpi As Single = 1.0F

#End Region

#Region "Constructor / Factory"

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

        Public Shared Function Create(Optional title As String = Nothing) As MASPivotTable
            Dim control As New MASPivotTable()
            control._title = MASPivotTablePolicy.NormalizeTitle(title)
            Return control
        End Function

#End Region

#Region "Public API"

        Public Event PivotTableChanged As EventHandler
        Public Event SelectedCellChanged As EventHandler

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

        Public ReadOnly Property RowCount As Integer
            Get
                Return _rows.Count
            End Get
        End Property

        Public ReadOnly Property ColumnCount As Integer
            Get
                Return _columns.Count
            End Get
        End Property

        Public ReadOnly Property SelectedRowKey As String
            Get
                Return _selectedRowKey
            End Get
        End Property

        Public ReadOnly Property SelectedColumnKey As String
            Get
                Return _selectedColumnKey
            End Get
        End Property

        Public ReadOnly Property GrandTotal As Decimal
            Get
                EnsureSummaryCache()
                Return _grandTotal
            End Get
        End Property

        Public ReadOnly Property SummaryText As String
            Get
                Return MASPivotTablePolicy.BuildSummary(RowCount, ColumnCount, GrandTotal)
            End Get
        End Property

        Public Function GetRowKeys() As IReadOnlyList(Of String)
            Dim keys As New List(Of String)()
            For Each row As PivotRowState In _rows
                keys.Add(row.Key)
            Next
            Return New ReadOnlyCollection(Of String)(keys)
        End Function

        Public Function GetColumnKeys() As IReadOnlyList(Of String)
            Dim keys As New List(Of String)()
            For Each column As PivotColumnState In _columns
                keys.Add(column.Key)
            Next
            Return New ReadOnlyCollection(Of String)(keys)
        End Function

        Public Function AddColumn(columnKey As String, header As String) As MASPivotTable
            DetachPivotSourceIfManualMutation("MASPivotTable.AddColumn")
            If _columns.Count >= PivotTableTokens.MaxColumns Then Return Me
            Dim normalizedKey As String = MASPivotTablePolicy.NormalizeKey(columnKey, "column", _columns.Count + 1)
            If FindColumn(normalizedKey) IsNot Nothing Then Return Me
            Dim column As New PivotColumnState(normalizedKey, MASPivotTablePolicy.NormalizeColumnHeader(header))
            _columnIndexByKey.Add(column.Key, _columns.Count)
            _columns.Add(column)
            _columnLookup.Add(column.Key, column)
            _columnTotals(column.Key) = 0D
            InvalidateSummaryCache("MASPivotTable.AddColumn")
            EnsureSelectionExists()
            RequestSizeLayoutRefreshForSizeAffectingChange("MASPivotTable.AddColumn")
            InvalidateVisual()
            RaisePivotTableChangedSafe()
            Return Me
        End Function

        Public Function AddRow(rowKey As String, label As String) As MASPivotTable
            DetachPivotSourceIfManualMutation("MASPivotTable.AddRow")
            If _rows.Count >= PivotTableTokens.MaxRows Then Return Me
            Dim normalizedKey As String = MASPivotTablePolicy.NormalizeKey(rowKey, "row", _rows.Count + 1)
            If FindRow(normalizedKey) IsNot Nothing Then Return Me
            Dim row As New PivotRowState(normalizedKey, MASPivotTablePolicy.NormalizeRowLabel(label))
            _rowIndexByKey.Add(row.Key, _rows.Count)
            _rows.Add(row)
            _rowLookup.Add(row.Key, row)
            _rowTotals(row.Key) = 0D
            InvalidateSummaryCache("MASPivotTable.AddRow")
            EnsureSelectionExists()
            RequestSizeLayoutRefreshForSizeAffectingChange("MASPivotTable.AddRow")
            InvalidateVisual()
            RaisePivotTableChangedSafe()
            Return Me
        End Function

        Public Function SetCellValue(rowKey As String, columnKey As String, value As Decimal) As MASPivotTable
            DetachPivotSourceIfManualMutation("MASPivotTable.SetCellValue")
            Dim row As PivotRowState = FindRow(MASPivotTablePolicy.NormalizeKey(rowKey, "row", 1))
            Dim column As PivotColumnState = FindColumn(MASPivotTablePolicy.NormalizeKey(columnKey, "column", 1))
            If row Is Nothing OrElse column Is Nothing Then Return Me
            Dim cellKey As String = BuildCellKey(row.Key, column.Key)
            Dim oldValue As Decimal = 0D
            _values.TryGetValue(cellKey, oldValue)
            If oldValue = value Then Return Me
            If value = 0D Then
                _values.Remove(cellKey)
            Else
                _values(cellKey) = value
            End If
            InvalidateSummaryCache("MASPivotTable.SetCellValue")
            EnsureSelectionExists()
            InvalidateVisual()
            RaisePivotTableChangedSafe()
            Return Me
        End Function

        Public Function GetCellValue(rowKey As String, columnKey As String) As Decimal
            Dim value As Decimal = 0D
            _values.TryGetValue(BuildCellKey(rowKey, columnKey), value)
            Return value
        End Function

        Public Function GetRowTotal(rowKey As String) As Decimal
            Dim row As PivotRowState = FindRow(rowKey)
            If row Is Nothing Then Return 0D
            EnsureSummaryCache()
            Dim total As Decimal = 0D
            If _rowTotals.TryGetValue(row.Key, total) Then Return total
            Return 0D
        End Function

        Public Function GetColumnTotal(columnKey As String) As Decimal
            Dim column As PivotColumnState = FindColumn(columnKey)
            If column Is Nothing Then Return 0D
            EnsureSummaryCache()
            Dim total As Decimal = 0D
            If _columnTotals.TryGetValue(column.Key, total) Then Return total
            Return 0D
        End Function

        Public Function SelectCell(rowKey As String, columnKey As String) As Boolean
            Dim row As PivotRowState = FindRow(rowKey)
            Dim column As PivotColumnState = FindColumn(columnKey)
            If row Is Nothing OrElse column Is Nothing Then Return False
            Dim rowIndex As Integer = -1
            Dim columnIndex As Integer = -1
            If Not _rowIndexByKey.TryGetValue(row.Key, rowIndex) Then Return False
            If Not _columnIndexByKey.TryGetValue(column.Key, columnIndex) Then Return False
            SetSelection(rowIndex, columnIndex, True)
            Return True
        End Function

        Public Function ClearRows() As MASPivotTable
            DetachPivotSourceIfManualMutation("MASPivotTable.ClearRows") : StopSelectionMotion()
            _rows.Clear()
            _rowLookup.Clear()
            _rowIndexByKey.Clear()
            _values.Clear()
            InvalidateSummaryCache("MASPivotTable.ClearRows")
            _selectedRowKey = String.Empty
            _selectedColumnKey = String.Empty
            _hoverRowKey = String.Empty
            _hoverColumnKey = String.Empty
            _pressedRowKey = String.Empty
            _pressedColumnKey = String.Empty
            _topRowIndex = 0
            RequestSizeLayoutRefreshForSizeAffectingChange("MASPivotTable.ClearRows")
            InvalidateVisual()
            RaisePivotTableChangedSafe()
            Return Me
        End Function

        Public Function ClearColumns() As MASPivotTable
            DetachPivotSourceIfManualMutation("MASPivotTable.ClearColumns") : StopSelectionMotion()
            _columns.Clear()
            _columnLookup.Clear()
            _columnIndexByKey.Clear()
            _values.Clear()
            InvalidateSummaryCache("MASPivotTable.ClearColumns")
            _selectedColumnKey = String.Empty
            _hoverColumnKey = String.Empty
            _pressedColumnKey = String.Empty
            _leftColumnIndex = 0
            RequestSizeLayoutRefreshForSizeAffectingChange("MASPivotTable.ClearColumns")
            InvalidateVisual()
            RaisePivotTableChangedSafe()
            Return Me
        End Function

        Public Function ClearAll() As MASPivotTable
            DetachPivotSourceIfManualMutation("MASPivotTable.ClearAll") : StopSelectionMotion()
            _rows.Clear()
            _columns.Clear()
            _rowLookup.Clear()
            _columnLookup.Clear()
            _rowIndexByKey.Clear()
            _columnIndexByKey.Clear()
            _values.Clear()
            InvalidateSummaryCache("MASPivotTable.ClearAll")
            _selectedRowKey = String.Empty
            _selectedColumnKey = String.Empty
            _hoverRowKey = String.Empty
            _hoverColumnKey = String.Empty
            _pressedRowKey = String.Empty
            _pressedColumnKey = String.Empty
            _topRowIndex = 0
            _leftColumnIndex = 0
            RequestSizeLayoutRefreshForSizeAffectingChange("MASPivotTable.ClearAll")
            InvalidateVisual()
            RaisePivotTableChangedSafe()
            Return Me
        End Function

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

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

#End Region

    End Class

End Namespace
