Option Strict On
Option Explicit On

Imports System
Imports System.Reflection
Imports Nexamas.UI.Host

Namespace Nexamas.UI.Quality

    ''' <summary>
    ''' Runtime-structure proof that FrameScheduler / HostRuntime native surface access is guarded by
    ''' dispatcher ownership boundaries and no longer falls back to direct WinForms calls after BeginInvoke
    ''' rejection.
    ''' </summary>
    Friend NotInheritable Class MASFrameSchedulerDispatcherBoundaryGate

        Private Sub New()
        End Sub

        Friend Shared Function Passes() As Boolean
            Try
                Return ProbeFrameSchedulerBoundaryShape() AndAlso
                       ProbeHostRuntimeResizeBoundaryShape()
            Catch ex As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDiagnosticOnly(ex, "MASFrameSchedulerDispatcherBoundaryGate")
                Return False
            End Try
        End Function

        Private Shared Function ProbeFrameSchedulerBoundaryShape() As Boolean
            Dim schedulerType As Type = GetType(MASSkiaFrameScheduler)

            If Not HasPrivateInstanceMethod(schedulerType, "CanPostToDispatcher") Then Return False
            If Not HasPrivateInstanceMethod(schedulerType, "CanUseSurfaceOnUiThread") Then Return False
            If Not HasPrivateInstanceMethod(schedulerType, "RequestPostedFrame") Then Return False

            If MethodCalls(schedulerType, "PostDispatcherInvalidate", "RequestImmediateFrame") Then Return False
            If Not MethodCalls(schedulerType, "RequestImmediateFrame", "CanUseSurfaceOnUiThread") Then Return False
            If Not MethodCalls(schedulerType, "RequestImmediateFrame", "RequestPostedFrame") Then Return False
            If Not MethodCalls(schedulerType, "InvalidateSurfaceNow", "CanUseSurfaceOnUiThread") Then Return False
            If Not MethodCalls(schedulerType, "OnDispatcherInvalidateTurn", "CanUseSurfaceOnUiThread") Then Return False

            Return True
        End Function

        Private Shared Function ProbeHostRuntimeResizeBoundaryShape() As Boolean
            Dim runtimeType As Type = GetType(MASSkiaHostRuntime)

            If Not HasPrivateInstanceMethod(runtimeType, "CanPostToSurfaceDispatcher") Then Return False
            If Not HasPrivateInstanceMethod(runtimeType, "CanUseSurfaceOnUiThread") Then Return False

            If Not MethodCalls(runtimeType, "PresentResizeFrameNow", "CanUseSurfaceOnUiThread") Then Return False
            If Not MethodCalls(runtimeType, "CommitPendingResizeBoundary", "CanUseSurfaceOnUiThread") Then Return False
            If Not MethodCalls(runtimeType, "CommitPendingResize", "CanUseSurfaceOnUiThread") Then Return False
            If Not MethodCalls(runtimeType, "ScheduleResizeCommit", "RequestInvalidate") Then Return False

            Return True
        End Function

        Private Shared Function HasPrivateInstanceMethod(targetType As Type,
                                                        methodName As String) As Boolean
            Return GetPrivateInstanceMethod(targetType, methodName) IsNot Nothing
        End Function

        Private Shared Function MethodCalls(targetType As Type,
                                            callerName As String,
                                            calleeName As String) As Boolean
            Dim caller As MethodInfo = GetPrivateInstanceMethod(targetType, callerName)
            Dim callee As MethodInfo = GetPrivateInstanceMethod(targetType, calleeName)

            If caller Is Nothing OrElse callee Is Nothing Then Return False

            Dim body As MethodBody = caller.GetMethodBody()
            If body Is Nothing Then Return False

            Dim il As Byte() = body.GetILAsByteArray()
            If il Is Nothing OrElse il.Length = 0 Then Return False

            Dim calleeToken As Integer = callee.MetadataToken

            For i As Integer = 0 To il.Length - 5
                Dim opCode As Byte = il(i)
                If opCode = &H28 OrElse opCode = &H6F Then
                    Dim token As Integer = BitConverter.ToInt32(il, i + 1)
                    If token = calleeToken Then Return True
                End If
            Next

            Return False
        End Function

        Private Shared Function GetPrivateInstanceMethod(targetType As Type,
                                                         methodName As String) As MethodInfo
            If targetType Is Nothing OrElse String.IsNullOrWhiteSpace(methodName) Then Return Nothing

            Return targetType.GetMethod(
                methodName,
                BindingFlags.Instance Or BindingFlags.NonPublic)
        End Function

    End Class

End Namespace
