Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports Nexamas.UI.Components

Namespace Nexamas.UI.Virtualization

    ''' <summary>
    ''' VIRT-04 runtime gate. It proves that source/data projection scale is an explicit surface-owned contract and not an
    ''' accidental claim hidden inside render/window virtualization. The gate executes 100k MASDataView filter/sort/group and
    ''' 100k MASTreeView expand/flatten paths without creating a renderer, MASDataGrid, viewport plan, or virtualization controller.
    ''' </summary>
    Friend NotInheritable Class MASVirtualizationProjectionScaleGate
        Friend Const RequiredLogicalItemCount As Integer = 100000
        Friend Const RequiredDataViewGroupCount As Integer = 10
        Friend Const RequiredTreeRootCount As Integer = 1000
        Friend Const RequiredTreeChildrenPerRoot As Integer = 99

        Friend Const DataViewProjectionFailedCode As String = "MASVirtualizationProjectionScale.DataViewProjectionFailed"
        Friend Const TreeProjectionFailedCode As String = "MASVirtualizationProjectionScale.TreeProjectionFailed"
        Friend Const RenderWindowDependencyCode As String = "MASVirtualizationProjectionScale.RenderWindowDependency"
        Friend Const ExceptionCode As String = "MASVirtualizationProjectionScale.Exception"

        Private Sub New()
        End Sub

        Friend Shared Function EvaluateDefault() As MASVirtualizationProjectionScaleReport
            Dim findings As New List(Of MASVirtualizationReadinessFinding)()
            Dim dataViewFacts As DataViewProjectionFacts = DataViewProjectionFacts.Empty
            Dim treeFacts As TreeProjectionFacts = TreeProjectionFacts.Empty
            Dim renderWindowDependencyCount As Integer = 0

            Try
                dataViewFacts = RunDataViewProjectionScaleProof(findings)
                treeFacts = RunTreeExpandProjectionScaleProof(findings)
            Catch ex As Exception
                findings.Add(New MASVirtualizationReadinessFinding(ExceptionCode, "Projection-scale proof threw: " & ex.GetType().Name & " - " & ex.Message, MASVirtualizationFindingSeverity.Error))
            End Try

            Return New MASVirtualizationProjectionScaleReport(
                logicalItemCount:=RequiredLogicalItemCount,
                dataViewFilteredRowCount:=dataViewFacts.FilteredRowCount,
                dataViewSortedFirstId:=dataViewFacts.SortedFirstId,
                dataViewSortedLastId:=dataViewFacts.SortedLastId,
                dataViewProjectedRowCount:=dataViewFacts.ProjectedRowCount,
                dataViewGroupCount:=dataViewFacts.GroupCount,
                treeCollapsedVisibleCount:=treeFacts.CollapsedVisibleCount,
                treeExpandedVisibleCount:=treeFacts.ExpandedVisibleCount,
                treeExpandToggleCount:=treeFacts.ExpandToggleCount,
                renderWindowDependencyCount:=renderWindowDependencyCount,
                findings:=findings)
        End Function

        Private Shared Function RunDataViewProjectionScaleProof(findings As IList(Of MASVirtualizationReadinessFinding)) As DataViewProjectionFacts
            Dim view As New MASDataView()
            Dim idColumn As MASDataGridColumn = view.AddColumn("Id", "Id")
            idColumn.ValueTypeCode = TypeCode.Int32
            view.AddColumn("GroupKey", "Group")
            view.AddColumn("Label", "Label")

            view.ReplaceRows(CreateDataViewValueRows())

            view.SetFilterText("GroupKey=Group03")
            view.SortByColumn("Id", MASDataGridSortDirection.Ascending)

            Dim filteredCount As Integer = view.VisibleRowCount
            Dim firstId As Integer = ResolveRowId(view.GetVisibleRow(0))
            Dim lastId As Integer = ResolveRowId(view.GetVisibleRow(Math.Max(0, filteredCount - 1)))

            If filteredCount <= 0 OrElse filteredCount >= RequiredLogicalItemCount Then
                findings.Add(New MASVirtualizationReadinessFinding(DataViewProjectionFailedCode, "MASDataView 100k filter proof did not produce a bounded filtered projection. FilteredRows=" & filteredCount.ToString(Global.System.Globalization.CultureInfo.InvariantCulture), MASVirtualizationFindingSeverity.Error))
            End If

            If firstId > lastId Then
                findings.Add(New MASVirtualizationReadinessFinding(DataViewProjectionFailedCode, "MASDataView 100k sort proof did not preserve ascending typed source projection order.", MASVirtualizationFindingSeverity.Error))
            End If

            view.ClearAllMenuFilters()
            view.SetColumnMenuGrouping("GroupKey")

            Dim projectedRowCount As Integer = view.VisibleRowCount
            Dim grouping As MASDataViewGroupingSnapshot = view.GroupingSnapshot
            Dim groupCount As Integer = If(grouping IsNot Nothing, grouping.GroupCount, 0)
            Dim groupedTotal As Integer = If(grouping IsNot Nothing, grouping.TotalRowCount, 0)

            If projectedRowCount < RequiredLogicalItemCount OrElse groupedTotal < RequiredLogicalItemCount OrElse groupCount < RequiredDataViewGroupCount Then
                findings.Add(New MASVirtualizationReadinessFinding(DataViewProjectionFailedCode, "MASDataView 100k group proof did not retain all source rows in the grouped projection. ProjectedRows=" & projectedRowCount.ToString(Global.System.Globalization.CultureInfo.InvariantCulture) & "; GroupedTotal=" & groupedTotal.ToString(Global.System.Globalization.CultureInfo.InvariantCulture) & "; Groups=" & groupCount.ToString(Global.System.Globalization.CultureInfo.InvariantCulture), MASVirtualizationFindingSeverity.Error))
            End If

            Return New DataViewProjectionFacts(filteredCount, firstId, lastId, projectedRowCount, groupCount)
        End Function

        Private Shared Function RunTreeExpandProjectionScaleProof(findings As IList(Of MASVirtualizationReadinessFinding)) As TreeProjectionFacts
            Dim roots As List(Of MASTreeNode) = CreateTreeRoots()
            Dim engine As New MASTreeLayoutEngine()

            For Each root As MASTreeNode In roots
                If root IsNot Nothing Then root.IsExpanded = False
            Next

            Dim collapsedVisible As Integer = engine.Flatten(roots).Count
            Dim expandToggleCount As Integer = 0

            For Each root As MASTreeNode In roots
                If root Is Nothing Then Continue For
                root.IsExpanded = True
                expandToggleCount += 1
            Next

            Dim expandedVisible As Integer = engine.Flatten(roots).Count

            If collapsedVisible <> RequiredTreeRootCount Then
                findings.Add(New MASVirtualizationReadinessFinding(TreeProjectionFailedCode, "MASTreeView collapsed projection proof expected root-only visibility. Visible=" & collapsedVisible.ToString(Global.System.Globalization.CultureInfo.InvariantCulture), MASVirtualizationFindingSeverity.Error))
            End If

            If expandedVisible < RequiredLogicalItemCount Then
                findings.Add(New MASVirtualizationReadinessFinding(TreeProjectionFailedCode, "MASTreeView expanded projection proof did not reach the required 100k visible-node projection. Visible=" & expandedVisible.ToString(Global.System.Globalization.CultureInfo.InvariantCulture), MASVirtualizationFindingSeverity.Error))
            End If

            If expandToggleCount < RequiredTreeRootCount Then
                findings.Add(New MASVirtualizationReadinessFinding(TreeProjectionFailedCode, "MASTreeView expanded projection proof did not toggle every root expansion.", MASVirtualizationFindingSeverity.Error))
            End If

            Return New TreeProjectionFacts(collapsedVisible, expandedVisible, expandToggleCount)
        End Function

        Private Shared Function CreateDataViewValueRows() As IEnumerable(Of Object())
            Return New DataViewValueRows()
        End Function

        Private Shared Function CreateTreeRoots() As List(Of MASTreeNode)
            Dim roots As New List(Of MASTreeNode)(RequiredTreeRootCount)
            For rootIndex As Integer = 0 To RequiredTreeRootCount - 1
                Dim root As New MASTreeNode("Root " & rootIndex.ToString(Global.System.Globalization.CultureInfo.InvariantCulture))
                For childIndex As Integer = 0 To RequiredTreeChildrenPerRoot - 1
                    root.AddChild("Child " & rootIndex.ToString(Global.System.Globalization.CultureInfo.InvariantCulture) & ":" & childIndex.ToString(Global.System.Globalization.CultureInfo.InvariantCulture))
                Next
                roots.Add(root)
            Next
            Return roots
        End Function

        Private Shared Function ResolveRowId(row As MASDataGridRow) As Integer
            If row Is Nothing Then Return Integer.MaxValue
            Dim value As Object = row.GetValue("Id")
            If value Is Nothing Then Return Integer.MaxValue
            Return Convert.ToInt32(value, Global.System.Globalization.CultureInfo.InvariantCulture)
        End Function

        Private NotInheritable Class DataViewValueRows
            Implements IEnumerable(Of Object())

            Public Function GetEnumerator() As IEnumerator(Of Object()) Implements IEnumerable(Of Object()).GetEnumerator
                Return CreateRowsIterator().GetEnumerator()
            End Function

            Private Function IEnumerable_GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
                Return GetEnumerator()
            End Function

            Private Iterator Function CreateRowsIterator() As IEnumerable(Of Object())
                For index As Integer = 0 To RequiredLogicalItemCount - 1
                    Yield New Object() {
                        RequiredLogicalItemCount - index,
                        "Group" & (index Mod RequiredDataViewGroupCount).ToString("00", Global.System.Globalization.CultureInfo.InvariantCulture),
                        "Item " & index.ToString(Global.System.Globalization.CultureInfo.InvariantCulture)
                    }
                Next
            End Function
        End Class

        Private Structure DataViewProjectionFacts
            Friend Shared ReadOnly Empty As New DataViewProjectionFacts(0, 0, 0, 0, 0)

            Friend Sub New(filteredRowCount As Integer,
                           sortedFirstId As Integer,
                           sortedLastId As Integer,
                           projectedRowCount As Integer,
                           groupCount As Integer)
                Me.FilteredRowCount = filteredRowCount
                Me.SortedFirstId = sortedFirstId
                Me.SortedLastId = sortedLastId
                Me.ProjectedRowCount = projectedRowCount
                Me.GroupCount = groupCount
            End Sub

            Friend ReadOnly FilteredRowCount As Integer
            Friend ReadOnly SortedFirstId As Integer
            Friend ReadOnly SortedLastId As Integer
            Friend ReadOnly ProjectedRowCount As Integer
            Friend ReadOnly GroupCount As Integer
        End Structure

        Private Structure TreeProjectionFacts
            Friend Shared ReadOnly Empty As New TreeProjectionFacts(0, 0, 0)

            Friend Sub New(collapsedVisibleCount As Integer,
                           expandedVisibleCount As Integer,
                           expandToggleCount As Integer)
                Me.CollapsedVisibleCount = collapsedVisibleCount
                Me.ExpandedVisibleCount = expandedVisibleCount
                Me.ExpandToggleCount = expandToggleCount
            End Sub

            Friend ReadOnly CollapsedVisibleCount As Integer
            Friend ReadOnly ExpandedVisibleCount As Integer
            Friend ReadOnly ExpandToggleCount As Integer
        End Structure

    End Class

End Namespace
