Option Strict On
Option Explicit On

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

Namespace Nexamas.UI.Virtualization

    ''' <summary>
    ''' Behavioral gate for the current virtualization consumer catalog. This is intentionally stricter than the metadata-only
    ''' integration gate: every active shared-controller binding must execute the official coordinator path against a 100k-item
    ''' scenario and prove bounded realization, sparse measurement, scroll-to-index correctness, point/hit mapping, lease recycling,
    ''' and environment invalidation. Composed consumers are proven only through their official child consumer proofs.
    ''' </summary>
    Friend NotInheritable Class MASVirtualizationConsumerBehaviorGate

        Friend Const MissingBehaviorProofCode As String = "MASVirtualizationConsumerBehavior.MissingProof"
        Friend Const BoundedRealizationCode As String = "MASVirtualizationConsumerBehavior.UnboundedRealization"
        Friend Const SparseMeasurementCode As String = "MASVirtualizationConsumerBehavior.UnboundedMeasurement"
        Friend Const ScrollToIndexCode As String = "MASVirtualizationConsumerBehavior.ScrollToIndex"
        Friend Const HitMappingCode As String = "MASVirtualizationConsumerBehavior.HitMapping"
        Friend Const InvalidationCode As String = "MASVirtualizationConsumerBehavior.Invalidation"
        Friend Const RecyclingCode As String = "MASVirtualizationConsumerBehavior.Recycling"
        Friend Const ComposedConsumerCode As String = "MASVirtualizationConsumerBehavior.ComposedConsumer"
        Friend Const ExceptionCode As String = "MASVirtualizationConsumerBehavior.Exception"

        Friend Const RequiredLogicalItemCount As Integer = 100000
        Friend Const RequiredActiveSharedControllerProofs As Integer = 6
        Friend Const RequiredComposedSharedControllerProofs As Integer = 1
        Friend Const RequiredTotalConsumerProofs As Integer = 7

        Private Const LinearEstimatedExtentPx As Single = 24.0F
        Private Const LinearViewportExtentPx As Single = 240.0F
        Private Const LinearOverscanItemCount As Integer = 2

        Private Sub New()
        End Sub

        Friend Shared Function EvaluateCurrentConsumers() As MASVirtualizationConsumerBehaviorReport
            Return Evaluate(MASVirtualizationConsumerCatalog.GetCurrentConsumers())
        End Function

        Friend Shared Function Evaluate(bindings As IEnumerable(Of MASVirtualizationConsumerBinding)) As MASVirtualizationConsumerBehaviorReport
            Dim safeBindings As MASVirtualizationConsumerBinding() = If(bindings, Enumerable.Empty(Of MASVirtualizationConsumerBinding)()).Where(Function(binding) binding IsNot Nothing).ToArray()
            Dim findings As New List(Of MASVirtualizationReadinessFinding)()
            Dim provedConsumerIds As New List(Of String)()
            Dim provedConsumerBehaviorKeys As New List(Of String)()

            For Each binding In safeBindings
                If Not binding.IsActiveOrComposedSharedControllerConsumer Then Continue For

                If binding.Stage = MASVirtualizationIntegrationStage.ComposedSharedControllerConsumer Then
                    If TryProveComposedConsumer(binding, provedConsumerIds, findings, provedConsumerBehaviorKeys) Then
                        AddProof(provedConsumerIds, binding.Id)
                    End If
                    Continue For
                End If

                Try
                    Dim passed As Boolean
                    If binding.Kind = MASVirtualizationConsumerKind.TileSurface OrElse binding.ExtentStrategy = MASVirtualizationExtentStrategy.TileGridMeasuredCells Then
                        passed = RunTileGridConsumerBehaviorProof(binding, findings, provedConsumerBehaviorKeys)
                    Else
                        passed = RunLinearConsumerBehaviorProof(binding, findings, provedConsumerBehaviorKeys)
                    End If

                    If passed Then
                        AddProof(provedConsumerIds, binding.Id)
                    End If
                Catch ex As Exception
                    findings.Add(New MASVirtualizationReadinessFinding(ExceptionCode, "Virtualization consumer behavior proof threw for " & binding.Id & ": " & ex.GetType().Name & " - " & ex.Message, MASVirtualizationFindingSeverity.Error))
                End Try
            Next

            Dim activeProofs As Integer = Global.System.Linq.Enumerable.Count(provedConsumerIds, Function(id) safeBindings.Any(Function(binding) binding.Id = id AndAlso binding.Stage = MASVirtualizationIntegrationStage.ActiveSharedControllerConsumer))
            Dim composedProofs As Integer = Global.System.Linq.Enumerable.Count(provedConsumerIds, Function(id) safeBindings.Any(Function(binding) binding.Id = id AndAlso binding.Stage = MASVirtualizationIntegrationStage.ComposedSharedControllerConsumer))

            If activeProofs < RequiredActiveSharedControllerProofs Then
                findings.Add(New MASVirtualizationReadinessFinding(MissingBehaviorProofCode, "Virtualization behavior proof must execute all active shared-controller consumers. ActiveProofs=" & activeProofs.ToString(Global.System.Globalization.CultureInfo.InvariantCulture), MASVirtualizationFindingSeverity.Error))
            End If

            If composedProofs < RequiredComposedSharedControllerProofs Then
                findings.Add(New MASVirtualizationReadinessFinding(MissingBehaviorProofCode, "Virtualization behavior proof must include composed consumers through their official child proofs. ComposedProofs=" & composedProofs.ToString(Global.System.Globalization.CultureInfo.InvariantCulture), MASVirtualizationFindingSeverity.Error))
            End If

            If provedConsumerIds.Count < RequiredTotalConsumerProofs Then
                findings.Add(New MASVirtualizationReadinessFinding(MissingBehaviorProofCode, "Virtualization behavior proof must cover the full current catalog. Proofs=" & provedConsumerIds.Count.ToString(Global.System.Globalization.CultureInfo.InvariantCulture), MASVirtualizationFindingSeverity.Error))
            End If

            ValidateRequiredBehaviorDimensions(safeBindings, provedConsumerBehaviorKeys, findings)

            Return New MASVirtualizationConsumerBehaviorReport(safeBindings, findings, provedConsumerIds, provedConsumerBehaviorKeys)
        End Function

        Private Shared Function RunLinearConsumerBehaviorProof(binding As MASVirtualizationConsumerBinding,
                                                               findings As IList(Of MASVirtualizationReadinessFinding),
                                                               provedConsumerBehaviorKeys As IList(Of String)) As Boolean
            Dim passed As Boolean = True
            Dim coordinator As New MASLinearVirtualizationConsumerCoordinator()
            coordinator.Configure(
                itemCount:=RequiredLogicalItemCount,
                estimatedItemExtentPx:=LinearEstimatedExtentPx,
                overscanItemCount:=LinearOverscanItemCount,
                axis:=binding.RecommendedAxis,
                extentStrategy:=binding.ExtentStrategy)
            AddBehaviorProof(provedConsumerBehaviorKeys, binding.Id, MASVirtualizationConsumerBehaviorReport.LogicalItemPressureDimension)

            Dim firstStamp As MASVirtualizationEnvironmentStamp = CreateStamp(1, 1, LinearViewportExtentPx)
            Dim firstDecision As MASVirtualizationInvalidationDecision = coordinator.EvaluateEnvironment(firstStamp)
            If firstDecision Is Nothing OrElse firstDecision.Invalidated Then
                findings.Add(New MASVirtualizationReadinessFinding(InvalidationCode, binding.Id & " behavior proof rejected baseline environment invalidation.", MASVirtualizationFindingSeverity.Error))
                passed = False
            End If

            Dim firstPlan As MASVirtualizationRealizationPlan = coordinator.UpdateViewport(0.0F, LinearViewportExtentPx)
            If AssertBoundedPlan(binding.Id, "initial", firstPlan, ResolveLinearRealizationBudget(), findings) Then
                AddBehaviorProof(provedConsumerBehaviorKeys, binding.Id, MASVirtualizationConsumerBehaviorReport.RenderPlanDimension)
            Else
                passed = False
            End If

            Dim measuredWrites As Integer = RecordMeasuredOverridesForPlan(coordinator, firstPlan)
            Dim measuredPlan As MASVirtualizationRealizationPlan = coordinator.UpdateViewport(0.0F, LinearViewportExtentPx)
            passed = AssertBoundedPlan(binding.Id, "measured", measuredPlan, ResolveLinearRealizationBudget(), findings) AndAlso passed

            Dim diagnostics As MASVirtualizationDiagnosticsSnapshot = coordinator.DiagnosticsSnapshot
            If diagnostics Is Nothing OrElse diagnostics.MeasurementCount > Math.Max(1, measuredWrites) OrElse diagnostics.MeasurementCount >= RequiredLogicalItemCount Then
                findings.Add(New MASVirtualizationReadinessFinding(SparseMeasurementCode, binding.Id & " behavior proof rejected non-sparse measurement count.", MASVirtualizationFindingSeverity.Error))
                passed = False
            Else
                AddBehaviorProof(provedConsumerBehaviorKeys, binding.Id, MASVirtualizationConsumerBehaviorReport.SparseMeasurementDimension)
            End If

            Dim scrollIndex As Integer = 75000
            Dim scrollRequest As MASVirtualizationScrollRequest = coordinator.CreateScrollToIndexRequest(scrollIndex, 0.0F, LinearViewportExtentPx, MASVirtualizationAlignment.Start)
            If scrollRequest Is Nothing OrElse scrollRequest.Index <> scrollIndex Then
                findings.Add(New MASVirtualizationReadinessFinding(ScrollToIndexCode, binding.Id & " behavior proof rejected scroll-to-index request identity.", MASVirtualizationFindingSeverity.Error))
                passed = False
            End If

            Dim scrolledPlan As MASVirtualizationRealizationPlan = coordinator.UpdateViewport(If(scrollRequest IsNot Nothing, scrollRequest.TargetOffsetPx, 0.0F), LinearViewportExtentPx)
            passed = AssertBoundedPlan(binding.Id, "scroll-to-index", scrolledPlan, ResolveLinearRealizationBudget(), findings) AndAlso passed
            If scrolledPlan Is Nothing OrElse scrolledPlan.Range Is Nothing OrElse Not scrolledPlan.Range.Contains(scrollIndex) Then
                findings.Add(New MASVirtualizationReadinessFinding(ScrollToIndexCode, binding.Id & " behavior proof rejected range that does not contain the scroll target.", MASVirtualizationFindingSeverity.Error))
                passed = False
            Else
                AddBehaviorProof(provedConsumerBehaviorKeys, binding.Id, MASVirtualizationConsumerBehaviorReport.ScrollToIndexDimension)
            End If

            Dim hitIndex As Integer = coordinator.ResolveIndexAtPointY(8.0F, 0.0F, LinearViewportExtentPx, If(scrollRequest IsNot Nothing, scrollRequest.TargetOffsetPx, 0.0F))
            If hitIndex < 0 OrElse scrolledPlan Is Nothing OrElse scrolledPlan.Range Is Nothing OrElse Not scrolledPlan.Range.Contains(hitIndex) Then
                findings.Add(New MASVirtualizationReadinessFinding(HitMappingCode, binding.Id & " behavior proof rejected point-to-index mapping inside the realized range.", MASVirtualizationFindingSeverity.Error))
                passed = False
            Else
                AddBehaviorProof(provedConsumerBehaviorKeys, binding.Id, MASVirtualizationConsumerBehaviorReport.HitTestDimension)
            End If

            Dim createdBeforeRelease As Integer = If(coordinator.DiagnosticsSnapshot IsNot Nothing, coordinator.DiagnosticsSnapshot.CreatedLeaseCount, 0)
            Dim emptyPlan As MASVirtualizationRealizationPlan = coordinator.UpdateViewport(0.0F, 0.0F)
            If emptyPlan Is Nothing OrElse Not emptyPlan.IsEmpty Then
                findings.Add(New MASVirtualizationReadinessFinding(RecyclingCode, binding.Id & " behavior proof rejected empty viewport release plan.", MASVirtualizationFindingSeverity.Error))
                passed = False
            End If

            Dim recycledPlan As MASVirtualizationRealizationPlan = coordinator.UpdateViewport(0.0F, LinearViewportExtentPx)
            Dim recycledDiagnostics As MASVirtualizationDiagnosticsSnapshot = coordinator.DiagnosticsSnapshot
            If createdBeforeRelease > 0 AndAlso (recycledDiagnostics Is Nothing OrElse recycledDiagnostics.ReusedLeaseCount <= 0 OrElse recycledPlan Is Nothing OrElse recycledPlan.RealizedCount <= 0) Then
                findings.Add(New MASVirtualizationReadinessFinding(RecyclingCode, binding.Id & " behavior proof rejected lease reuse after release/re-realize.", MASVirtualizationFindingSeverity.Error))
                passed = False
            Else
                AddBehaviorProof(provedConsumerBehaviorKeys, binding.Id, MASVirtualizationConsumerBehaviorReport.DetachReleaseDimension)
            End If

            If Not ProveLinearEnvironmentInvalidation(
                binding.Id,
                coordinator,
                findings,
                provedConsumerBehaviorKeys,
                CreateStamp(2, 1, LinearViewportExtentPx, dpiRevision:=1, localizationRevision:=1, rtlRevision:=1),
                MASVirtualizationInvalidationReason.ThemeChanged,
                MASVirtualizationConsumerBehaviorReport.ThemeInvalidationDimension) Then
                passed = False
            End If

            If Not ProveLinearEnvironmentInvalidation(
                binding.Id,
                coordinator,
                findings,
                provedConsumerBehaviorKeys,
                CreateStamp(2, 1, LinearViewportExtentPx, dpiRevision:=2, localizationRevision:=1, rtlRevision:=1),
                MASVirtualizationInvalidationReason.DpiChanged,
                MASVirtualizationConsumerBehaviorReport.DpiInvalidationDimension) Then
                passed = False
            End If

            If Not ProveLinearEnvironmentInvalidation(
                binding.Id,
                coordinator,
                findings,
                provedConsumerBehaviorKeys,
                CreateStamp(2, 1, LinearViewportExtentPx, dpiRevision:=2, localizationRevision:=1, rtlRevision:=2),
                MASVirtualizationInvalidationReason.RtlChanged,
                MASVirtualizationConsumerBehaviorReport.RtlInvalidationDimension) Then
                passed = False
            End If

            If Not ProveLinearEnvironmentInvalidation(
                binding.Id,
                coordinator,
                findings,
                provedConsumerBehaviorKeys,
                CreateStamp(2, 1, LinearViewportExtentPx + 120.0F, dpiRevision:=2, localizationRevision:=1, rtlRevision:=2),
                MASVirtualizationInvalidationReason.ControlSizeChanged,
                MASVirtualizationConsumerBehaviorReport.ResizeInvalidationDimension) Then
                passed = False
            End If

            Return passed
        End Function

        Private Shared Function RunTileGridConsumerBehaviorProof(binding As MASVirtualizationConsumerBinding,
                                                                 findings As IList(Of MASVirtualizationReadinessFinding),
                                                                 provedConsumerBehaviorKeys As IList(Of String)) As Boolean
            Dim passed As Boolean = True
            Dim coordinator As New MASTileGridVirtualizationConsumerCoordinator()
            Dim ctx As MASThemeContext = CreateThemeContext(1)
            Dim viewport As New SKRect(0.0F, 0.0F, 500.0F, 480.0F)
            Dim itemCount As Integer = RequiredLogicalItemCount
            Dim columns As Integer = 5
            Dim rowExtent As Single = 96.0F
            Dim rowBudget As Integer = CInt(Math.Ceiling(CDbl(viewport.Height / rowExtent))) + (2 * 2) + 2
            AddBehaviorProof(provedConsumerBehaviorKeys, binding.Id, MASVirtualizationConsumerBehaviorReport.LogicalItemPressureDimension)

            Dim firstPlan As MASVirtualizationRealizationPlan = coordinator.PrepareGridPlan(ctx, itemCount, columns, rowExtent, viewport, 0.0F, 1, 1)
            If AssertBoundedPlan(binding.Id, "tile-initial", firstPlan, rowBudget, findings) Then
                AddBehaviorProof(provedConsumerBehaviorKeys, binding.Id, MASVirtualizationConsumerBehaviorReport.RenderPlanDimension)
                AddBehaviorProof(provedConsumerBehaviorKeys, binding.Id, MASVirtualizationConsumerBehaviorReport.SparseMeasurementDimension)
            Else
                passed = False
            End If

            Dim hitIndex As Integer = coordinator.ResolveGridHitIndex(New SKPoint(40.0F, 40.0F), viewport, 0.0F, 0.0F, 100.0F, rowExtent, 0.0F, columns, itemCount)
            If hitIndex < 0 OrElse hitIndex >= columns Then
                findings.Add(New MASVirtualizationReadinessFinding(HitMappingCode, binding.Id & " behavior proof rejected tile-grid hit mapping for the first visible row.", MASVirtualizationFindingSeverity.Error))
                passed = False
            Else
                AddBehaviorProof(provedConsumerBehaviorKeys, binding.Id, MASVirtualizationConsumerBehaviorReport.HitTestDimension)
            End If

            Dim targetItemIndex As Integer = itemCount - 10
            Dim targetRowIndex As Integer = targetItemIndex \ columns
            Dim scrollRequest As MASVirtualizationScrollRequest = coordinator.CreateScrollToItemRequest(targetItemIndex, columns, 0.0F, viewport.Height, MASVirtualizationAlignment.Start)
            If scrollRequest Is Nothing OrElse scrollRequest.Index <> targetRowIndex Then
                findings.Add(New MASVirtualizationReadinessFinding(ScrollToIndexCode, binding.Id & " behavior proof rejected tile-grid scroll-to-item row identity.", MASVirtualizationFindingSeverity.Error))
                passed = False
            End If

            Dim scrolledPlan As MASVirtualizationRealizationPlan = coordinator.PrepareGridPlan(ctx, itemCount, columns, rowExtent, viewport, If(scrollRequest IsNot Nothing, scrollRequest.TargetOffsetPx, 0.0F), 1, 1)
            passed = AssertBoundedPlan(binding.Id, "tile-scroll", scrolledPlan, rowBudget, findings) AndAlso passed
            If scrolledPlan Is Nothing OrElse scrolledPlan.Range Is Nothing OrElse Not scrolledPlan.Range.Contains(targetRowIndex) Then
                findings.Add(New MASVirtualizationReadinessFinding(ScrollToIndexCode, binding.Id & " behavior proof rejected tile-grid realized row range for scroll target.", MASVirtualizationFindingSeverity.Error))
                passed = False
            Else
                AddBehaviorProof(provedConsumerBehaviorKeys, binding.Id, MASVirtualizationConsumerBehaviorReport.ScrollToIndexDimension)
            End If

            Dim emptyPlan As MASVirtualizationRealizationPlan = coordinator.PrepareGridPlan(ctx, itemCount, columns, rowExtent, New SKRect(0.0F, 0.0F, 500.0F, 0.0F), 0.0F, 1, 1)
            If emptyPlan Is Nothing OrElse Not emptyPlan.IsEmpty Then
                findings.Add(New MASVirtualizationReadinessFinding(RecyclingCode, binding.Id & " behavior proof rejected tile-grid empty viewport release plan.", MASVirtualizationFindingSeverity.Error))
                passed = False
            Else
                AddBehaviorProof(provedConsumerBehaviorKeys, binding.Id, MASVirtualizationConsumerBehaviorReport.DetachReleaseDimension)
            End If

            If Not ProveTileEnvironmentInvalidation(
                binding.Id,
                coordinator,
                findings,
                provedConsumerBehaviorKeys,
                CreateThemeContext(2),
                itemCount,
                columns,
                rowExtent,
                viewport,
                0.0F,
                1,
                1,
                MASVirtualizationInvalidationReason.ThemeChanged,
                MASVirtualizationConsumerBehaviorReport.ThemeInvalidationDimension,
                rowBudget,
                "tile-theme-change") Then
                passed = False
            End If

            If Not ProveTileEnvironmentInvalidation(
                binding.Id,
                coordinator,
                findings,
                provedConsumerBehaviorKeys,
                CreateThemeContext(2, 1.25F),
                itemCount,
                columns,
                rowExtent,
                viewport,
                0.0F,
                1,
                1,
                MASVirtualizationInvalidationReason.DpiChanged,
                MASVirtualizationConsumerBehaviorReport.DpiInvalidationDimension,
                rowBudget,
                "tile-dpi-change") Then
                passed = False
            End If

            If Not ProveTileEnvironmentInvalidation(
                binding.Id,
                coordinator,
                findings,
                provedConsumerBehaviorKeys,
                CreateThemeContext(2, 1.25F),
                itemCount,
                columns,
                rowExtent,
                New SKRect(0.0F, 0.0F, 500.0F, 600.0F),
                0.0F,
                1,
                1,
                MASVirtualizationInvalidationReason.ControlSizeChanged,
                MASVirtualizationConsumerBehaviorReport.ResizeInvalidationDimension,
                rowBudget + 2,
                "tile-resize-change") Then
                passed = False
            End If

            If Not ProveTileRtlInvalidation(
                binding.Id,
                coordinator,
                findings,
                provedConsumerBehaviorKeys,
                itemCount,
                columns,
                rowExtent,
                viewport,
                rowBudget) Then
                passed = False
            End If


            Return passed
        End Function

        Private Shared Function TryProveComposedConsumer(binding As MASVirtualizationConsumerBinding,
                                                         provedConsumerIds As IList(Of String),
                                                         findings As IList(Of MASVirtualizationReadinessFinding),
                                                         provedConsumerBehaviorKeys As IList(Of String)) As Boolean
            If Not binding.IsComposedSharedControllerConsumer Then
                findings.Add(New MASVirtualizationReadinessFinding(ComposedConsumerCode, binding.Id & " behavior proof rejected composed binding without official child consumers.", MASVirtualizationFindingSeverity.Error))
                Return False
            End If

            For Each dependencyId As String In binding.ComposedThroughConsumerIds
                If Not provedConsumerIds.Contains(dependencyId) Then
                    findings.Add(New MASVirtualizationReadinessFinding(ComposedConsumerCode, binding.Id & " behavior proof requires completed child proof: " & dependencyId, MASVirtualizationFindingSeverity.Error))
                    Return False
                End If
            Next

            AddBehaviorProof(provedConsumerBehaviorKeys, binding.Id, MASVirtualizationConsumerBehaviorReport.ComposedThroughChildrenDimension)
            Return True
        End Function

        Private Shared Function RecordMeasuredOverridesForPlan(coordinator As MASLinearVirtualizationConsumerCoordinator,
                                                               plan As MASVirtualizationRealizationPlan) As Integer
            If coordinator Is Nothing OrElse plan Is Nothing OrElse plan.Requests Is Nothing Then Return 0

            Dim writes As Integer = 0
            For Each request As MASVirtualizationItemRequest In plan.Requests
                If request Is Nothing Then Continue For
                coordinator.RecordMeasuredExtent(request.Index, LinearEstimatedExtentPx + CSng((request.Index Mod 3) * 4))
                writes += 1
            Next

            Return writes
        End Function

        Private Shared Function AssertBoundedPlan(consumerId As String,
                                                  scenario As String,
                                                  plan As MASVirtualizationRealizationPlan,
                                                  maxRealizedCount As Integer,
                                                  findings As IList(Of MASVirtualizationReadinessFinding)) As Boolean
            If plan Is Nothing Then
                findings.Add(New MASVirtualizationReadinessFinding(BoundedRealizationCode, consumerId & " behavior proof produced no plan for " & scenario & ".", MASVirtualizationFindingSeverity.Error))
                Return False
            End If

            If plan.RealizedCount < 0 OrElse plan.RealizedCount > maxRealizedCount Then
                findings.Add(New MASVirtualizationReadinessFinding(BoundedRealizationCode, consumerId & " behavior proof rejected unbounded realized count for " & scenario & ". Realized=" & plan.RealizedCount.ToString(Global.System.Globalization.CultureInfo.InvariantCulture) & ", Budget=" & maxRealizedCount.ToString(Global.System.Globalization.CultureInfo.InvariantCulture), MASVirtualizationFindingSeverity.Error))
                Return False
            End If

            If plan.TotalExtentPx <= 0.0F Then
                findings.Add(New MASVirtualizationReadinessFinding(BoundedRealizationCode, consumerId & " behavior proof rejected invalid total extent for " & scenario & ".", MASVirtualizationFindingSeverity.Error))
                Return False
            End If

            Return True
        End Function

        Private Shared Function ResolveLinearRealizationBudget() As Integer
            Return CInt(Math.Ceiling(CDbl(LinearViewportExtentPx / LinearEstimatedExtentPx))) + (LinearOverscanItemCount * 2) + 4
        End Function

        Private Shared Function ProveLinearEnvironmentInvalidation(consumerId As String,
                                                                   coordinator As MASLinearVirtualizationConsumerCoordinator,
                                                                   findings As IList(Of MASVirtualizationReadinessFinding),
                                                                   provedConsumerBehaviorKeys As IList(Of String),
                                                                   stamp As MASVirtualizationEnvironmentStamp,
                                                                   expectedReason As MASVirtualizationInvalidationReason,
                                                                   dimensionId As String) As Boolean
            Dim invalidationDecision As MASVirtualizationInvalidationDecision = coordinator.EvaluateEnvironment(stamp)
            If invalidationDecision Is Nothing OrElse Not invalidationDecision.Invalidated OrElse invalidationDecision.Reason <> expectedReason Then
                findings.Add(New MASVirtualizationReadinessFinding(InvalidationCode, consumerId & " behavior proof rejected " & expectedReason.ToString() & " environment invalidation path.", MASVirtualizationFindingSeverity.Error))
                Return False
            End If

            Dim invalidatedDiagnostics As MASVirtualizationDiagnosticsSnapshot = coordinator.DiagnosticsSnapshot
            If invalidatedDiagnostics Is Nothing OrElse invalidatedDiagnostics.LastInvalidationReason <> expectedReason Then
                findings.Add(New MASVirtualizationReadinessFinding(InvalidationCode, consumerId & " behavior proof rejected diagnostics after " & expectedReason.ToString() & " environment invalidation.", MASVirtualizationFindingSeverity.Error))
                Return False
            End If

            AddBehaviorProof(provedConsumerBehaviorKeys, consumerId, dimensionId)
            Return True
        End Function

        Private Shared Function ProveTileEnvironmentInvalidation(consumerId As String,
                                                                 coordinator As MASTileGridVirtualizationConsumerCoordinator,
                                                                 findings As IList(Of MASVirtualizationReadinessFinding),
                                                                 provedConsumerBehaviorKeys As IList(Of String),
                                                                 ctx As MASThemeContext,
                                                                 itemCount As Integer,
                                                                 columns As Integer,
                                                                 rowExtent As Single,
                                                                 viewport As SKRect,
                                                                 viewportOffset As Single,
                                                                 itemRevision As Integer,
                                                                 layoutRevision As Integer,
                                                                 expectedReason As MASVirtualizationInvalidationReason,
                                                                 dimensionId As String,
                                                                 rowBudget As Integer,
                                                                 scenario As String) As Boolean
            Dim plan As MASVirtualizationRealizationPlan = coordinator.PrepareGridPlan(ctx, itemCount, columns, rowExtent, viewport, viewportOffset, itemRevision, layoutRevision)
            Dim passed As Boolean = AssertBoundedPlan(consumerId, scenario, plan, rowBudget, findings)
            Dim diagnostics As MASVirtualizationDiagnosticsSnapshot = coordinator.DiagnosticsSnapshot
            If diagnostics Is Nothing OrElse diagnostics.LastInvalidationReason <> expectedReason Then
                findings.Add(New MASVirtualizationReadinessFinding(InvalidationCode, consumerId & " behavior proof rejected tile-grid diagnostics after " & expectedReason.ToString() & " environment invalidation.", MASVirtualizationFindingSeverity.Error))
                Return False
            End If

            If passed Then AddBehaviorProof(provedConsumerBehaviorKeys, consumerId, dimensionId)
            Return passed
        End Function

        Private Shared Function ProveTileRtlInvalidation(consumerId As String,
                                                         coordinator As MASTileGridVirtualizationConsumerCoordinator,
                                                         findings As IList(Of MASVirtualizationReadinessFinding),
                                                         provedConsumerBehaviorKeys As IList(Of String),
                                                         itemCount As Integer,
                                                         columns As Integer,
                                                         rowExtent As Single,
                                                         viewport As SKRect,
                                                         rowBudget As Integer) As Boolean
            Dim previousSnapshot As MASLocalizationRuntimeEnvironmentSnapshot = Nothing
            Try
                previousSnapshot = MASLocalization.GetRuntimeEnvironmentSnapshot()
                Dim rtlSnapshot As MASLocalizationRuntimeEnvironmentSnapshot = MASLocalization.SetRuntimeCulture("ar-SY", "VirtualizationConsumerBehaviorGate.RtlProof")
                If rtlSnapshot Is Nothing OrElse Not rtlSnapshot.IsRightToLeft Then
                    findings.Add(New MASVirtualizationReadinessFinding(InvalidationCode, consumerId & " behavior proof rejected RTL runtime culture snapshot for tile-grid proof.", MASVirtualizationFindingSeverity.Error))
                    Return False
                End If

                Return ProveTileEnvironmentInvalidation(
                    consumerId,
                    coordinator,
                    findings,
                    provedConsumerBehaviorKeys,
                    CreateThemeContext(2, 1.25F),
                    itemCount,
                    columns,
                    rowExtent,
                    viewport,
                    0.0F,
                    1,
                    1,
                    MASVirtualizationInvalidationReason.LocalizationChanged,
                    MASVirtualizationConsumerBehaviorReport.RtlInvalidationDimension,
                    rowBudget,
                    "tile-rtl-change")
            Finally
                If previousSnapshot IsNot Nothing AndAlso String.Equals(previousSnapshot.Source, "ThreadCulture", Global.System.StringComparison.Ordinal) Then
                    MASLocalization.UseCurrentThreadCulture("VirtualizationConsumerBehaviorGate.RestoreThreadCulture")
                ElseIf previousSnapshot IsNot Nothing AndAlso previousSnapshot.CultureName.Length > 0 Then
                    MASLocalization.SetRuntimeCulture(previousSnapshot.CultureName, "VirtualizationConsumerBehaviorGate.RestoreCulture")
                Else
                    MASLocalization.UseCurrentThreadCulture("VirtualizationConsumerBehaviorGate.RestoreThreadCulture")
                End If
            End Try
        End Function

        Private Shared Sub ValidateRequiredBehaviorDimensions(bindings As IEnumerable(Of MASVirtualizationConsumerBinding),
                                                              provedConsumerBehaviorKeys As IList(Of String),
                                                              findings As IList(Of MASVirtualizationReadinessFinding))
            For Each binding As MASVirtualizationConsumerBinding In If(bindings, Enumerable.Empty(Of MASVirtualizationConsumerBinding)())
                If binding Is Nothing Then Continue For

                If binding.Stage = MASVirtualizationIntegrationStage.ActiveSharedControllerConsumer Then
                    For Each dimension As String In MASVirtualizationConsumerBehaviorReport.RequiredActiveBehaviorDimensionIds
                        If Not HasBehaviorProof(provedConsumerBehaviorKeys, binding.Id, dimension) Then
                            findings.Add(New MASVirtualizationReadinessFinding(MissingBehaviorProofCode, binding.Id & " behavior proof is missing required dimension: " & dimension, MASVirtualizationFindingSeverity.Error))
                        End If
                    Next
                ElseIf binding.Stage = MASVirtualizationIntegrationStage.ComposedSharedControllerConsumer AndAlso
                       Not HasBehaviorProof(provedConsumerBehaviorKeys, binding.Id, MASVirtualizationConsumerBehaviorReport.ComposedThroughChildrenDimension) Then
                    findings.Add(New MASVirtualizationReadinessFinding(MissingBehaviorProofCode, binding.Id & " behavior proof is missing composed child proof dimension.", MASVirtualizationFindingSeverity.Error))
                End If
            Next
        End Sub

        Private Shared Function HasBehaviorProof(provedConsumerBehaviorKeys As IList(Of String),
                                                 consumerId As String,
                                                 dimensionId As String) As Boolean
            If provedConsumerBehaviorKeys Is Nothing Then Return False
            Return provedConsumerBehaviorKeys.Contains(MASVirtualizationConsumerBehaviorReport.CreateBehaviorKey(consumerId, dimensionId))
        End Function

        Private Shared Sub AddBehaviorProof(provedConsumerBehaviorKeys As IList(Of String),
                                            consumerId As String,
                                            dimensionId As String)
            If provedConsumerBehaviorKeys Is Nothing Then Return
            Dim key As String = MASVirtualizationConsumerBehaviorReport.CreateBehaviorKey(consumerId, dimensionId)
            If key.Length <= 1 OrElse provedConsumerBehaviorKeys.Contains(key) Then Return
            provedConsumerBehaviorKeys.Add(key)
        End Sub

        Private Shared Sub AddProof(provedConsumerIds As IList(Of String), id As String)
            Dim safeId As String = If(id, String.Empty).Trim()
            If safeId.Length = 0 OrElse provedConsumerIds.Contains(safeId) Then Return
            provedConsumerIds.Add(safeId)
        End Sub

        Private Shared Function CreateStamp(themeRevision As Integer,
                                            itemRevision As Integer,
                                            viewportExtentPx As Single,
                                            Optional dpiRevision As Integer = 1,
                                            Optional localizationRevision As Integer = 1,
                                            Optional rtlRevision As Integer = 1) As MASVirtualizationEnvironmentStamp
            Return New MASVirtualizationEnvironmentStamp(
                themeRevision:=themeRevision,
                dpiRevision:=dpiRevision,
                densityRevision:=1,
                fontRevision:=1,
                localizationRevision:=localizationRevision,
                rtlRevision:=rtlRevision,
                itemCollectionRevision:=itemRevision,
                controlSizeRevision:=MASLinearVirtualizationConsumerCoordinator.ResolveControlSizeRevision(320.0F, viewportExtentPx),
                viewportExtentPx:=viewportExtentPx)
        End Function

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

    End Class

End Namespace
