Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.Virtualization

    ''' <summary>
    ''' VIRT-06 runtime gate for MASTileBox. The gate exercises the official tile-grid virtualization coordinator under
    ''' 100k items and records a render-equivalent operation order: resolve virtual scroll extent, clamp/restore offset,
    ''' then call PrepareGridPlan once. The source-contract suite separately locks the real MASTileBox.RenderCore method
    ''' to that same order so the runtime proof cannot drift into a parallel implementation.
    ''' </summary>
    Friend NotInheritable Class MASTileBoxSinglePlanningGate
        Friend Const RequiredLogicalItemCount As Integer = 100000
        Friend Const RequiredPlanningPassCount As Integer = 4
        Friend Const MaxPrepareGridPlanCallsPerPass As Integer = 1
        Friend Const MaxRealizedRowsPerPass As Integer = 64

        Friend Const DuplicatePlanningCode As String = "MASTileBoxVirtualization.DuplicatePlanningPass"
        Friend Const MissingPrePlanScrollExtentCode As String = "MASTileBoxVirtualization.MissingPrePlanScrollExtent"
        Friend Const UnboundedRealizationCode As String = "MASTileBoxVirtualization.UnboundedRealization"
        Friend Const InvalidPlanCode As String = "MASTileBoxVirtualization.InvalidPlan"
        Friend Const ExceptionCode As String = "MASTileBoxVirtualization.Exception"

        Private Const ColumnCount As Integer = 7
        Private Const RowExtentPx As Single = 96.0F
        Private Const ViewportWidthPx As Single = 760.0F
        Private Const ViewportHeightPx As Single = 420.0F

        Private Sub New()
        End Sub

        Friend Shared Function EvaluateDefault() As MASTileBoxSinglePlanningReport
            Dim findings As New List(Of MASVirtualizationReadinessFinding)()

            Try
                Return Evaluate(findings)
            Catch ex As Exception
                findings.Add(New MASVirtualizationReadinessFinding(ExceptionCode, "MASTileBox single-planning proof threw: " & ex.GetType().Name & " - " & ex.Message, MASVirtualizationFindingSeverity.Error))
                Return New MASTileBoxSinglePlanningReport(
                    logicalItemCount:=RequiredLogicalItemCount,
                    passCount:=0,
                    scrollExtentResolvedBeforePlanningPassCount:=0,
                    maxPrepareGridPlanCallsPerPass:=RequiredLogicalItemCount,
                    duplicatePlanningPassCount:=RequiredPlanningPassCount,
                    maxRealizedRowsPerPass:=RequiredLogicalItemCount,
                    minResolvedScrollExtentPx:=0.0F,
                    maxResolvedScrollExtentPx:=0.0F,
                    findings:=findings)
            End Try
        End Function

        Private Shared Function Evaluate(findings As IList(Of MASVirtualizationReadinessFinding)) As MASTileBoxSinglePlanningReport
            Dim coordinator As New MASTileGridVirtualizationConsumerCoordinator()
            Dim viewport As New SKRect(0.0F, 0.0F, ViewportWidthPx, ViewportHeightPx)

            Dim passCount As Integer = 0
            Dim prePlanExtentPassCount As Integer = 0
            Dim maxPrepareCalls As Integer = 0
            Dim duplicatePlanningPassCount As Integer = 0
            Dim maxRealizedRows As Integer = 0
            Dim minResolvedExtent As Single = Single.MaxValue
            Dim maxResolvedExtent As Single = 0.0F

            RunPlanningPass(
                name:="baseline",
                coordinator:=coordinator,
                themeContext:=CreateThemeContext(1, 1.0F),
                viewport:=viewport,
                requestedOffsetPx:=0.0F,
                itemCollectionRevision:=1,
                layoutRevision:=1,
                passCount:=passCount,
                prePlanExtentPassCount:=prePlanExtentPassCount,
                maxPrepareCalls:=maxPrepareCalls,
                duplicatePlanningPassCount:=duplicatePlanningPassCount,
                maxRealizedRows:=maxRealizedRows,
                minResolvedExtent:=minResolvedExtent,
                maxResolvedExtent:=maxResolvedExtent,
                findings:=findings)

            RunPlanningPass(
                name:="theme-invalidation",
                coordinator:=coordinator,
                themeContext:=CreateThemeContext(2, 1.0F),
                viewport:=viewport,
                requestedOffsetPx:=0.0F,
                itemCollectionRevision:=1,
                layoutRevision:=1,
                passCount:=passCount,
                prePlanExtentPassCount:=prePlanExtentPassCount,
                maxPrepareCalls:=maxPrepareCalls,
                duplicatePlanningPassCount:=duplicatePlanningPassCount,
                maxRealizedRows:=maxRealizedRows,
                minResolvedExtent:=minResolvedExtent,
                maxResolvedExtent:=maxResolvedExtent,
                findings:=findings)

            RunPlanningPass(
                name:="layout-resize",
                coordinator:=coordinator,
                themeContext:=CreateThemeContext(2, 1.25F),
                viewport:=New SKRect(0.0F, 0.0F, ViewportWidthPx + 80.0F, ViewportHeightPx),
                requestedOffsetPx:=RowExtentPx * 1250.0F,
                itemCollectionRevision:=1,
                layoutRevision:=2,
                passCount:=passCount,
                prePlanExtentPassCount:=prePlanExtentPassCount,
                maxPrepareCalls:=maxPrepareCalls,
                duplicatePlanningPassCount:=duplicatePlanningPassCount,
                maxRealizedRows:=maxRealizedRows,
                minResolvedExtent:=minResolvedExtent,
                maxResolvedExtent:=maxResolvedExtent,
                findings:=findings)

            RunPlanningPass(
                name:="deep-scroll-item-invalidation",
                coordinator:=coordinator,
                themeContext:=CreateThemeContext(2, 1.25F),
                viewport:=viewport,
                requestedOffsetPx:=RowExtentPx * 12000.0F,
                itemCollectionRevision:=2,
                layoutRevision:=3,
                passCount:=passCount,
                prePlanExtentPassCount:=prePlanExtentPassCount,
                maxPrepareCalls:=maxPrepareCalls,
                duplicatePlanningPassCount:=duplicatePlanningPassCount,
                maxRealizedRows:=maxRealizedRows,
                minResolvedExtent:=minResolvedExtent,
                maxResolvedExtent:=maxResolvedExtent,
                findings:=findings)

            If passCount < RequiredPlanningPassCount Then
                findings.Add(New MASVirtualizationReadinessFinding(InvalidPlanCode, "MASTileBox single-planning proof did not execute enough planning passes. Passes=" & passCount.ToString(Global.System.Globalization.CultureInfo.InvariantCulture), MASVirtualizationFindingSeverity.Error))
            End If

            If prePlanExtentPassCount < RequiredPlanningPassCount Then
                findings.Add(New MASVirtualizationReadinessFinding(MissingPrePlanScrollExtentCode, "MASTileBox single-planning proof did not resolve virtual scroll extent before every plan. Passes=" & prePlanExtentPassCount.ToString(Global.System.Globalization.CultureInfo.InvariantCulture), MASVirtualizationFindingSeverity.Error))
            End If

            If minResolvedExtent = Single.MaxValue Then minResolvedExtent = 0.0F

            Return New MASTileBoxSinglePlanningReport(
                logicalItemCount:=RequiredLogicalItemCount,
                passCount:=passCount,
                scrollExtentResolvedBeforePlanningPassCount:=prePlanExtentPassCount,
                maxPrepareGridPlanCallsPerPass:=maxPrepareCalls,
                duplicatePlanningPassCount:=duplicatePlanningPassCount,
                maxRealizedRowsPerPass:=maxRealizedRows,
                minResolvedScrollExtentPx:=minResolvedExtent,
                maxResolvedScrollExtentPx:=maxResolvedExtent,
                findings:=findings)
        End Function

        Private Shared Sub RunPlanningPass(name As String,
                                           coordinator As MASTileGridVirtualizationConsumerCoordinator,
                                           themeContext As MASThemeContext,
                                           viewport As SKRect,
                                           requestedOffsetPx As Single,
                                           itemCollectionRevision As Integer,
                                           layoutRevision As Integer,
                                           ByRef passCount As Integer,
                                           ByRef prePlanExtentPassCount As Integer,
                                           ByRef maxPrepareCalls As Integer,
                                           ByRef duplicatePlanningPassCount As Integer,
                                           ByRef maxRealizedRows As Integer,
                                           ByRef minResolvedExtent As Single,
                                           ByRef maxResolvedExtent As Single,
                                           findings As IList(Of MASVirtualizationReadinessFinding))
            Dim planCalls As Integer = 0
            Dim rowCount As Integer = MASTileGridVirtualizationConsumerCoordinator.ResolveRowCount(RequiredLogicalItemCount, ColumnCount)
            Dim contentHeightPx As Single = CSng(rowCount) * RowExtentPx
            Dim maxOffsetPx As Single = Math.Max(0.0F, contentHeightPx - Math.Max(0.0F, viewport.Height))
            Dim clampedOffsetPx As Single = Math.Max(0.0F, Math.Min(Math.Max(0.0F, requestedOffsetPx), maxOffsetPx))

            If contentHeightPx > 0.0F AndAlso contentHeightPx >= Math.Max(0.0F, viewport.Height) Then
                prePlanExtentPassCount += 1
                minResolvedExtent = Math.Min(minResolvedExtent, contentHeightPx)
                maxResolvedExtent = Math.Max(maxResolvedExtent, contentHeightPx)
            End If

            planCalls += 1
            Dim plan As MASVirtualizationRealizationPlan = coordinator.PrepareGridPlan(
                ctx:=themeContext,
                itemCount:=RequiredLogicalItemCount,
                columnCount:=ColumnCount,
                rowExtentPx:=RowExtentPx,
                viewportRectPx:=viewport,
                viewportOffsetPx:=clampedOffsetPx,
                itemCollectionRevision:=itemCollectionRevision,
                layoutRevision:=layoutRevision)

            passCount += 1
            maxPrepareCalls = Math.Max(maxPrepareCalls, planCalls)

            If planCalls > MaxPrepareGridPlanCallsPerPass Then
                duplicatePlanningPassCount += 1
                findings.Add(New MASVirtualizationReadinessFinding(DuplicatePlanningCode, "MASTileBox single-planning proof rejected duplicate PrepareGridPlan calls for " & name & ". Calls=" & planCalls.ToString(Global.System.Globalization.CultureInfo.InvariantCulture), MASVirtualizationFindingSeverity.Error))
            End If

            If plan Is Nothing OrElse plan.IsEmpty OrElse plan.RealizedCount <= 0 OrElse plan.TotalExtentPx <= 0.0F Then
                findings.Add(New MASVirtualizationReadinessFinding(InvalidPlanCode, "MASTileBox single-planning proof produced an invalid tile-grid plan for " & name & ".", MASVirtualizationFindingSeverity.Error))
                Return
            End If

            maxRealizedRows = Math.Max(maxRealizedRows, plan.RealizedCount)

            If plan.RealizedCount > MaxRealizedRowsPerPass OrElse plan.RealizedCount >= rowCount Then
                findings.Add(New MASVirtualizationReadinessFinding(UnboundedRealizationCode, "MASTileBox single-planning proof rejected unbounded tile-grid realization for " & name & ". Rows=" & plan.RealizedCount.ToString(Global.System.Globalization.CultureInfo.InvariantCulture), MASVirtualizationFindingSeverity.Error))
            End If
        End Sub

        Private Shared Function CreateThemeContext(themeStamp As Integer, dpi As Single) As MASThemeContext
            Return New MASThemeContext(dpi, New MASTypography(1.0F), ThemeManager.CurrentCore, themeStamp)
        End Function
    End Class

End Namespace
