Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.Drawing
Imports System.Windows.Forms
Imports Nexamas.UI.Layout

Namespace Nexamas.UI.Application

    ''' <summary>
    ''' Official presentation facade for one lightweight layered MAS scene.
    ''' It shares the MAS window presentation profiles, planner, and runtime store used by MASApplicationWindow,
    ''' but does not arrange child controls; composition layout remains owned by MASComposition/MASLayout.
    ''' </summary>
    Friend NotInheritable Class MASApplicationLayeredScenePresentation
        Implements IDisposable

        Private ReadOnly _owner As Form
        Private ReadOnly _invalidateLayout As Action
        Private _currentProfile As MASWindowPresentationProfile = MASWindowPresentationProfile.Utility
        Private _currentOptions As MASWindowPresentationOptions
        Private _eventsAttached As Boolean
        Private _disposed As Boolean

        Friend Sub New(owner As Form,
                       invalidateLayout As Action)
            If owner Is Nothing Then Throw New ArgumentNullException(NameOf(owner))
            _owner = owner
            _invalidateLayout = invalidateLayout
        End Sub

        Friend ReadOnly Property CurrentProfile As MASWindowPresentationProfile
            Get
                Return MASWindowPresentationProfile.Normalize(_currentProfile)
            End Get
        End Property

        Friend ReadOnly Property CurrentProfileKind As MASWindowPresentationProfileKind
            Get
                Return CurrentProfile.Kind
            End Get
        End Property

        Friend Function GetAllProfileKinds() As IReadOnlyList(Of MASWindowPresentationProfileKind)
            Return Array.AsReadOnly(New MASWindowPresentationProfileKind() {
                MASWindowPresentationProfileKind.Utility,
                MASWindowPresentationProfileKind.Compact,
                MASWindowPresentationProfileKind.Standard,
                MASWindowPresentationProfileKind.Spacious,
                MASWindowPresentationProfileKind.Productive,
                MASWindowPresentationProfileKind.Immersive
            })
        End Function

        Friend Function GetAllProfiles() As IReadOnlyList(Of MASWindowPresentationProfile)
            Return Array.AsReadOnly(New MASWindowPresentationProfile() {
                MASWindowPresentationProfile.Utility,
                MASWindowPresentationProfile.Compact,
                MASWindowPresentationProfile.Standard,
                MASWindowPresentationProfile.Spacious,
                MASWindowPresentationProfile.Productive,
                MASWindowPresentationProfile.Immersive
            })
        End Function

        ''' <summary>
        ''' Applies a small utility presentation for lightweight layered scenes such as startup/preflight windows.
        ''' </summary>
        Friend Function UseUtility() As MASApplicationLayeredScenePresentation
            Return ApplyCore(MASWindowPresentationProfile.FromKind(MASWindowPresentationProfileKind.Utility), MASWindowPresentationOptions.OneShot())
        End Function

        Friend Function UseCompact() As MASApplicationLayeredScenePresentation
            Return ApplyCore(MASWindowPresentationProfile.FromKind(MASWindowPresentationProfileKind.Compact), MASWindowPresentationOptions.OneShot())
        End Function

        Friend Function UseStandard() As MASApplicationLayeredScenePresentation
            Return ApplyCore(MASWindowPresentationProfile.FromKind(MASWindowPresentationProfileKind.Standard), MASWindowPresentationOptions.OneShot())
        End Function

        Friend Function UseUserPreferences(persistenceKey As String,
                                           Optional profileKind As MASWindowPresentationProfileKind = MASWindowPresentationProfileKind.Utility) As MASApplicationLayeredScenePresentation
            Return ApplyCore(MASWindowPresentationProfile.FromKind(profileKind), MASWindowPresentationOptions.UserPreferences(persistenceKey))
        End Function

        Friend Function Apply(profileKind As MASWindowPresentationProfileKind) As MASApplicationLayeredScenePresentation
            Return ApplyCore(MASWindowPresentationProfile.FromKind(profileKind), MASWindowPresentationOptions.OneShot())
        End Function

        Friend Function Apply(profileKind As MASWindowPresentationProfileKind,
                              startupPlacement As MASWindowStartupPlacement) As MASApplicationLayeredScenePresentation
            Return ApplyCore(MASWindowPresentationProfile.FromKind(profileKind), MASWindowPresentationOptions.OneShot(startupPlacement))
        End Function

        Friend Function ApplyCore(profile As MASWindowPresentationProfile,
                                  Optional options As MASWindowPresentationOptions = Nothing) As MASApplicationLayeredScenePresentation
            ThrowIfDisposed()

            Dim safeProfile As MASWindowPresentationProfile = MASWindowPresentationProfile.Normalize(profile)
            Dim safeOptions As MASWindowPresentationOptions = MASWindowPresentationOptions.Normalize(options, safeProfile)
            Dim storedState As MASWindowPresentationState = Nothing

            If safeOptions.RestoreUserChoice AndAlso Not String.IsNullOrWhiteSpace(safeOptions.PersistenceKey) Then
                storedState = MASWindowPresentationStore.Load(safeOptions.PersistenceKey)
                If safeOptions.RestoreProfileChoice AndAlso storedState IsNot Nothing Then
                    safeProfile = MASWindowPresentationProfile.FromKind(storedState.ProfileKind)
                    safeOptions = MASWindowPresentationOptions.Normalize(safeOptions, safeProfile)
                End If
            End If

            Dim plan As MASWindowPresentationState = MASWindowPresentationPlanner.Plan(_owner, safeProfile, safeOptions, storedState)
            ApplyPlan(plan)

            _currentProfile = safeProfile
            _currentOptions = safeOptions

            If safeOptions.SaveUserChoice AndAlso Not String.IsNullOrWhiteSpace(safeOptions.PersistenceKey) Then
                AttachEvents()
            Else
                DetachEvents()
            End If

            If safeOptions.ApplySizeProfile Then
                MASRuntimeSizeProfileService.TryUse(safeOptions.SizeProfileKind)
            End If

            RefreshLayeredScene()
            Return Me
        End Function

        Friend Function ApplyAndSave(profileKind As MASWindowPresentationProfileKind,
                                     persistenceKey As String) As MASApplicationLayeredScenePresentation
            Dim options As MASWindowPresentationOptions = MASWindowPresentationOptions.UserPreferences(persistenceKey)
            options.RestoreProfileChoice = False
            ApplyCore(MASWindowPresentationProfile.FromKind(profileKind), options)
            SaveCurrent(persistenceKey)
            Return Me
        End Function

        Friend Function SaveCurrent() As Boolean
            ThrowIfDisposed()
            If _currentOptions Is Nothing OrElse String.IsNullOrWhiteSpace(_currentOptions.PersistenceKey) Then Return False
            Return SaveCurrent(_currentOptions.PersistenceKey)
        End Function

        Friend Function SaveCurrent(persistenceKey As String) As Boolean
            ThrowIfDisposed()
            If String.IsNullOrWhiteSpace(persistenceKey) Then Return False

            Dim state As MASWindowPresentationState = MASWindowPresentationPlanner.Capture(_owner, CurrentProfile)
            Return MASWindowPresentationStore.Save(persistenceKey, state)
        End Function

        Private Sub ApplyPlan(plan As MASWindowPresentationState)
            If plan Is Nothing Then Return

            Dim oldState As FormWindowState = _owner.WindowState
            If oldState <> FormWindowState.Normal Then _owner.WindowState = FormWindowState.Normal

            If plan.Bounds.Width > 0 AndAlso plan.Bounds.Height > 0 Then
                _owner.StartPosition = FormStartPosition.Manual
                _owner.Bounds = plan.Bounds
                _owner.MinimumSize = New Size(MASWindowPresentationSizeTokens.MinimumVisibleWidth, MASWindowPresentationSizeTokens.MinimumVisibleHeight)
            End If

            If plan.WindowState = FormWindowState.Maximized Then
                _owner.WindowState = FormWindowState.Maximized
            ElseIf oldState <> FormWindowState.Minimized Then
                _owner.WindowState = FormWindowState.Normal
            End If
        End Sub

        Private Sub AttachEvents()
            If _eventsAttached Then Return
            AddHandler _owner.FormClosing, AddressOf OwnerForm_FormClosing
            AddHandler _owner.ResizeEnd, AddressOf OwnerForm_ResizeEnd
            AddHandler _owner.Disposed, AddressOf OwnerForm_Disposed
            _eventsAttached = True
        End Sub

        Private Sub DetachEvents()
            If Not _eventsAttached Then Return
            RemoveHandler _owner.FormClosing, AddressOf OwnerForm_FormClosing
            RemoveHandler _owner.ResizeEnd, AddressOf OwnerForm_ResizeEnd
            RemoveHandler _owner.Disposed, AddressOf OwnerForm_Disposed
            _eventsAttached = False
        End Sub

        Private Sub OwnerForm_FormClosing(sender As Object, e As FormClosingEventArgs)
            SaveCurrent()
        End Sub

        Private Sub OwnerForm_ResizeEnd(sender As Object, e As EventArgs)
            SaveCurrent()
        End Sub

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

        Private Sub RefreshLayeredScene()
            Try
                If _invalidateLayout IsNot Nothing Then _invalidateLayout.Invoke()
            Catch ex As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(ex, "Application.LayeredScene.Presentation.Refresh")
            End Try
        End Sub

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

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

    End Class

End Namespace
