Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports Nexamas.UI.Controls
Imports Nexamas.UI.Host
Imports Nexamas.UI.Layout
Imports Nexamas.UI.Theming
Imports SkiaSharp

Namespace Nexamas.UI.Application


    ''' <summary>
    ''' Retained Application layout session for a whole-window page recipe.
    ''' It rebuilds the intent tree on resize/profile changes, but repaints only when the produced slots differ.
    ''' </summary>
    Friend NotInheritable Class MASApplicationControlsLayoutSession
        Private ReadOnly _rootHost As MASSkiaRootHost
        Private ReadOnly _controls As MASApplicationWindowControls
        Private ReadOnly _buildAction As Action(Of MASApplicationLayoutPageBuilder)
        Private ReadOnly _fixedSizeProfile As MASSizeProfile
        Private ReadOnly _engine As New MASLayoutEngine()
        Private _disposed As Boolean
        Private _isApplying As Boolean

        Friend Sub New(rootHost As MASSkiaRootHost,
                       controls As MASApplicationWindowControls,
                       buildAction As Action(Of MASApplicationLayoutPageBuilder),
                       fixedSizeProfile As MASSizeProfile)
            If rootHost Is Nothing Then Throw New ArgumentNullException(NameOf(rootHost))
            If controls Is Nothing Then Throw New ArgumentNullException(NameOf(controls))
            If buildAction Is Nothing Then Throw New ArgumentNullException(NameOf(buildAction))

            _rootHost = rootHost
            _controls = controls
            _buildAction = buildAction
            _fixedSizeProfile = fixedSizeProfile

            AddHandler _rootHost.SurfaceMeasured, AddressOf RootHost_SurfaceMeasured
            AddHandler _rootHost.SizeLayoutRefreshRequested, AddressOf RootHost_SizeLayoutRefreshRequested
        End Sub

        Friend Function ApplyNow() As Boolean
            Return ApplyCore(throwOnError:=True, refreshReason:="InitialApply")
        End Function

        Friend Sub Dispose()
            If _disposed Then Return
            _disposed = True
            RemoveHandler _rootHost.SurfaceMeasured, AddressOf RootHost_SurfaceMeasured
            RemoveHandler _rootHost.SizeLayoutRefreshRequested, AddressOf RootHost_SizeLayoutRefreshRequested
        End Sub

        Private Sub RootHost_SurfaceMeasured(sizePx As SKSizeI)
            If _disposed Then Return
            ApplyCore(throwOnError:=False, refreshReason:="SurfaceMeasured")
        End Sub

        Private Sub RootHost_SizeLayoutRefreshRequested(reason As String)
            If _disposed Then Return
            ApplyCore(throwOnError:=False, refreshReason:=NormalizeRefreshReason(reason))
        End Sub

        Private Shared Function NormalizeRefreshReason(reason As String) As String
            If String.IsNullOrWhiteSpace(reason) Then Return "SizeLayoutRefreshRequested"
            Return reason.Trim()
        End Function

        Private Function ResolveSizeProfile() As MASSizeProfile
            If _fixedSizeProfile IsNot Nothing Then Return MASSizeProfile.Normalize(_fixedSizeProfile)
            Return MASRuntimeSizeProfileService.Current
        End Function

        Private Function ApplyCore(throwOnError As Boolean, refreshReason As String) As Boolean
            If _disposed OrElse _isApplying OrElse _rootHost.IsDisposed Then Return False

            _isApplying = True

            Try
                _rootHost.EnsureThemeContextForComposition()

                Dim viewportPx As SKRect = _rootHost.GetSurfaceViewportPx()
                Dim boundsLogical As SKRect = _rootHost.PxToLogical(viewportPx)
                If boundsLogical.IsEmpty OrElse boundsLogical.Width <= 1.0F OrElse boundsLogical.Height <= 1.0F Then Return False

                Dim page As New MASApplicationLayoutPageBuilder()
                _controls.BeginRetainedLayoutBuildAdmissionInternal()
                Try
                    _buildAction.Invoke(page)
                Finally
                    _controls.EndRetainedLayoutBuildAdmissionInternal()
                End Try

                Dim hostedControls As New List(Of MASControlBase)()
                page.CollectControls(hostedControls)

                Dim diagnostics As New MASSizeLayoutDiagnosticBag()
                Dim themeContext As MASThemeContext = _rootHost.ThemeHostCore.TryGetContext()
                Dim sizeProfile As MASSizeProfile = ResolveSizeProfile()
                Dim integration As MASSizeLayoutIntegrationContext = MASSizeLayoutIntegrationGateway.Create(themeContext, sizeProfile, _rootHost.GetDpi())
                Dim layoutContext As New MASLayoutContext(boundsLogical, integration, diagnostics)
                Dim plan As MASLayoutPlan = _engine.Arrange(page.BuildNode(sizeProfile), layoutContext)
                If diagnostics.HasErrors Then
                    _controls.RecordLayoutRefreshDiagnosticsFailureInternal("LayoutPage", refreshReason, "Arrange", diagnostics)
                    Return False
                End If

                Dim geometryChanged As Boolean = _controls.TryApplySizeLayoutPlanInternal(plan, diagnostics)
                If diagnostics.HasErrors Then
                    _controls.RecordLayoutRefreshDiagnosticsFailureInternal("LayoutPage", refreshReason, "Apply", diagnostics)
                    Return False
                End If
                Dim ownershipChanged As Boolean = _controls.ReconcileLayoutHostedControlsInternal(hostedControls)
                If geometryChanged OrElse ownershipChanged Then _rootHost.RequestInvalidate()
                _controls.ClearLayoutRefreshFailureInternal("LayoutPage")
                Return True
            Catch ex As Exception
                _controls.RecordLayoutRefreshExceptionFailureInternal("LayoutPage", refreshReason, "BuildOrApply", ex)

                If throwOnError Then
                    Throw New InvalidOperationException("MASApplicationWindow.Controls.LayoutPage failed while building or applying the official MASLayout plan.", ex)
                End If

                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(ex, "Application.Controls.LayoutPage.ResizeApply")
                Return False
            Finally
                _isApplying = False
            End Try
        End Function
    End Class

End Namespace
