Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.Windows.Forms
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Diagnostics
Imports Nexamas.UI.Layout
Imports Nexamas.UI.Theming

Namespace Nexamas.UI.Application

    ''' <summary>
    ''' Official beginner entry point for MAS-powered applications.
    ''' MASApplication owns application-level lifetime and creates MASApplicationWindow instances;
    ''' it does not replace MASSkiaRootHost, it owns it through MASApplicationWindow.
    ''' </summary>
    Public NotInheritable Class MASApplication
        Implements IDisposable

        Private Shared ReadOnly _runtimeGate As New Object()
        Private Shared ReadOnly _diagnostics As New MASApplicationDiagnostics()
        Private Shared _runtimeOptions As New MASApplicationRuntimeOptions()
        Private Shared _runtimeConfigurationFrozenByWindowCreation As Boolean
        Private Shared _runtimeLiveWindowCount As Integer

        Private ReadOnly _gate As New Object()
        Private ReadOnly _windows As New List(Of MASApplicationWindow)()
        Private ReadOnly _theme As New MASApplicationTheme()
        Private ReadOnly _sizing As New MASApplicationSizing()
        Private ReadOnly _outputGateway As New MASApplicationOutputGateway()
        Private ReadOnly _output As MASApplicationOutput
        Private _disposed As Boolean

        Private Sub New()
            _output = New MASApplicationOutput(_outputGateway)
        End Sub

        Public Shared Function Create() As MASApplication
            ApplyRuntimeOptionsSnapshot()
            Return New MASApplication()
        End Function

        ''' <summary>
        ''' Official public, read-only diagnostics gateway for SDK consumers.
        ''' </summary>
        ''' <remarks>
        ''' The gateway exposes safe immutable snapshots only. It does not expose runtime fault
        ''' internals, source paths, source lines, or mutable diagnostics channels.
        ''' </remarks>
        Public Shared ReadOnly Property Diagnostics As MASApplicationDiagnostics
            Get
                Return _diagnostics
            End Get
        End Property

        ''' <summary>
        ''' Configures Nexamas UI runtime startup and diagnostics behavior. Call this before creating the first MAS window.
        ''' </summary>
        ''' <remarks>
        ''' This is the official SDK-safe, process-wide diagnostics entry point. It does not expose RootHost,
        ''' MASExceptionSilencer, or the internal runtime options object; it only projects the
        ''' reviewed switches that a packed consumer may need for production diagnostics or
        ''' fail-fast development sessions. Configuration is frozen while any MAS window is alive
        ''' so one host cannot silently change diagnostics/startup policy for another live host in the same process.
        ''' </remarks>
        Public Shared Sub ConfigureRuntime(Optional writeArchitectureReportsOnStartup As Boolean = False,
                                           Optional writeDeepArchitectureReportsOnStartup As Boolean = False,
                                           Optional registerDefaultIconCatalogOnStartup As Boolean = True,
                                           Optional enableDiagnosticsDiskLogging As Boolean = False,
                                           Optional throwOnSwallowedException As Boolean = False,
                                           Optional throwOnLifecycleBreakingException As Boolean = False,
                                           Optional traceRepeatedBoundaryExceptions As Boolean = False,
                                           Optional failFastOnContractlessControlConstruction As Boolean = False)

            SyncLock _runtimeGate
                ThrowIfRuntimeConfigurationFrozenLocked()

                _runtimeOptions = New MASApplicationRuntimeOptions() With {
                    .WriteArchitectureReportsOnStartup = writeArchitectureReportsOnStartup,
                    .WriteDeepArchitectureReportsOnStartup = writeDeepArchitectureReportsOnStartup,
                    .RegisterDefaultIconCatalogOnStartup = registerDefaultIconCatalogOnStartup,
                    .EnableDiagnosticsDiskLogging = enableDiagnosticsDiskLogging,
                    .ThrowOnSwallowedException = throwOnSwallowedException,
                    .ThrowOnLifecycleBreakingException = throwOnLifecycleBreakingException,
                    .TraceRepeatedBoundaryExceptions = traceRepeatedBoundaryExceptions,
                    .FailFastOnContractlessControlConstruction = failFastOnContractlessControlConstruction
                }

                ApplyRuntimeOptionsSnapshotLocked()
            End SyncLock
        End Sub

        Friend Shared Sub ConfigureRuntime(options As MASApplicationRuntimeOptions)
            SyncLock _runtimeGate
                ThrowIfRuntimeConfigurationFrozenLocked()
                _runtimeOptions = If(options Is Nothing, New MASApplicationRuntimeOptions(), options.Clone())
                ApplyRuntimeOptionsSnapshotLocked()
            End SyncLock
        End Sub

        Friend Shared Function GetRuntimeOptionsSnapshot() As MASApplicationRuntimeOptions
            SyncLock _runtimeGate
                Return _runtimeOptions.Clone()
            End SyncLock
        End Function

        Private Shared Sub ApplyRuntimeOptionsSnapshot()
            SyncLock _runtimeGate
                ApplyRuntimeOptionsSnapshotLocked()
            End SyncLock
        End Sub

        Private Shared Sub MarkRuntimeWindowCreated()
            SyncLock _runtimeGate
                _runtimeLiveWindowCount += 1
                _runtimeConfigurationFrozenByWindowCreation = _runtimeLiveWindowCount > 0
            End SyncLock
        End Sub

        Private Shared Sub MarkRuntimeWindowDisposed()
            SyncLock _runtimeGate
                If _runtimeLiveWindowCount > 0 Then _runtimeLiveWindowCount -= 1
                _runtimeConfigurationFrozenByWindowCreation = _runtimeLiveWindowCount > 0
            End SyncLock
        End Sub

        Private Shared Sub ThrowIfRuntimeConfigurationFrozenLocked()
            If Not _runtimeConfigurationFrozenByWindowCreation Then Return

            Throw New InvalidOperationException(
                "MASApplication.ConfigureRuntime is process-wide and must be called before creating a MAS window. Runtime diagnostics/startup policy is frozen while MAS windows are alive to prevent one live host from silently changing another host's SDK behavior.")
        End Sub

        Private Shared Sub ApplyRuntimeOptionsSnapshotLocked()
            NexamasUIRuntimeBootstrapOptions.Configure(
                writeArchitectureReportsOnStartup:=_runtimeOptions.WriteArchitectureReportsOnStartup,
                writeDeepArchitectureReportsOnStartup:=_runtimeOptions.WriteDeepArchitectureReportsOnStartup,
                registerDefaultIconCatalogOnStartup:=_runtimeOptions.RegisterDefaultIconCatalogOnStartup,
                enableDiagnosticsDiskLogging:=_runtimeOptions.EnableDiagnosticsDiskLogging)

            MASExceptionSilencer.ConfigureStrictness(
                throwOnAnySwallowedException:=_runtimeOptions.ThrowOnSwallowedException,
                throwOnLifecycleBreakingException:=_runtimeOptions.ThrowOnLifecycleBreakingException,
                traceRepeatedBoundaryExceptions:=_runtimeOptions.TraceRepeatedBoundaryExceptions)

            MASArchitectureRuntime.ConfigureConstructionProbeStrictMode(
                _runtimeOptions.FailFastOnContractlessControlConstruction)
        End Sub

        Public ReadOnly Property Theme As MASApplicationTheme
            Get
                ThrowIfDisposed()
                Return _theme
            End Get
        End Property

        ''' <summary>
        ''' Official application-level size profile facade used by settings pages to switch Compact/Default/Comfortable/Large/Touch profiles.
        ''' </summary>
        Public ReadOnly Property Sizing As MASApplicationSizing
            Get
                ThrowIfDisposed()
                Return _sizing
            End Get
        End Property

        ''' <summary>
        ''' Official public Output facade for readiness, chart-foundation, report-preview, and render-backed visual-capture exports.
        ''' </summary>
        Public ReadOnly Property Output As MASApplicationOutput
            Get
                ThrowIfDisposed()
                Return _output
            End Get
        End Property

        ''' <summary>
        ''' Internal Output gateway used by certification gates to prove the public facade remains Application-owned.
        ''' </summary>
        Friend ReadOnly Property OutputGateway As MASApplicationOutputGateway
            Get
                Return _outputGateway
            End Get
        End Property

        Public Function CreateWindow(owner As Form,
                                     Optional dockStyle As DockStyle = DockStyle.Fill,
                                     Optional autoHookPointerInput As Boolean = True,
                                     Optional setupImmediately As Boolean = True,
                                     Optional rebuildCallback As Action = Nothing) As MASApplicationWindow

            ThrowIfDisposed()
            If owner Is Nothing Then Throw New ArgumentNullException(NameOf(owner))

            ApplyRuntimeOptionsSnapshot()

            Dim window As MASApplicationWindow = Nothing

            Try
                window = New MASApplicationWindow(owner, dockStyle, autoHookPointerInput, rebuildCallback)

                If setupImmediately Then
                    window.Setup()
                End If

                AddHandler window.Disposed, AddressOf OnWindowDisposed

                SyncLock _gate
                    If _disposed Then Throw New ObjectDisposedException(NameOf(MASApplication))
                    _windows.Add(window)
                End SyncLock

                MarkRuntimeWindowCreated()
                Return window
            Catch
                If window IsNot Nothing Then
                    Try
                        RemoveHandler window.Disposed, AddressOf OnWindowDisposed
                    Catch masCaughtExceptionRemoveHandler As Exception
                        MASExceptionSilencer.SwallowDisposeCleanup(
                            masCaughtExceptionRemoveHandler,
                            "MASApplication.CreateWindow.Rollback.RemoveDisposedHandler")
                    End Try

                    Try
                        window.Dispose()
                    Catch masCaughtExceptionWindowRollback As Exception
                        MASExceptionSilencer.SwallowDisposeCleanup(
                            masCaughtExceptionWindowRollback,
                            "MASApplication.CreateWindow.Rollback.DisposeWindow")
                    End Try
                End If

                Throw
            End Try
        End Function

        ''' <summary>
        ''' Creates a MASApplicationWindow for one WinForms owner in a single call and owns the created
        ''' MASApplication lifetime until the owner form closes. Use this for small demos and simple external
        ''' applications that need the official MAS runtime without manually storing the MASApplication object.
        ''' Larger multi-window applications can still use Create()/CreateWindow() to share one application lifetime.
        ''' </summary>
        Public Shared Function AttachWindow(owner As Form,
                                            Optional dockStyle As DockStyle = DockStyle.Fill,
                                            Optional autoHookPointerInput As Boolean = True,
                                            Optional setupImmediately As Boolean = True,
                                            Optional rebuildCallback As Action = Nothing,
                                            Optional configure As Action(Of MASApplicationWindow) = Nothing) As MASApplicationWindow

            If owner Is Nothing Then Throw New ArgumentNullException(NameOf(owner))

            Dim application As MASApplication = MASApplication.Create()

            Try
                Dim window As MASApplicationWindow = application.CreateWindow(
                    owner:=owner,
                    dockStyle:=dockStyle,
                    autoHookPointerInput:=autoHookPointerInput,
                    setupImmediately:=setupImmediately,
                    rebuildCallback:=rebuildCallback)

                If configure IsNot Nothing Then
                    configure.Invoke(window)
                End If

                Dim lifetime As New MASAttachWindowLifetime(owner, window, application)
                lifetime.Attach()

                Return window
            Catch masCaughtException1 As Exception
                application.Dispose()
                Throw
            End Try
        End Function

        Private NotInheritable Class MASAttachWindowLifetime
            Private ReadOnly _gate As New Object()
            Private ReadOnly _owner As Form
            Private ReadOnly _window As MASApplicationWindow
            Private _application As MASApplication
            Private _attached As Boolean
            Private _disposed As Boolean

            Public Sub New(owner As Form, window As MASApplicationWindow, application As MASApplication)
                If owner Is Nothing Then Throw New ArgumentNullException(NameOf(owner))
                If window Is Nothing Then Throw New ArgumentNullException(NameOf(window))
                If application Is Nothing Then Throw New ArgumentNullException(NameOf(application))

                _owner = owner
                _window = window
                _application = application
            End Sub

            Public Sub Attach()
                SyncLock _gate
                    If _disposed Then Throw New ObjectDisposedException(NameOf(MASAttachWindowLifetime))
                    If _attached Then Return

                    AddHandler _owner.FormClosed, AddressOf OwnerFormClosed
                    AddHandler _owner.Disposed, AddressOf OwnerDisposed
                    AddHandler _window.Disposed, AddressOf WindowDisposed
                    _attached = True
                End SyncLock
            End Sub

            Private Sub OwnerFormClosed(sender As Object, e As FormClosedEventArgs)
                DisposeApplicationOnce("MASApplication.AttachWindow.OwnerFormClosed")
            End Sub

            Private Sub OwnerDisposed(sender As Object, e As EventArgs)
                DisposeApplicationOnce("MASApplication.AttachWindow.OwnerDisposed")
            End Sub

            Private Sub WindowDisposed(sender As Object, e As EventArgs)
                DisposeApplicationOnce("MASApplication.AttachWindow.WindowDisposed")
            End Sub

            Private Sub DisposeApplicationOnce(diagnosticContext As String)
                Dim applicationToDispose As MASApplication = Nothing

                SyncLock _gate
                    If _disposed Then Return
                    _disposed = True
                    applicationToDispose = _application
                    _application = Nothing
                    DetachHandlersLocked()
                End SyncLock

                If applicationToDispose Is Nothing Then Return

                Try
                    applicationToDispose.Dispose()
                Catch masCaughtExceptionDisposeApplication As Exception
                    MASExceptionSilencer.SwallowDisposeCleanup(
                        masCaughtExceptionDisposeApplication,
                        diagnosticContext)
                End Try
            End Sub

            Private Sub DetachHandlersLocked()
                If Not _attached Then Return
                _attached = False

                Try
                    RemoveHandler _owner.FormClosed, AddressOf OwnerFormClosed
                Catch masCaughtExceptionOwnerFormClosed As Exception
                    MASExceptionSilencer.SwallowDisposeCleanup(
                        masCaughtExceptionOwnerFormClosed,
                        "MASApplication.AttachWindow.Detach.OwnerFormClosed")
                End Try

                Try
                    RemoveHandler _owner.Disposed, AddressOf OwnerDisposed
                Catch masCaughtExceptionOwnerDisposed As Exception
                    MASExceptionSilencer.SwallowDisposeCleanup(
                        masCaughtExceptionOwnerDisposed,
                        "MASApplication.AttachWindow.Detach.OwnerDisposed")
                End Try

                Try
                    RemoveHandler _window.Disposed, AddressOf WindowDisposed
                Catch masCaughtExceptionWindowDisposed As Exception
                    MASExceptionSilencer.SwallowDisposeCleanup(
                        masCaughtExceptionWindowDisposed,
                        "MASApplication.AttachWindow.Detach.WindowDisposed")
                End Try
            End Sub
        End Class

        Public ReadOnly Property IsDisposed As Boolean
            Get
                Return _disposed
            End Get
        End Property

        Public ReadOnly Property WindowCount As Integer
            Get
                ThrowIfDisposed()

                SyncLock _gate
                    Return _windows.Count
                End SyncLock
            End Get
        End Property

        Friend Function GetWindowsSnapshot() As MASApplicationWindow()
            ThrowIfDisposed()

            SyncLock _gate
                Return _windows.ToArray()
            End SyncLock
        End Function

        Public Sub Refresh()
            ThrowIfDisposed()

            Dim snapshot As MASApplicationWindow()
            SyncLock _gate
                snapshot = _windows.ToArray()
            End SyncLock

            For Each window As MASApplicationWindow In snapshot
                If window IsNot Nothing Then
                    window.Refresh()
                End If
            Next
        End Sub

        Private Sub OnWindowDisposed(sender As Object, e As EventArgs)
            Dim window As MASApplicationWindow = TryCast(sender, MASApplicationWindow)
            If window Is Nothing Then Return

            RemoveHandler window.Disposed, AddressOf OnWindowDisposed

            SyncLock _gate
                _windows.Remove(window)
            End SyncLock

            MarkRuntimeWindowDisposed()
        End Sub

        Private Sub ThrowIfDisposed()
            If _disposed Then Throw New ObjectDisposedException(NameOf(MASApplication))
        End Sub

        Friend Function CreateLayeredSceneHost(owner As Form,
                                       drawScene As MASApplicationLayeredSceneHost.DrawSceneCallback,
                                       Optional renderPadding As Integer = 2,
                                       Optional supersampleScale As Integer = 2) As MASApplicationLayeredSceneHost

            ThrowIfDisposed()

            If owner Is Nothing Then Throw New ArgumentNullException(NameOf(owner))
            If drawScene Is Nothing Then Throw New ArgumentNullException(NameOf(drawScene))

            Return New MASApplicationLayeredSceneHost(
        owner:=owner,
        drawScene:=drawScene,
        renderPadding:=renderPadding,
        supersampleScale:=supersampleScale)

        End Function

        Public Sub Dispose() Implements IDisposable.Dispose
            If _disposed Then Return
            _disposed = True


            Dim snapshot As MASApplicationWindow()
            SyncLock _gate
                snapshot = _windows.ToArray()
                _windows.Clear()
            End SyncLock

            For Each window As MASApplicationWindow In snapshot
                Try
                    If window IsNot Nothing Then window.Dispose()
                Catch masCaughtException1 As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(masCaughtException1)
                End Try
            Next
        End Sub

    End Class

End Namespace
