Option Strict On
Option Explicit On

Imports System
Imports System.Windows.Forms
Imports Nexamas.UI.Composition
Imports Nexamas.UI.Host
Imports Nexamas.UI.Rendering
Imports SkiaSharp

Namespace Nexamas.UI.Application

    ''' <summary>
    ''' Beginner-facing owner of one MAS window host.
    ''' It hides normal RootHost ownership while keeping the technical host available as AdvancedRootHost.
    ''' </summary>
    Public NotInheritable Class MASApplicationWindow
        Implements IDisposable

        Private ReadOnly _rootHost As MASSkiaRootHost
        Private ReadOnly _services As MASApplicationWindowServices
        Private ReadOnly _shell As MASApplicationWindowShell
        Private ReadOnly _geometry As MASApplicationWindowGeometry
        Private ReadOnly _rendering As MASApplicationWindowRendering
        Private ReadOnly _controls As MASApplicationWindowControls
        Private ReadOnly _pages As MASApplicationPageHost
        Private ReadOnly _input As MASApplicationWindowInput
        Private ReadOnly _theme As MASApplicationWindowTheme
        Private ReadOnly _sizing As MASApplicationWindowSizing
        Private ReadOnly _settings As MASApplicationWindowSettings
        Private ReadOnly _presentation As MASApplicationWindowPresentation
        Private ReadOnly _chrome As MASApplicationWindowChrome
        Private _disposed As Boolean

        Public Event Disposed As EventHandler

        Friend Sub New(owner As Form,
                       dockStyle As DockStyle,
                       autoHookPointerInput As Boolean,
                       rebuildCallback As Action)

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

            Try
                _rootHost = New MASSkiaRootHost(
                    owner:=owner,
                    dockStyle:=dockStyle,
                    autoHookPointerInput:=autoHookPointerInput,
                    rebuildCallback:=rebuildCallback)

                _services = New MASApplicationWindowServices(_rootHost)
                _shell = New MASApplicationWindowShell(_rootHost)
                _geometry = New MASApplicationWindowGeometry(_rootHost)
                _rendering = New MASApplicationWindowRendering(_rootHost)
                _controls = New MASApplicationWindowControls(_rootHost, _shell)
                _pages = New MASApplicationPageHost(Me)
                _input = New MASApplicationWindowInput(_rootHost, _rootHost.InputCore, AddressOf ThrowIfDisposed)
                _theme = New MASApplicationWindowTheme(_rootHost)
                _sizing = New MASApplicationWindowSizing(_rootHost)
                _settings = New MASApplicationWindowSettings(Me)
                _presentation = New MASApplicationWindowPresentation(owner, _rootHost)
                _chrome = New MASApplicationWindowChrome(owner)

                AddHandler owner.FormClosed, AddressOf OwnerForm_FormClosed
                AddHandler owner.Disposed, AddressOf OwnerForm_Disposed
            Catch
                Try
                    RemoveHandler owner.FormClosed, AddressOf OwnerForm_FormClosed
                Catch masCaughtExceptionFormClosedRollback As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(
                        masCaughtExceptionFormClosedRollback,
                        "MASApplicationWindow.Ctor.Rollback.FormClosed")
                End Try

                Try
                    RemoveHandler owner.Disposed, AddressOf OwnerForm_Disposed
                Catch masCaughtExceptionFormDisposedRollback As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(
                        masCaughtExceptionFormDisposedRollback,
                        "MASApplicationWindow.Ctor.Rollback.FormDisposed")
                End Try

                Try
                    If _rootHost IsNot Nothing Then _rootHost.Dispose()
                Catch masCaughtExceptionRootRollback As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(
                        masCaughtExceptionRootRollback,
                        "MASApplicationWindow.Ctor.Rollback.RootHost")
                End Try

                Throw
            End Try
        End Sub

        ''' <summary>
        ''' Advanced compatibility access. New beginner code should prefer the typed facades on this class.
        ''' </summary>
        <Obsolete("AdvancedRootHost is a internal advanced compatibility hook. Prefer MASApplicationWindow typed facades (Services/Shell/Controls/Theme/Input/Geometry) for new SDK code.", True)>
        Friend ReadOnly Property AdvancedRootHost As MASSkiaRootHost
            Get
                Return _rootHost
            End Get
        End Property

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

        Friend ReadOnly Property OwnerForm As Form
            Get
                ThrowIfDisposed()
                Return _rootHost.OwnerForm
            End Get
        End Property

        Public ReadOnly Property Services As MASApplicationWindowServices
            Get
                ThrowIfDisposed()
                Return _services
            End Get
        End Property

        Public ReadOnly Property Shell As MASApplicationWindowShell
            Get
                ThrowIfDisposed()
                Return _shell
            End Get
        End Property

        Public ReadOnly Property Geometry As MASApplicationWindowGeometry
            Get
                ThrowIfDisposed()
                Return _geometry
            End Get
        End Property

        Friend ReadOnly Property Rendering As MASApplicationWindowRendering
            Get
                Return _rendering
            End Get
        End Property

        Public ReadOnly Property Controls As MASApplicationWindowControls
            Get
                ThrowIfDisposed()
                Return _controls
            End Get
        End Property

        Friend ReadOnly Property ControlsCoreForDispose As MASApplicationWindowControls
            Get
                Return _controls
            End Get
        End Property

        ''' <summary>
        ''' Official same-window Application page host. Use it when an application should keep one
        ''' MAS shell and swap workspace content through LayoutApplicationSurface instead of opening
        ''' new Forms or maintaining hidden page panels.
        ''' </summary>
        Public ReadOnly Property Pages As MASApplicationPageHost
            Get
                ThrowIfDisposed()
                Return _pages
            End Get
        End Property

        Public ReadOnly Property Input As MASApplicationWindowInput
            Get
                ThrowIfDisposed()
                Return _input
            End Get
        End Property

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

        ''' <summary>
        ''' Official size profile facade for this MAS window. Profile selection is global; the window refresh hook is local.
        ''' </summary>
        Public ReadOnly Property Sizing As MASApplicationWindowSizing
            Get
                ThrowIfDisposed()
                Return _sizing
            End Get
        End Property

        ''' <summary>
        ''' Official Application settings facade for this MAS window.
        ''' </summary>
        Friend ReadOnly Property Settings As MASApplicationWindowSettings
            Get
                ThrowIfDisposed()
                Return _settings
            End Get
        End Property

        ''' <summary>
        ''' Official native window chrome facade for this MAS window.
        ''' Use this instead of consuming MASDwmShadowHost / MASDwmShadow directly.
        ''' </summary>
        Public ReadOnly Property Chrome As MASApplicationWindowChrome
            Get
                ThrowIfDisposed()
                Return _chrome
            End Get
        End Property

        ''' <summary>
        ''' Official host-window presentation facade for size, placement, restore, and user preferences.
        ''' It does not arrange child controls; use Screen/Layout/Page for content layout.
        ''' </summary>
        Public Property SurfaceMaterial As String
            Get
                ThrowIfDisposed()
                Return _rootHost.ApplicationSurfaceMaterialKeyCore
            End Get
            Set(value As String)
                ThrowIfDisposed()
                _rootHost.ApplicationSurfaceMaterialKeyCore = value
            End Set
        End Property

        Public Function WithSurfaceMaterial(materialKey As String) As MASApplicationWindow
            SurfaceMaterial = materialKey
            Return Me
        End Function

        Public ReadOnly Property Presentation As MASApplicationWindowPresentation
            Get
                ThrowIfDisposed()
                Return _presentation
            End Get
        End Property

        Friend ReadOnly Property Layout As MASHostLayoutFacade
            Get
                ThrowIfDisposed()
                Return _rootHost.Layout
            End Get
        End Property

        Friend ReadOnly Property Page As MASHostPageFacade
            Get
                ThrowIfDisposed()
                Return _rootHost.Page
            End Get
        End Property

        Friend ReadOnly Property Screen As MASHostScreenFacade
            Get
                ThrowIfDisposed()
                Return _rootHost.Screen
            End Get
        End Property

        Public ReadOnly Property SurfaceSizePx As SKSizeI
            Get
                ThrowIfDisposed()
                Return _geometry.SurfaceSizePx
            End Get
        End Property

        Public ReadOnly Property Dpi As Single
            Get
                ThrowIfDisposed()
                Return _geometry.Dpi
            End Get
        End Property

        Public Sub Setup()
            ThrowIfDisposed()
            _rootHost.Setup()
        End Sub

        Friend Function SetContent(layout As IMASLayoutNode) As Boolean
            ThrowIfDisposed()
            Return _rootHost.Composition.SetLayout(layout)
        End Function

        Friend Function SetContentFactory(factory As Func(Of IMASLayoutNode),
                                          Optional applyImmediately As Boolean = True) As Boolean
            ThrowIfDisposed()
            Return _rootHost.Composition.SetLayoutFactory(factory, applyImmediately)
        End Function

        Friend Function RebuildContent() As Boolean
            If _disposed Then Return False
            Return _rootHost.Composition.RebuildCurrent()
        End Function

        Friend Function ClearContent() As Boolean
            If _disposed Then Return False
            Return _rootHost.Composition.Clear()
        End Function

        Public Sub Refresh()
            ThrowIfDisposed()

            _rootHost.PostToUiThreadCore(
                Sub()
                    If _disposed Then Return
                    _rootHost.RequestInvalidate()
                End Sub,
                "MASApplicationWindow.Refresh")
        End Sub

        Public Sub RefreshNow()
            ThrowIfDisposed()

            _rootHost.InvokeOnUiThreadCore(
                Sub()
                    If _disposed Then Return
                    _rootHost.InvalidateNow()
                End Sub,
                "MASApplicationWindow.RefreshNow")
        End Sub

        ''' <summary>
        ''' Friend-only application facade used by MAS-owned public gateways that must marshal
        ''' lifecycle-sensitive UI construction back to the RootHost dispatcher without exposing
        ''' the RootHost or any WinForms control to SDK consumers.
        ''' </summary>
        Friend Function InvokeOnUiThreadForInternalGateway(action As Action,
                                                          diagnosticName As String) As Boolean
            If action Is Nothing Then Return False
            ThrowIfDisposed()

            Return _rootHost.InvokeOnUiThreadCore(
                Sub()
                    If _disposed Then Return
                    action.Invoke()
                End Sub,
                diagnosticName)
        End Function

        Private Sub OwnerForm_FormClosed(sender As Object, e As FormClosedEventArgs)
            Dispose()
        End Sub

        Private Sub OwnerForm_Disposed(sender As Object, e As EventArgs)
            Dispose()
        End Sub

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

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

            Try
                If _rootHost IsNot Nothing AndAlso _rootHost.OwnerForm IsNot Nothing Then
                    RemoveHandler _rootHost.OwnerForm.FormClosed, AddressOf OwnerForm_FormClosed
                    RemoveHandler _rootHost.OwnerForm.Disposed, AddressOf OwnerForm_Disposed
                End If
            Catch masCaughtException1 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(masCaughtException1)
            End Try

            Try
                If _pages IsNot Nothing Then _pages.Dispose()
            Catch masCaughtExceptionPagesDispose As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(masCaughtExceptionPagesDispose)
            End Try

            Try
                If _presentation IsNot Nothing Then _presentation.Dispose()
            Catch masCaughtException2 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(masCaughtException2)
            End Try

            Try
                If _chrome IsNot Nothing Then _chrome.Dispose()
            Catch masCaughtException3 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(masCaughtException3)
            End Try

            Try
                If _rendering IsNot Nothing Then _rendering.Dispose()
            Catch masCaughtException4 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(masCaughtException4)
            End Try

            Try
                If _services IsNot Nothing Then _services.Dispose()
            Catch masCaughtException5 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(masCaughtException5)
            End Try

            Try
                If _settings IsNot Nothing Then _settings.Dispose()
            Catch masCaughtException6 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(masCaughtException6)
            End Try

            Try
                If _rootHost IsNot Nothing Then _rootHost.Dispose()
            Catch masCaughtException7 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(masCaughtException7)
            End Try

            RaiseEvent Disposed(Me, EventArgs.Empty)
        End Sub

    End Class

End Namespace
