Option Strict On
Option Explicit On

Imports System
Imports Nexamas.UI.Controls
Imports Nexamas.UI.Motion
Imports Nexamas.UI.Theming
Imports SkiaSharp

Namespace Nexamas.UI.Quality

    ''' <summary>
    ''' Runtime proof that Motion frame consumers are isolated by their owning host clock.
    ''' </summary>
    ''' <remarks>
    ''' The probes intentionally avoid creating WinForms windows.  They exercise the same internal
    ''' host-scoped clock objects that MASSkiaFrameScheduler owns, plus the MASControlRuntimeServices
    ''' binding used by attached controls to resolve their scheduler-owned clock.
    ''' </remarks>
    Friend NotInheritable Class MASMultiWindowMotionIsolationGate

        Private Sub New()
        End Sub

        Friend Shared Function Passes() As Boolean
            Dim previousMode As MASMotionReducedMotionMode = MASMotionSystem.ReducedMotionMode

            Try
                MASMotionSystem.ReducedMotionMode = MASMotionReducedMotionMode.FullMotion

                Return ProbeDirectClockIsolation() AndAlso
                       ProbeControlRuntimeClockResolution() AndAlso
                       ProbeExplicitClockRouteForNonControlOwners()
            Catch ex As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDiagnosticOnly(ex, "MASMultiWindowMotionIsolationGate")
                Return False
            Finally
                MASMotionSystem.ReducedMotionMode = previousMode
            End Try
        End Function

        Private Shared Function ProbeDirectClockIsolation() As Boolean
            Dim clockA As New MASMotionFrameClock("multi-window-direct-a")
            Dim clockB As New MASMotionFrameClock("multi-window-direct-b")
            Dim ownerA As New Object()
            Dim ownerB As New Object()
            Dim requestA As Integer = 0
            Dim requestB As Integer = 0
            Dim advanceA As Integer = 0
            Dim advanceB As Integer = 0

            Try
                clockA.Register(
                    owner:=ownerA,
                    consumerName:="probe-a",
                    requestFrame:=Sub()
                                      requestA += 1
                                  End Sub,
                    advanceFrame:=Function(deltaMs As Integer)
                                      advanceA += 1
                                      Return True
                                  End Function)

                clockB.Register(
                    owner:=ownerB,
                    consumerName:="probe-b",
                    requestFrame:=Sub()
                                      requestB += 1
                                  End Sub,
                    advanceFrame:=Function(deltaMs As Integer)
                                      advanceB += 1
                                      Return True
                                  End Function)

                If clockA.ActiveConsumerCount() <> 1 Then Return False
                If clockB.ActiveConsumerCount() <> 1 Then Return False

                clockA.AdvanceScheduledAnimations()

                If advanceA <> 1 Then Return False
                If advanceB <> 0 Then Return False
                If requestA < 2 Then Return False
                If requestB <> 1 Then Return False
                If clockA.ActiveConsumerCount() <> 1 Then Return False
                If clockB.ActiveConsumerCount() <> 1 Then Return False

                Return True
            Finally
                clockA.Dispose()
                clockB.Dispose()
            End Try
        End Function

        Private Shared Function ProbeControlRuntimeClockResolution() As Boolean
            Dim clockA As New MASMotionFrameClock("multi-window-control-a")
            Dim clockB As New MASMotionFrameClock("multi-window-control-b")
            Dim controlA As New MotionProbeControl("control-a")
            Dim controlB As New MotionProbeControl("control-b")
            Dim applyA As Integer = 0
            Dim applyB As Integer = 0
            Dim requestA As Integer = 0
            Dim requestB As Integer = 0
            Dim runnerA As MASMotionTimelineRunner = Nothing
            Dim runnerB As MASMotionTimelineRunner = Nothing

            Try
                controlA.SetRuntimeServicesInternal(CreateProbeServices(clockA))
                controlB.SetRuntimeServicesInternal(CreateProbeServices(clockB))

                Dim plan As MASMotionTransitionPlan =
                    MASMotionSystem.CreateTransitionPlan(MASMotionTokenId.PressDown, 0.0F, 1.0F, 10000, MASMotionEasing.Linear)

                runnerA = MASMotionSystem.CreateTimelineRunner(
                    owner:=controlA,
                    consumerName:="multi-window-control-a",
                    plan:=plan,
                    requestFrame:=Sub()
                                      requestA += 1
                                  End Sub,
                    applyFrame:=Sub(frame As MASMotionFrame)
                                    applyA += 1
                                End Sub)

                runnerB = MASMotionSystem.CreateTimelineRunner(
                    owner:=controlB,
                    consumerName:="multi-window-control-b",
                    plan:=plan,
                    requestFrame:=Sub()
                                      requestB += 1
                                  End Sub,
                    applyFrame:=Sub(frame As MASMotionFrame)
                                    applyB += 1
                                End Sub)

                runnerA.Start()
                runnerB.Start()

                If clockA.ActiveConsumerCount() <> 1 Then Return False
                If clockB.ActiveConsumerCount() <> 1 Then Return False
                If applyA <> 1 Then Return False
                If applyB <> 1 Then Return False

                clockA.AdvanceScheduledAnimations()

                If applyA <= 1 Then Return False
                If applyB <> 1 Then Return False
                If requestA < 2 Then Return False
                If requestB <> 1 Then Return False
                If clockA.ActiveConsumerCount() <> 1 Then Return False
                If clockB.ActiveConsumerCount() <> 1 Then Return False

                Return True
            Finally
                If runnerA IsNot Nothing Then runnerA.Dispose()
                If runnerB IsNot Nothing Then runnerB.Dispose()
                clockA.Dispose()
                clockB.Dispose()
                controlA.Dispose()
                controlB.Dispose()
            End Try
        End Function

        Private Shared Function ProbeExplicitClockRouteForNonControlOwners() As Boolean
            Dim clockA As New MASMotionFrameClock("multi-window-explicit-a")
            Dim clockB As New MASMotionFrameClock("multi-window-explicit-b")
            Dim owner As New Object()
            Dim applyCount As Integer = 0
            Dim runner As MASMotionTimelineRunner = Nothing

            Try
                Dim plan As MASMotionTransitionPlan =
                    MASMotionSystem.CreateTransitionPlan(MASMotionTokenId.FloatOpen, 0.0F, 1.0F, 10000, MASMotionEasing.Linear)

                runner = MASMotionSystem.CreateTimelineRunner(
                    owner:=owner,
                    consumerName:="multi-window-explicit-owner",
                    plan:=plan,
                    requestFrame:=Sub()
                                  End Sub,
                    applyFrame:=Sub(frame As MASMotionFrame)
                                    applyCount += 1
                                End Sub,
                    motionFrameClock:=clockA)

                runner.Start()

                If clockA.ActiveConsumerCount() <> 1 Then Return False
                If clockB.ActiveConsumerCount() <> 0 Then Return False
                If applyCount <> 1 Then Return False

                clockB.AdvanceScheduledAnimations()
                If applyCount <> 1 Then Return False

                clockA.AdvanceScheduledAnimations()
                If applyCount <= 1 Then Return False

                Return True
            Finally
                If runner IsNot Nothing Then runner.Dispose()
                clockA.Dispose()
                clockB.Dispose()
            End Try
        End Function

        Private Shared Function CreateProbeServices(clock As MASMotionFrameClock) As MASControlRuntimeServices
            Return New MASControlRuntimeServices(
                getFloatRuntime:=Function() Nothing,
                getSurfaceSizePx:=Function() SKSizeI.Empty,
                getMotionFrameClock:=Function() clock)
        End Function

        Private NotInheritable Class MotionProbeControl
            Inherits MASControlBase

            Friend Sub New(name As String)
                Me.Name = If(name, String.Empty)
            End Sub

            Protected Overrides Sub Render(canvas As SKCanvas,
                                           ctx As MASThemeContext,
                                           pixelBounds As SKRect)
                ' Runtime clock-isolation probe only; no drawing path is exercised.
            End Sub
        End Class

    End Class

End Namespace
