Option Strict On
Option Explicit On

Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports Nexamas.UI.Controls
Imports Nexamas.UI.Host
Imports Nexamas.UI.Theming
Imports SkiaSharp
Imports Nexamas.UI.Values

Namespace Nexamas.UI.Quality

    ''' <summary>
    ''' Friend-only runtime hardening proof for root input/focus fixes that must hold for SDK consumers.
    ''' The gate deliberately avoids WinForms handles and rendering; it exercises the official internal
    ''' control focus/capture services with synthetic controls so external hook faults cannot leave
    ''' stale capture, half-committed focus state, or DPI-dependent pointer target drift.
    ''' </summary>
    Friend NotInheritable Class MASInputFocusRuntimeHardeningGate

        Private Sub New()
        End Sub

        Friend Shared Function Passes() As Boolean
            Return ProbeCaptureCancelClearsCapturedReference() AndAlso
                   ProbeFocusGotExceptionRollsBack() AndAlso
                   ProbeFocusLostExceptionKeepsPreviousFocus() AndAlso
                   ProbeSuccessfulFocusTransitionCommitsOnlyAtEnd() AndAlso
                   ProbeDpiCoordinateContract() AndAlso
                   ProbeHitTestGeometryRejectsBeforeRoutePredicate() AndAlso
                   ProbeOrdinaryWheelFaultAllowsFallbackScroll() AndAlso
                   ProbeModalWheelFaultFailsClosed() AndAlso
                   ProbeCapturedWheelFaultFailsClosed()
        End Function

        Private Shared Function ProbeCaptureCancelClearsCapturedReference() As Boolean
            Try
                Dim captured As MASControlBase = New FocusProbeControl("capture")

                MASControlsCaptureService.CancelPointerCapture(captured, Nothing)

                Return captured Is Nothing
            Catch ex As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDiagnosticOnly(ex, "MASInputFocusRuntimeHardeningGate.CaptureCancel")
                Return False
            End Try
        End Function

        Private Shared Function ProbeFocusGotExceptionRollsBack() As Boolean
            Try
                Dim focused As MASControlBase = Nothing
                Dim throwingControl As New FocusProbeControl("got-throws") With {
                    .ThrowOnGotFocus = True
                }

                MASControlsFocusService.SetFocusedControl(
                    focused,
                    throwingControl,
                    Function(ctrl As MASControlBase) True)

                Return focused Is Nothing AndAlso
                       Not throwingControl.HasKeyboardFocus AndAlso
                       throwingControl.GotFocusCount = 1
            Catch ex As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDiagnosticOnly(ex, "MASInputFocusRuntimeHardeningGate.FocusGotRollback")
                Return False
            End Try
        End Function

        Private Shared Function ProbeFocusLostExceptionKeepsPreviousFocus() As Boolean
            Try
                Dim oldFocused As New FocusProbeControl("old")
                Dim nextFocused As New FocusProbeControl("next")
                Dim focused As MASControlBase = Nothing

                MASControlsFocusService.SetFocusedControl(
                    focused,
                    oldFocused,
                    Function(ctrl As MASControlBase) True)

                If Not Object.ReferenceEquals(focused, oldFocused) OrElse Not oldFocused.HasKeyboardFocus Then Return False

                oldFocused.ThrowOnLostFocus = True

                MASControlsFocusService.SetFocusedControl(
                    focused,
                    nextFocused,
                    Function(ctrl As MASControlBase) True)

                Return Object.ReferenceEquals(focused, oldFocused) AndAlso
                       oldFocused.HasKeyboardFocus AndAlso
                       Not nextFocused.HasKeyboardFocus AndAlso
                       oldFocused.LostFocusCount = 1
            Catch ex As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDiagnosticOnly(ex, "MASInputFocusRuntimeHardeningGate.FocusLostRollback")
                Return False
            End Try
        End Function

        Private Shared Function ProbeSuccessfulFocusTransitionCommitsOnlyAtEnd() As Boolean
            Try
                Dim oldFocused As New FocusProbeControl("old-success")
                Dim nextFocused As New FocusProbeControl("next-success")
                Dim focused As MASControlBase = Nothing

                MASControlsFocusService.SetFocusedControl(
                    focused,
                    oldFocused,
                    Function(ctrl As MASControlBase) True)

                MASControlsFocusService.SetFocusedControl(
                    focused,
                    nextFocused,
                    Function(ctrl As MASControlBase) True)

                Return Object.ReferenceEquals(focused, nextFocused) AndAlso
                       Not oldFocused.HasKeyboardFocus AndAlso
                       nextFocused.HasKeyboardFocus AndAlso
                       oldFocused.LostFocusCount = 1 AndAlso
                       nextFocused.GotFocusCount = 1
            Catch ex As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDiagnosticOnly(ex, "MASInputFocusRuntimeHardeningGate.FocusCommit")
                Return False
            End Try
        End Function


        Private Shared Function ProbeDpiCoordinateContract() As Boolean
            Try
                Dim scales As Single() = New Single() {1.25F, 1.5F, 2.0F}

                For Each dpi As Single In scales
                    If Not ProbeDpiCoordinateContract(dpi) Then Return False
                Next

                Return True
            Catch ex As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDiagnosticOnly(ex, "MASInputFocusRuntimeHardeningGate.DpiCoordinateContract")
                Return False
            End Try
        End Function

        Private Shared Function ProbeDpiCoordinateContract(dpi As Single) As Boolean
            ThemeManager.EnsureInitializedCore()

            Dim ctx As New MASThemeContext(dpi, New MASTypography(1.0F), ThemeManager.CurrentCore, 0)
            Dim probe As New MASButton("dpi-contract")

            probe.SetLocalLayoutBoundsInternal(New SKRect(10.0F, 20.0F, 50.0F, 60.0F))

            Dim clientCenter As New SKPoint(30.0F, 40.0F)
            Dim clientOutside As New SKPoint(9.0F, 19.0F)
            Dim surfaceCenter As SKPoint = ControlsLayer.ConvertClientInputToSurfacePxForContract(ctx, clientCenter)
            Dim surfaceOutside As SKPoint = ControlsLayer.ConvertClientInputToSurfacePxForContract(ctx, clientOutside)

            If Not NearlyEqual(surfaceCenter.X, clientCenter.X * dpi) Then Return False
            If Not NearlyEqual(surfaceCenter.Y, clientCenter.Y * dpi) Then Return False

            Dim roots As MASControlBase() = New MASControlBase() {probe}
            Dim hit As MASControlBase = MASControlsHitTestService.HitTestTopPointerEnabled(
                roots,
                ctx,
                surfaceCenter,
                Function(ctrl As MASControlBase) True)

            If Not Object.ReferenceEquals(hit, probe) Then Return False

            Dim miss As MASControlBase = MASControlsHitTestService.HitTestTopPointerEnabled(
                roots,
                ctx,
                surfaceOutside,
                Function(ctrl As MASControlBase) True)

            If miss IsNot Nothing Then Return False

            Return True
        End Function

        Private Shared Function NearlyEqual(left As Single, right As Single) As Boolean
            Return Math.Abs(left - right) <= 0.01F
        End Function

        Private Shared Function ProbeHitTestGeometryRejectsBeforeRoutePredicate() As Boolean
            Try
                ThemeManager.EnsureInitializedCore()

                Dim ctx As New MASThemeContext(1.0F, New MASTypography(1.0F), ThemeManager.CurrentCore, 0)
                Dim roots(9999) As MASControlBase

                For i As Integer = 0 To roots.Length - 1
                    Dim probe As New MASButton("hit-test-budget-" & i.ToString(System.Globalization.CultureInfo.InvariantCulture))
                    probe.SetLocalLayoutBoundsInternal(New SKRect(0.0F, 0.0F, 10.0F, 10.0F))
                    roots(i) = probe
                Next

                Dim routePredicateCalls As Integer = 0
                Dim hit As MASControlBase = MASControlsHitTestService.HitTestTopPointerEnabled(
                    roots,
                    ctx,
                    New SKPoint(100000.0F, 100000.0F),
                    Function(ctrl As MASControlBase)
                        routePredicateCalls += 1
                        Return True
                    End Function)

                Return hit Is Nothing AndAlso routePredicateCalls = 0
            Catch ex As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDiagnosticOnly(ex, "MASInputFocusRuntimeHardeningGate.HitTestGeometryBudget")
                Return False
            End Try
        End Function

        Private Shared Function ProbeOrdinaryWheelFaultAllowsFallbackScroll() As Boolean
            Dim sk As SkiaSharp.Views.Desktop.SKGLControl = Nothing

            Try
                sk = New SkiaSharp.Views.Desktop.SKGLControl()

                Dim fallbackCalls As Integer = 0
                Dim pipeline As New MASSkiaInputRoutingPipeline(
                    sk,
                    Function() False,
                    Sub()
                    End Sub)

                pipeline.Register(
                    New WheelProbeParticipant(
                        "ordinary-throws",
                        MASInputRoutingPriority.Controls,
                        New MASInputParticipantCapabilities(acceptsWheel:=True),
                        throwOnWheel:=True,
                        wheelHandler:=Nothing))

                pipeline.Register(
                    New WheelProbeParticipant(
                        "fallback-scroll",
                        MASInputRoutingPriority.ApplicationScroll,
                        New MASInputParticipantCapabilities(acceptsWheel:=True, wheelFallbackOnly:=True),
                        throwOnWheel:=False,
                        wheelHandler:=Sub() fallbackCalls += 1))

                Dim handled As Boolean = pipeline.RoutePointerWheel(New MASSkiaPointerInputArgs(4, 8, MouseButtons.None, -120, Point.Empty))

                Return handled AndAlso fallbackCalls = 1
            Catch ex As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDiagnosticOnly(ex, "MASInputFocusRuntimeHardeningGate.OrdinaryWheelFaultFallback")
                Return False
            Finally
                If sk IsNot Nothing Then sk.Dispose()
            End Try
        End Function

        Private Shared Function ProbeModalWheelFaultFailsClosed() As Boolean
            Return ProbeBlockingWheelFaultFailsClosed(
                "modal-wheel",
                New MASInputParticipantCapabilities(acceptsWheel:=True, blocksLowerPriority:=True))
        End Function

        Private Shared Function ProbeCapturedWheelFaultFailsClosed() As Boolean
            Return ProbeBlockingWheelFaultFailsClosed(
                "captured-wheel",
                New MASInputParticipantCapabilities(acceptsWheel:=True, hasCapture:=True))
        End Function

        Private Shared Function ProbeBlockingWheelFaultFailsClosed(name As String,
                                                                  capabilities As MASInputParticipantCapabilities) As Boolean
            Dim sk As SkiaSharp.Views.Desktop.SKGLControl = Nothing

            Try
                sk = New SkiaSharp.Views.Desktop.SKGLControl()

                Dim fallbackCalls As Integer = 0
                Dim pipeline As New MASSkiaInputRoutingPipeline(
                    sk,
                    Function() False,
                    Sub()
                    End Sub)

                pipeline.Register(
                    New WheelProbeParticipant(
                        name,
                        MASInputRoutingPriority.FloatRuntime,
                        capabilities,
                        throwOnWheel:=True,
                        wheelHandler:=Nothing))

                pipeline.Register(
                    New WheelProbeParticipant(
                        "blocked-fallback",
                        MASInputRoutingPriority.ApplicationScroll,
                        New MASInputParticipantCapabilities(acceptsWheel:=True, wheelFallbackOnly:=True),
                        throwOnWheel:=False,
                        wheelHandler:=Sub() fallbackCalls += 1))

                Dim handled As Boolean = pipeline.RoutePointerWheel(New MASSkiaPointerInputArgs(4, 8, MouseButtons.None, -120, Point.Empty))

                Return handled AndAlso fallbackCalls = 0
            Catch ex As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDiagnosticOnly(ex, "MASInputFocusRuntimeHardeningGate.BlockingWheelFaultClosed")
                Return False
            Finally
                If sk IsNot Nothing Then sk.Dispose()
            End Try
        End Function

        Private NotInheritable Class FocusProbeControl
            Inherits MASControlBase

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

            Friend Property ThrowOnGotFocus As Boolean
            Friend Property ThrowOnLostFocus As Boolean
            Friend Property GotFocusCount As Integer
            Friend Property LostFocusCount As Integer

            Protected Overrides Function WantsKeyboardFocus() As Boolean
                Return True
            End Function

            Protected Overrides Sub OnGotKeyboardFocus()
                GotFocusCount += 1
                If ThrowOnGotFocus Then Throw New InvalidOperationException("Synthetic focus-got failure.")
            End Sub

            Protected Overrides Sub OnLostKeyboardFocus()
                LostFocusCount += 1
                If ThrowOnLostFocus Then Throw New InvalidOperationException("Synthetic focus-lost failure.")
            End Sub

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

        Private NotInheritable Class WheelProbeParticipant
            Implements IMASInputParticipant

            Private ReadOnly _name As String
            Private ReadOnly _priority As MASInputRoutingPriority
            Private ReadOnly _capabilities As MASInputParticipantCapabilities
            Private ReadOnly _throwOnWheel As Boolean
            Private ReadOnly _wheelHandler As Action

            Friend Sub New(name As String,
                           priority As MASInputRoutingPriority,
                           capabilities As MASInputParticipantCapabilities,
                           throwOnWheel As Boolean,
                           wheelHandler As Action)

                _name = If(name, String.Empty)
                _priority = priority
                _capabilities = If(capabilities, MASInputParticipantCapabilities.None())
                _throwOnWheel = throwOnWheel
                _wheelHandler = wheelHandler
            End Sub

            Public ReadOnly Property Name As String Implements IMASInputParticipant.Name
                Get
                    Return _name
                End Get
            End Property

            Public ReadOnly Property Priority As MASInputRoutingPriority Implements IMASInputParticipant.Priority
                Get
                    Return _priority
                End Get
            End Property

            Public ReadOnly Property HasPointerCapture As Boolean Implements IMASInputParticipant.HasPointerCapture
                Get
                    Return _capabilities.HasCapture
                End Get
            End Property

            Public ReadOnly Property Capabilities As MASInputParticipantCapabilities Implements IMASInputParticipant.Capabilities
                Get
                    Return _capabilities
                End Get
            End Property

            Public Function WantsPointer(xPx As Integer, yPx As Integer) As Boolean Implements IMASInputParticipant.WantsPointer
                Return False
            End Function

            Public Sub ReleasePointerCapture() Implements IMASInputParticipant.ReleasePointerCapture
            End Sub

            Public Sub ClearPointerHover() Implements IMASInputParticipant.ClearPointerHover
            End Sub

            Public Sub HostLostFocus() Implements IMASInputParticipant.HostLostFocus
            End Sub

            Public Function PointerMove(args As MASSkiaPointerInputArgs) As MASInputRoutingResult Implements IMASInputParticipant.PointerMove
                Return MASInputRoutingResult.ContinueRouting()
            End Function

            Public Function PointerDown(args As MASSkiaPointerInputArgs) As MASInputRoutingResult Implements IMASInputParticipant.PointerDown
                Return MASInputRoutingResult.ContinueRouting()
            End Function

            Public Function PointerUp(args As MASSkiaPointerInputArgs) As MASInputRoutingResult Implements IMASInputParticipant.PointerUp
                Return MASInputRoutingResult.ContinueRouting()
            End Function

            Public Function PointerWheel(args As MASSkiaPointerInputArgs) As MASInputRoutingResult Implements IMASInputParticipant.PointerWheel
                If _throwOnWheel Then Throw New InvalidOperationException("Synthetic wheel route failure.")

                If _wheelHandler IsNot Nothing Then _wheelHandler.Invoke()

                Return MASInputRoutingResult.HandledResult(requiresInvalidate:=False)
            End Function

            Public Function PointerLeave() As MASInputRoutingResult Implements IMASInputParticipant.PointerLeave
                Return MASInputRoutingResult.ContinueRouting()
            End Function

            Public Function KeyDown(e As KeyEventArgs) As MASInputRoutingResult Implements IMASInputParticipant.KeyDown
                Return MASInputRoutingResult.ContinueRouting()
            End Function

            Public Function KeyUp(e As KeyEventArgs) As MASInputRoutingResult Implements IMASInputParticipant.KeyUp
                Return MASInputRoutingResult.ContinueRouting()
            End Function

            Public Function TextInput(e As KeyPressEventArgs, text As String) As MASInputRoutingResult Implements IMASInputParticipant.TextInput
                Return MASInputRoutingResult.ContinueRouting()
            End Function

            Public Function Win32ImeMessage(m As Message) As MASInputRoutingResult Implements IMASInputParticipant.Win32ImeMessage
                Return MASInputRoutingResult.ContinueRouting()
            End Function
        End Class



    End Class

End Namespace
