﻿Option Strict On
Option Explicit On

Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Application
Imports Nexamas.UI.Components
Imports Nexamas.UI.Components.DropDownMenu
Imports Nexamas.UI.Controls
Imports Nexamas.UI.Layout

''' <summary>
''' Shared direct-host window used by the Nexamas UI Showcase mini app.
''' The current Showcase owns one borderless MASApplicationWindow shell and presents
''' every product page through MASApplicationWindow.Pages. It deliberately does not
''' own Form-per-page navigation, sample-window registries, or local product-showcase
''' dashboard windows.
''' </summary>
Friend MustInherit Class NexamasUIShowcaseWindowBase
    Inherits Form

    Private Shared ReadOnly _applicationGate As New Object()
    Private Shared _sharedApplication As MASApplication
    Private Shared _activeWindowCount As Integer

    Private _app As MASApplication
    Private _window As MASApplicationWindow
    Private _topBar As MASTopBarComponent
    Private _surfaceMaterialScope As MASApplicationSurfaceMaterialScope
    Private _loaded As Boolean
    Private _hasApplicationReservation As Boolean
    Private _layoutRequestPending As Boolean
    Private _layoutRequestDirty As Boolean
    Private _isClosingOrClosed As Boolean
    Private _sizeProfileChangedHandler As EventHandler(Of Nexamas.UI.Layout.MASSizeProfileChangedEventArgs)
    Private ReadOnly _windowSizeKind As ShowcaseWindowSizeKind

    Protected Sub New(title As String,
                      Optional windowSizeKind As ShowcaseWindowSizeKind = ShowcaseWindowSizeKind.Host)

        _windowSizeKind = windowSizeKind

        Me.Text = If(title, String.Empty)
        Me.StartPosition = FormStartPosition.CenterScreen
        ApplyShowcaseWindowPresentationContract()
        Me.FormBorderStyle = FormBorderStyle.None
        Me.DoubleBuffered = False
        Me.BackColor = Color.White
        Me.KeyPreview = True
    End Sub

    Protected Sub ApplyShowcaseWindowPresentationContract()
        Me.Size = ShowcaseConsumerContentContract.CreateInitialWindowSize(_windowSizeKind)
        Me.MinimumSize = ShowcaseConsumerContentContract.CreateMinimumWindowSize(_windowSizeKind)
    End Sub

    Protected ReadOnly Property Window As MASApplicationWindow
        Get
            Return _window
        End Get
    End Property

    Protected ReadOnly Property ApplicationFacade As MASApplication
        Get
            Return _app
        End Get
    End Property

    Protected ReadOnly Property ShowcaseTopBar As MASTopBarComponent
        Get
            Return _topBar
        End Get
    End Property

    Protected ReadOnly Property ShowcaseSurfaceMaterialScope As MASApplicationSurfaceMaterialScope
        Get
            Return _surfaceMaterialScope
        End Get
    End Property


    Protected Overrides Sub OnLoad(e As EventArgs)
        MyBase.OnLoad(e)

        _app = AcquireApplication()
        _hasApplicationReservation = True

        _window = _app.CreateWindow(
            owner:=Me,
            dockStyle:=DockStyle.Fill,
            setupImmediately:=False)

        _window.Chrome.UseSoftShadow(MASWindowShadowMode.PopupSafe)

        _topBar = _window.Shell.AttachStandardTopBar(
            title:=Me.Text,
            configureMenu:=AddressOf ConfigureTopBarMenu,
            commandHandler:=AddressOf HandleTopBarMenuCommand,
            showLogo:=False,
            attachWindow:=True)

        _surfaceMaterialScope = MASApplicationSurfaceMaterialGateway.CreateScope(_window).
            RegisterTopBar(_topBar)

        _window.Setup()

        _sizeProfileChangedHandler = AddressOf HandleRuntimeSizeProfileChanged
        AddHandler _window.Sizing.CurrentChanged, _sizeProfileChangedHandler

        ShowcaseHostDiagnostics.RecordWindowOpened(Me.Text, IsShowcaseLauncherWindow, _activeWindowCount)

        _window.Controls.PerformUpdate(
            Sub(controls As MASApplicationWindowControls)
                RunShowcaseStartupStage("CreateSampleControls", AddressOf CreateSampleControls)
                _loaded = True
                RunShowcaseStartupStage("LayoutSampleControls", AddressOf LayoutSampleControls)
            End Sub)
    End Sub

    Private Shared Sub RunShowcaseStartupStage(stageName As String, action As Action)
        If action Is Nothing Then Throw New ArgumentNullException(NameOf(action))

        Try
            action.Invoke()
        Catch ex As Exception
            Throw New InvalidOperationException(
                "Nexamas UI Showcase startup stage failed: " & If(stageName, "Unknown"),
                ex)
        End Try
    End Sub

    Protected Overrides Sub OnShown(e As EventArgs)
        MyBase.OnShown(e)
        RequestShowcaseInputActivation()
    End Sub

    Protected Overrides Sub OnActivated(e As EventArgs)
        MyBase.OnActivated(e)
        RequestShowcaseInputActivation()
    End Sub

    Private Sub RequestShowcaseInputActivation()
        If _isClosingOrClosed OrElse _window Is Nothing OrElse IsDisposed Then Return

        If Not IsHandleCreated Then
            SyncShowcaseInputFromCurrentCursor()
            Return
        End If

        Try
            BeginInvoke(CType(AddressOf SyncShowcaseInputFromCurrentCursor, MethodInvoker))
        Catch nexamasUICaughtExceptionInputActivation As Exception
            System.Diagnostics.Debug.WriteLine("Showcase.InputActivation.BeginInvoke: " & nexamasUICaughtExceptionInputActivation.Message)
            SyncShowcaseInputFromCurrentCursor()
        End Try
    End Sub

    Private Sub SyncShowcaseInputFromCurrentCursor()
        If _isClosingOrClosed OrElse _window Is Nothing OrElse _window.IsDisposed OrElse IsDisposed Then Return

        Try
            Me.Activate()
            Window.Input.SyncPointerFromCursor()
            _window.Refresh()
        Catch nexamasUICaughtExceptionInputActivationSync As Exception
            System.Diagnostics.Debug.WriteLine("Showcase.InputActivation.Sync: " & nexamasUICaughtExceptionInputActivationSync.Message)
        End Try
    End Sub

    Protected Overrides Sub OnResize(e As EventArgs)
        MyBase.OnResize(e)
        RequestSampleLayout()
    End Sub

    Private Sub RequestSampleLayout()
        If _isClosingOrClosed OrElse Not _loaded OrElse _window Is Nothing Then Return

        _layoutRequestDirty = True

        If _layoutRequestPending Then Return

        If Not IsHandleCreated OrElse IsDisposed Then
            FlushPendingSampleLayout()
            Return
        End If

        _layoutRequestPending = True

        Try
            BeginInvoke(CType(AddressOf FlushPendingSampleLayout, MethodInvoker))
        Catch nexamasUICaughtExceptionResizeLayout As Exception
            System.Diagnostics.Debug.WriteLine("Showcase.ResizeLayout.BeginInvoke: " & nexamasUICaughtExceptionResizeLayout.Message)
            _layoutRequestPending = False
            FlushPendingSampleLayout()
        End Try
    End Sub

    Private Sub FlushPendingSampleLayout()
        _layoutRequestPending = False

        If Not _layoutRequestDirty Then Return
        _layoutRequestDirty = False

        If _isClosingOrClosed OrElse Not _loaded OrElse _window Is Nothing OrElse _window.IsDisposed Then Return

        _window.Controls.PerformUpdate(
            Sub(controls As MASApplicationWindowControls)
                LayoutSampleControls()
            End Sub)

        If _layoutRequestDirty Then
            RequestSampleLayout()
        End If
    End Sub

    Private Sub HandleRuntimeSizeProfileChanged(sender As Object, e As Nexamas.UI.Layout.MASSizeProfileChangedEventArgs)
        If _isClosingOrClosed Then Return
        RequestSampleLayout()
    End Sub

    Protected Overrides Sub OnKeyDown(e As KeyEventArgs)
        ' Escape is reserved for Nexamas.UI transient surfaces first.
        ' The Showcase host must not close the whole Form while an official
        ' FloatRuntime popup such as DatePicker, TimePicker, menu, drawer, or
        ' popover has the current interaction. Native SK input remains the owner
        ' of Escape routing; the showcase is only a consumer of MAS elements.
        MyBase.OnKeyDown(e)
    End Sub

    Protected Overrides Sub OnFormClosing(e As FormClosingEventArgs)
        _isClosingOrClosed = True
        _loaded = False
        _layoutRequestPending = False
        _layoutRequestDirty = False

        If _window IsNot Nothing AndAlso Not _window.IsDisposed Then
            _window.Services.Toasts.ClearAll()
            _window.Services.Tooltips.Clear()
        End If

        MyBase.OnFormClosing(e)
    End Sub

    Protected Overrides Sub OnFormClosed(e As FormClosedEventArgs)
        If _window IsNot Nothing AndAlso _sizeProfileChangedHandler IsNot Nothing Then
            RemoveHandler _window.Sizing.CurrentChanged, _sizeProfileChangedHandler
            _sizeProfileChangedHandler = Nothing
        End If

        If _window IsNot Nothing AndAlso Not _window.IsDisposed Then
            _window.Shell.DetachTopBar()
        End If

        If _topBar IsNot Nothing Then
            _topBar.Dispose()
            _topBar = Nothing
        End If

        _surfaceMaterialScope = Nothing

        If _window IsNot Nothing Then
            _window.Dispose()
            _window = Nothing
        End If

        ReleaseApplicationReservation()
        ShowcaseHostDiagnostics.RecordWindowClosed(Me.Text, IsShowcaseLauncherWindow, _activeWindowCount)
        MyBase.OnFormClosed(e)
    End Sub

    Private Shared Function AcquireApplication() As MASApplication
        SyncLock _applicationGate
            If _sharedApplication Is Nothing OrElse _sharedApplication.IsDisposed Then
                _sharedApplication = MASApplication.Create()
                ApplyPreferredTheme(_sharedApplication)
            End If

            _activeWindowCount += 1
            Return _sharedApplication
        End SyncLock
    End Function

    Private Sub ReleaseApplicationReservation()
        If Not _hasApplicationReservation Then Return
        _hasApplicationReservation = False

        Dim appToDispose As MASApplication = Nothing

        SyncLock _applicationGate
            If _activeWindowCount > 0 Then
                _activeWindowCount -= 1
            End If

            If _activeWindowCount = 0 Then
                appToDispose = _sharedApplication
                _sharedApplication = Nothing
            End If
        End SyncLock

        If appToDispose IsNot Nothing Then
            appToDispose.Dispose()
        End If

        _app = Nothing
    End Sub

    Protected Overridable ReadOnly Property IsShowcaseLauncherWindow As Boolean
        Get
            Return False
        End Get
    End Property

    Protected MustOverride Sub CreateSampleControls()
    Protected MustOverride Sub LayoutSampleControls()

    Protected Overridable Sub ConfigureTopBarMenu(builder As MASDropDownMenuBuilder)
        ConfigureThemeTopBarMenu(builder)
        builder.AddAction("About this Showcase", "showcase.about")
        builder.AddSeparator()
        builder.AddAction("Minimize", "window.minimize")
        builder.AddAction("Maximize / Restore", "window.toggle_max_restore")
        builder.AddDangerAction("Close", "window.close")
    End Sub

    Protected Sub ConfigureThemeTopBarMenu(builder As MASDropDownMenuBuilder)
        If builder Is Nothing Then Return

        Dim themes As IReadOnlyList(Of IMASAppTheme) = Nothing
        Dim currentThemeId As String = String.Empty

        If Window IsNot Nothing Then
            themes = Window.Theme.GetAll()
            currentThemeId = Window.Theme.CurrentId
        End If

        If themes Is Nothing OrElse themes.Count = 0 Then Return

        builder.AddSubMenu(
            text:="Theme",
            configure:=Sub(themeBuilder As MASDropDownMenuBuilder)
                           For Each theme As IMASAppTheme In themes
                               If theme Is Nothing Then Continue For

                               themeBuilder.AddCheckedAction(
                                   text:=theme.DisplayName,
                                   commandId:="showcase.theme.use." & theme.Id,
                                   isChecked:=String.Equals(theme.Id, currentThemeId, StringComparison.OrdinalIgnoreCase))
                           Next
                       End Sub)
        builder.AddSeparator()
    End Sub

    Protected Sub ConfigureSurfaceMaterialTopBarMenu(builder As MASDropDownMenuBuilder)
        If builder Is Nothing Then Return

        Dim options As MASApplicationSurfaceMaterialOption() = MASApplicationSurfaceMaterialGateway.GetSelectableMaterials()
        If options Is Nothing OrElse options.Length = 0 Then Return

        Dim currentMaterialKey As String = String.Empty
        If _surfaceMaterialScope IsNot Nothing Then
            currentMaterialKey = _surfaceMaterialScope.CurrentMaterialKey
        End If

        builder.AddSubMenu(
            text:="Surface material",
            configure:=Sub(materialBuilder As MASDropDownMenuBuilder)
                           For Each optionItem As MASApplicationSurfaceMaterialOption In options
                               If optionItem Is Nothing Then Continue For

                               materialBuilder.AddCheckedAction(
                                   text:=optionItem.DisplayName,
                                   commandId:="showcase.material.use." & optionItem.Key,
                                   isChecked:=String.Equals(optionItem.Key, currentMaterialKey, StringComparison.OrdinalIgnoreCase))
                           Next
                       End Sub)
        builder.AddSeparator()
    End Sub

    Protected Sub ConfigureSizeProfileTopBarMenu(builder As MASDropDownMenuBuilder)
        If builder Is Nothing OrElse Window Is Nothing Then Return

        Dim profiles As IReadOnlyList(Of MASSizeProfile) = Window.Sizing.GetAll()
        If profiles Is Nothing OrElse profiles.Count = 0 Then Return

        Dim currentKind As MASSizeProfileKind = Window.Sizing.CurrentKind

        builder.AddSubMenu(
            text:="Density / size",
            configure:=Sub(sizeBuilder As MASDropDownMenuBuilder)
                           For Each profile As MASSizeProfile In profiles
                               If profile Is Nothing Then Continue For

                               sizeBuilder.AddCheckedAction(
                                   text:=profile.Kind.ToString(),
                                   commandId:="showcase.size.use." & profile.Kind.ToString(),
                                   isChecked:=(profile.Kind = currentKind))
                           Next
                       End Sub)
        builder.AddSeparator()
    End Sub

    Private Sub HandleTopBarMenuCommand(commandId As String)
        OnTopBarMenuCommand(If(commandId, String.Empty))
    End Sub

    Protected Overridable Sub OnTopBarMenuCommand(commandId As String)
        Dim normalizedCommandId As String = If(commandId, String.Empty)

        If HandleThemeTopBarCommand(normalizedCommandId) Then Return
        If HandleSurfaceMaterialTopBarCommand(normalizedCommandId) Then Return
        If HandleSizeProfileTopBarCommand(normalizedCommandId) Then Return

        Select Case normalizedCommandId
            Case "showcase.about"
                If Window IsNot Nothing Then
                    Window.Services.Toasts.Info("This Showcase is hosted by one MASApplicationWindow.PageHost route. Legacy Form-per-page sample routes are intentionally not present.", "Nexamas UI Showcase")
                End If
        End Select
    End Sub

    Private Function HandleThemeTopBarCommand(commandId As String) As Boolean
        Const Prefix As String = "showcase.theme.use."

        Dim normalizedCommandId As String = If(commandId, String.Empty).Trim()
        If normalizedCommandId.Length = 0 OrElse Not normalizedCommandId.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase) Then Return False

        Dim themeId As String = normalizedCommandId.Substring(Prefix.Length).Trim()
        Return TryApplyTheme(themeId, persistSelection:=True, showToast:=True)
    End Function

    Private Function HandleSurfaceMaterialTopBarCommand(commandId As String) As Boolean
        Const Prefix As String = "showcase.material.use."

        Dim normalizedCommandId As String = If(commandId, String.Empty).Trim()
        If normalizedCommandId.Length = 0 OrElse Not normalizedCommandId.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase) Then Return False

        Dim materialKey As String = normalizedCommandId.Substring(Prefix.Length).Trim()
        Return TryApplySurfaceMaterial(materialKey, showToast:=True)
    End Function

    Private Function HandleSizeProfileTopBarCommand(commandId As String) As Boolean
        Const Prefix As String = "showcase.size.use."

        Dim normalizedCommandId As String = If(commandId, String.Empty).Trim()
        If normalizedCommandId.Length = 0 OrElse Not normalizedCommandId.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase) Then Return False

        Dim kindName As String = normalizedCommandId.Substring(Prefix.Length).Trim()
        Dim sizeKind As MASSizeProfileKind
        If Not [Enum].TryParse(Of MASSizeProfileKind)(kindName, True, sizeKind) Then Return False
        If Window Is Nothing OrElse Not Window.Sizing.TryUse(sizeKind) Then Return False

        Window.Sizing.Refresh()
        RefreshTopBarMenuModel()
        Window.Refresh()
        Window.Services.Toasts.Success("Size profile switched to " & sizeKind.ToString() & ".", "Nexamas UI Showcase")
        Return True
    End Function

    Private Function TryApplySurfaceMaterial(materialKey As String,
                                             showToast As Boolean) As Boolean
        If Window Is Nothing OrElse String.IsNullOrWhiteSpace(materialKey) Then Return False

        If _surfaceMaterialScope Is Nothing Then
            _surfaceMaterialScope = MASApplicationSurfaceMaterialGateway.CreateScope(Window)
            If _topBar IsNot Nothing Then _surfaceMaterialScope.RegisterTopBar(_topBar)
        End If

        If Not ApplyShowcaseTopBarSurfaceMaterial(materialKey) Then Return False
        RefreshTopBarMenuModel()
        Window.Refresh()

        If showToast Then
            Window.Services.Toasts.Success("Surface material switched to " & materialKey & ".", "Nexamas UI Showcase")
        End If

        Return True
    End Function

    Private Function ApplyShowcaseTopBarSurfaceMaterial(materialKey As String) As Boolean
        If String.IsNullOrWhiteSpace(materialKey) OrElse _surfaceMaterialScope Is Nothing Then Return False

        _surfaceMaterialScope.Apply(materialKey)
        Return True
    End Function

    Private Function TryApplyTheme(themeId As String,
                                   persistSelection As Boolean,
                                   showToast As Boolean) As Boolean
        If Window Is Nothing OrElse String.IsNullOrWhiteSpace(themeId) Then Return False

        If Not Window.Theme.TryUseGlobally(themeId) Then Return False

        If persistSelection Then
            ShowcaseThemePreferenceStore.TrySaveThemeId(themeId)
        End If

        RefreshTopBarMenuModel()
        Window.Refresh()

        If showToast Then
            Dim displayName As String = Window.Theme.CurrentDisplayName
            If String.IsNullOrWhiteSpace(displayName) Then displayName = themeId
            Window.Services.Toasts.Success("Theme switched to " & displayName & ". This choice will be restored next time the showcase starts.", "Nexamas UI Showcase")
        End If

        Return True
    End Function

    Private Shared Sub ApplyPreferredTheme(app As MASApplication)
        If app Is Nothing Then Return

        Dim preferredThemeId As String = ShowcaseThemePreferenceStore.TryLoadThemeId()
        If String.IsNullOrWhiteSpace(preferredThemeId) Then Return

        Try
            app.Theme.TryUse(preferredThemeId)
        Catch ex As Exception
            System.Diagnostics.Debug.WriteLine("NexamasUIShowcaseWindowBase.ApplyPreferredTheme: " & ex.Message)
        End Try
    End Sub

    Protected Sub RefreshShowcaseTopBarMenuModel()
        RefreshTopBarMenuModel()
    End Sub

    Private Sub RefreshTopBarMenuModel()
        If _topBar Is Nothing Then Return

        Try
            _topBar.WithMenu(AddressOf ConfigureTopBarMenu)
        Catch ex As Exception
            System.Diagnostics.Debug.WriteLine("NexamasUIShowcaseWindowBase.RefreshTopBarMenuModel: " & ex.Message)
        End Try
    End Sub

    Protected Sub ShowShowcaseNexamasUIError(message As String, ex As Exception)
        Try
            If Window IsNot Nothing AndAlso Not Window.IsDisposed AndAlso Window.Services IsNot Nothing Then
                Window.Services.Dialogs.Error(
                    message:=If(message, "The Showcase page could not be opened."),
                    title:="Nexamas UI Showcase",
                    detail:=If(ex Is Nothing, String.Empty, ex.Message),
                    ownerKey:="Nexamas.UI.Showcase.Error")
            Else
                System.Diagnostics.Debug.WriteLine("Nexamas UI Showcase dialog unavailable: " & If(ex Is Nothing, message, ex.Message))
            End If
        Catch dialogException As Exception
            System.Diagnostics.Debug.WriteLine("Nexamas UI Showcase dialog boundary: " & dialogException.Message)
        End Try
    End Sub

    Friend Shared Function TryReportUnhandledNexamasUIError(message As String, ex As Exception) As Boolean
        Try
            For Each openForm As Form In System.Windows.Forms.Application.OpenForms
                Dim showcase As NexamasUIShowcaseWindowBase = TryCast(openForm, NexamasUIShowcaseWindowBase)
                If showcase Is Nothing OrElse showcase.IsDisposed Then Continue For
                If showcase.Window Is Nothing OrElse showcase.Window.IsDisposed Then Continue For

                showcase.ShowShowcaseNexamasUIError(If(message, "The Nexamas UI Showcase caught an unexpected error."), ex)
                Return True
            Next
        Catch dialogException As Exception
            System.Diagnostics.Debug.WriteLine("Nexamas UI Showcase global dialog boundary: " & dialogException.Message)
        End Try

        System.Diagnostics.Debug.WriteLine("Nexamas UI Showcase unhandled boundary: " & If(ex Is Nothing, If(message, String.Empty), ex.Message))
        Return False
    End Function

End Class
