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.DataBinding
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 hierarchy-with-columns visual surface. It owns product-readable
    ''' tree-row and column presentation, selection, expand/collapse interaction, keyboard
    ''' navigation, RTL-aware layout, and readiness evidence. Row storage, binding engines,
    ''' external providers, DataGrid/TreeView wrapper composition, virtualization engines,
    ''' adapters, bridges, and background synchronization remain outside this control.
    ''' </summary>
    Partial Public NotInheritable Class MASTreeGrid
        Inherits MASControlBase
        Implements IMASLayoutParticipant
        Implements IMASIntrinsicSizeContract

#Region "Nested state"

        Private NotInheritable Class TreeGridColumnState
            Friend Sub New(key As String,
                           header As String,
                           widthWeight As Single)
                Me.Key = key
                Me.Header = header
                Me.WidthWeight = widthWeight
            End Sub

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

        Private NotInheritable Class TreeGridRowState
            Friend Sub New(key As String,
                           label As String,
                           values As IEnumerable(Of String),
                           expanded As Boolean)
                Me.Key = key
                Me.Label = label
                Me.Values = New List(Of String)()
                If values IsNot Nothing Then
                    For Each value As String In values
                        Me.Values.Add(MASTreeGridPolicy.NormalizeCellText(value))
                    Next
                End If
                Me.Children = New List(Of TreeGridRowState)()
                Me.Expanded = expanded
            End Sub

            Friend ReadOnly Property Key As String
            Friend Property Label As String
            Friend ReadOnly Property Values As List(Of String)
            Friend ReadOnly Property Children As List(Of TreeGridRowState)
            Friend Property Parent As TreeGridRowState
            Friend Property Expanded As Boolean
        End Class

        Private NotInheritable Class VisibleTreeGridRow
            Friend Sub New(row As TreeGridRowState,
                           depth As Integer)
                Me.Row = row
                Me.Depth = depth
            End Sub

            Friend ReadOnly Property Row As TreeGridRowState
            Friend ReadOnly Property Depth As Integer
        End Class

        Private NotInheritable Class TreeGridRowHit
            Friend Sub New(rowKey As String,
                           rect As SKRect,
                           expanderRect As SKRect)
                Me.RowKey = rowKey
                Me.Rect = rect
                Me.ExpanderRect = expanderRect
            End Sub

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

#End Region

#Region "Fields"
        Private ReadOnly _primitives As New MASVisualPrimitivesPainter()
        Private ReadOnly _columns As New List(Of TreeGridColumnState)()
        Private ReadOnly _roots As New List(Of TreeGridRowState)()
        Private ReadOnly _rowLookup As New Dictionary(Of String, TreeGridRowState)(StringComparer.OrdinalIgnoreCase)
        Private ReadOnly _visibleRowsCache As New List(Of VisibleTreeGridRow)()
        Private ReadOnly _visibleRowIndexByKey As New Dictionary(Of String, Integer)(StringComparer.Ordinal)
        Private ReadOnly _rowHits As New List(Of TreeGridRowHit)()
        Private _rowCount As Integer
        Private _treeRevision As Integer
        Private _visibleRowsCacheRevision As Integer = -1
        Private _title As String = TreeGridTokens.DefaultTitle
        Private _selectedRowKey As String = String.Empty
        Private _hoverRowKey As String = String.Empty
        Private _pressedRowKey As String = String.Empty
        Private _topRowIndex As Integer
        Private _leftColumnIndex As Integer
        Private _rowViewportCapacity As Integer = TreeGridTokens.MaxVisibleRows
        Private _contentRect As SKRect = SKRect.Empty
        Private _gridRect As SKRect = SKRect.Empty
        Private _lastDpi As Single = 1.0F
        Private _itemsSource As IMASItemsSource
        Private ReadOnly _sourceStableKeys As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase)
        Private _sourceRevision As Integer = -1
        Private _isProjectingSource As Boolean
        Private _rowFilterText As String = String.Empty
        Private _sortColumnKey As String = String.Empty
        Private _sortAscending As Boolean = True
        Private _sortByLabel As Boolean = False
        Private _editingRowKey As String = String.Empty
        Private _editingColumnKey As String = String.Empty
        Private _editingOriginalText As String = String.Empty
        Private _editingDraftText As String = String.Empty
        Private _editRevision As Integer

#End Region

#Region "Constructor / Factory"

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

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

#End Region

#Region "Public API"

        Public Event TreeGridChanged As EventHandler
        Public Event SelectedRowChanged As EventHandler
        Public Event RowExpandedChanged As EventHandler

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

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

        Public ReadOnly Property RowCount As Integer
            Get
                Return _rowCount
            End Get
        End Property

        Public ReadOnly Property VisibleRowCount As Integer
            Get
                Return BuildVisibleRows().Count
            End Get
        End Property

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

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

        Public ReadOnly Property FirstVisibleRowIndex As Integer
            Get
                Return Math.Max(0, Math.Min(_topRowIndex, ResolveRowViewportMaxTop()))
            End Get
        End Property

        Public ReadOnly Property LastVisibleRowIndex As Integer
            Get
                Dim visibleCount As Integer = BuildVisibleRows().Count
                If visibleCount <= 0 Then Return -1
                Return Math.Min(visibleCount - 1, FirstVisibleRowIndex + Math.Max(1, _rowViewportCapacity) - 1)
            End Get
        End Property

        Public ReadOnly Property RowViewportCapacity As Integer
            Get
                Return Math.Max(1, _rowViewportCapacity)
            End Get
        End Property

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

        Public Function GetRowKeys() As IReadOnlyList(Of String)
            Dim keys As New List(Of String)()
            CollectRowKeys(_roots, keys)
            Return New ReadOnlyCollection(Of String)(keys)
        End Function

        Public Function AddColumn(columnKey As String,
                                  header As String,
                                  Optional widthWeight As Single = 1.0F) As MASTreeGrid
            If _columns.Count >= TreeGridTokens.MaxColumns Then Return Me
            Dim normalizedKey As String = MASTreeGridPolicy.NormalizeKey(columnKey, "column", _columns.Count + 1)
            If FindColumn(normalizedKey) IsNot Nothing Then Return Me
            _columns.Add(New TreeGridColumnState(normalizedKey, MASTreeGridPolicy.NormalizeColumnHeader(header), MASTreeGridPolicy.NormalizeWidthWeight(widthWeight)))
            RequestSizeLayoutRefreshForSizeAffectingChange("MASTreeGrid.AddColumn")
            InvalidateVisual()
            RaiseTreeGridChangedSafe()
            Return Me
        End Function

        Public Function AddRootRow(rowKey As String,
                                  label As String,
                                  Optional values As IEnumerable(Of String) = Nothing,
                                  Optional expanded As Boolean = True) As MASTreeGrid
            ClearItemsSourceLinkForManualMutation()
            If _rowCount >= TreeGridTokens.MaxRows Then Return Me
            Dim normalizedKey As String = MASTreeGridPolicy.NormalizeKey(rowKey, "row", _rowCount + 1)
            If _rowLookup.ContainsKey(normalizedKey) Then Return Me
            Dim row As New TreeGridRowState(normalizedKey, MASTreeGridPolicy.NormalizeRowLabel(label), values, expanded)
            _roots.Add(row)
            _rowLookup.Add(row.Key, row)
            _rowCount += 1
            If _selectedRowKey.Length = 0 Then _selectedRowKey = row.Key
            ApplyActiveSortToTree()
            InvalidateTreeProjection("MASTreeGrid.AddRootRow")
            NormalizeViewState()
            RequestSizeLayoutRefreshForSizeAffectingChange("MASTreeGrid.AddRootRow")
            InvalidateVisual()
            RaiseTreeGridChangedSafe()
            Return Me
        End Function

        Public Function AddChildRow(parentRowKey As String,
                                    rowKey As String,
                                    label As String,
                                    Optional values As IEnumerable(Of String) = Nothing,
                                    Optional expanded As Boolean = True) As MASTreeGrid
            ClearItemsSourceLinkForManualMutation()
            If _rowCount >= TreeGridTokens.MaxRows Then Return Me
            Dim parent As TreeGridRowState = FindRow(parentRowKey)
            If parent Is Nothing Then Return Me
            Dim normalizedKey As String = MASTreeGridPolicy.NormalizeKey(rowKey, "row", _rowCount + 1)
            If _rowLookup.ContainsKey(normalizedKey) Then Return Me
            Dim row As New TreeGridRowState(normalizedKey, MASTreeGridPolicy.NormalizeRowLabel(label), values, expanded)
            row.Parent = parent
            parent.Children.Add(row)
            _rowLookup.Add(row.Key, row)
            _rowCount += 1
            ApplyActiveSortToTree()
            InvalidateTreeProjection("MASTreeGrid.AddChildRow")
            NormalizeViewState()
            RequestSizeLayoutRefreshForSizeAffectingChange("MASTreeGrid.AddChildRow")
            InvalidateVisual()
            RaiseTreeGridChangedSafe()
            Return Me
        End Function

        Public Function ClearRows() As MASTreeGrid
            ClearItemsSourceLinkForManualMutation()
            If _roots.Count = 0 Then Return Me
            _roots.Clear()
            _rowLookup.Clear()
            _rowCount = 0
            _selectedRowKey = String.Empty
            _hoverRowKey = String.Empty
            _pressedRowKey = String.Empty
            If IsEditingRow Then
                ClearEditState()
                AdvanceEditRevision()
            End If
            _topRowIndex = 0
            InvalidateTreeProjection("MASTreeGrid.ClearRows")
            RequestSizeLayoutRefreshForSizeAffectingChange("MASTreeGrid.ClearRows")
            InvalidateVisual()
            RaiseTreeGridChangedSafe()
            RaiseSelectedRowChangedSafe()
            Return Me
        End Function

        Public Function ClearColumns() As MASTreeGrid
            If _columns.Count = 0 Then Return Me
            _columns.Clear()
            If _editingColumnKey.Length > 0 Then
                ClearEditState()
                AdvanceEditRevision()
            End If
            _leftColumnIndex = 0
            RequestSizeLayoutRefreshForSizeAffectingChange("MASTreeGrid.ClearColumns")
            InvalidateVisual()
            RaiseTreeGridChangedSafe()
            Return Me
        End Function

        Public Function SelectRow(rowKey As String) As Boolean
            Dim row As TreeGridRowState = FindRow(rowKey)
            If row Is Nothing Then Return False
            If EnsureAncestorsExpanded(row) Then InvalidateTreeProjection("MASTreeGrid.SelectRow")
            SetSelection(row.Key, True)
            Return True
        End Function

        Public Function EnsureRowVisible(rowKey As String) As Boolean
            Dim row As TreeGridRowState = FindRow(rowKey)
            If row Is Nothing Then Return False
            If EnsureAncestorsExpanded(row) Then InvalidateTreeProjection("MASTreeGrid.EnsureRowVisible")
            Dim visible As List(Of VisibleTreeGridRow) = BuildVisibleRows()
            Dim index As Integer = FindVisibleIndex(row.Key, visible)
            If index < 0 Then Return False
            Dim oldTop As Integer = _topRowIndex
            _topRowIndex = EnsureVisibleRowIndex(index, _topRowIndex, visible.Count, _rowViewportCapacity)
            If oldTop <> _topRowIndex Then InvalidateVisual()
            Return True
        End Function

        Public Function ScrollRows(deltaRows As Integer) As Boolean
            If deltaRows = 0 Then Return False
            Dim maxTop As Integer = ResolveRowViewportMaxTop()
            Dim oldTop As Integer = _topRowIndex
            _topRowIndex = Math.Max(0, Math.Min(maxTop, _topRowIndex + deltaRows))
            If oldTop = _topRowIndex Then Return False
            InvalidateVisual()
            Return True
        End Function

        Public Function ToggleRow(rowKey As String) As Boolean
            Dim row As TreeGridRowState = FindRow(rowKey)
            If row Is Nothing OrElse row.Children.Count = 0 Then Return False
            Return SetRowExpanded(row.Key, Not row.Expanded)
        End Function

        Public Function SetRowExpanded(rowKey As String,
                                       expanded As Boolean) As Boolean
            Dim row As TreeGridRowState = FindRow(rowKey)
            If row Is Nothing OrElse row.Children.Count = 0 Then Return False
            If row.Expanded = expanded Then Return True
            row.Expanded = expanded
            InvalidateTreeProjection("MASTreeGrid.SetRowExpanded")
            NormalizeViewState()
            RequestSizeLayoutRefreshForSizeAffectingChange("MASTreeGrid.SetRowExpanded")
            InvalidateVisual()
            RaiseRowExpandedChangedSafe()
            RaiseTreeGridChangedSafe()
            Return True
        End Function

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

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

#End Region


    End Class

End Namespace
