Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports Nexamas.UI.Theming

Namespace Nexamas.UI.Application

    ''' <summary>
    ''' Public event data for MAS application theme changes exposed by MASApplication.Theme.
    ''' </summary>
    Public NotInheritable Class NexamasUIChangedEventArgs
        Inherits EventArgs

        Friend Sub New(oldTheme As IMASAppTheme, newTheme As IMASAppTheme)
            Me.OldTheme = oldTheme
            Me.NewTheme = newTheme
        End Sub

        Public ReadOnly Property OldTheme As IMASAppTheme
        Public ReadOnly Property NewTheme As IMASAppTheme

    End Class

    ''' <summary>
    ''' Beginner-facing application-level theme facade.
    ''' This is the official global entry point for listing, registering, and selecting MAS themes.
    ''' Window repaint/snapshot refresh remains owned by each MASApplicationWindow/ThemeHost.
    ''' </summary>
    Public NotInheritable Class MASApplicationTheme

        Private Shared ReadOnly _syncRoot As New Object()

        Private Shared ReadOnly _currentChangedAdapters As New Dictionary(
            Of Action(Of IMASAppTheme),
            Action(Of IMASAppTheme)
        )()

        Private Shared ReadOnly _currentThemeChangedAdapters As New Dictionary(
            Of EventHandler(Of NexamasUIChangedEventArgs),
            EventHandler(Of ThemeManager.CurrentThemeChangedEventArgs)
        )()

        Private Shared ReadOnly _themeRegisteredAdapters As New Dictionary(
            Of Action(Of IMASAppTheme),
            Action(Of IMASAppTheme)
        )()

        Friend Sub New()
        End Sub

#Region "Events"

        ''' <summary>
        ''' Raised when the current MAS application theme changes through MASApplication.Theme or any window theme facade.
        ''' </summary>
        Public Custom Event CurrentChanged As Action(Of IMASAppTheme)

            AddHandler(value As Action(Of IMASAppTheme))

                If value Is Nothing Then Return

                SyncLock _syncRoot

                    If _currentChangedAdapters.ContainsKey(value) Then Return

                    Dim adapter As Action(Of IMASAppTheme) =
                        Sub(newTheme As IMASAppTheme)
                            value.Invoke(newTheme)
                        End Sub

                    _currentChangedAdapters(value) = adapter
                    ThemeManager.AddPublicCurrentChangedHandler(adapter)

                End SyncLock

            End AddHandler

            RemoveHandler(value As Action(Of IMASAppTheme))

                If value Is Nothing Then Return

                SyncLock _syncRoot

                    Dim adapter As Action(Of IMASAppTheme) = Nothing

                    If Not _currentChangedAdapters.TryGetValue(value, adapter) Then Return

                    ThemeManager.RemovePublicCurrentChangedHandler(adapter)
                    _currentChangedAdapters.Remove(value)

                End SyncLock

            End RemoveHandler

            RaiseEvent(newTheme As IMASAppTheme)
                ' Forwarded by ThemeManager.
            End RaiseEvent

        End Event

        ''' <summary>
        ''' Raised with old/new theme data when the current MAS application theme changes.
        ''' </summary>
        Public Custom Event CurrentThemeChanged As EventHandler(Of NexamasUIChangedEventArgs)

            AddHandler(value As EventHandler(Of NexamasUIChangedEventArgs))

                If value Is Nothing Then Return

                SyncLock _syncRoot

                    If _currentThemeChangedAdapters.ContainsKey(value) Then Return

                    Dim adapter As EventHandler(Of ThemeManager.CurrentThemeChangedEventArgs) =
                        Sub(sender As Object, e As ThemeManager.CurrentThemeChangedEventArgs)

                            Dim args As New NexamasUIChangedEventArgs(
                                oldTheme:=If(e Is Nothing, Nothing, e.OldTheme),
                                newTheme:=If(e Is Nothing, Nothing, e.NewTheme)
                            )

                            value.Invoke(sender, args)

                        End Sub

                    _currentThemeChangedAdapters(value) = adapter
                    ThemeManager.AddPublicCurrentThemeChangedHandler(adapter)

                End SyncLock

            End AddHandler

            RemoveHandler(value As EventHandler(Of NexamasUIChangedEventArgs))

                If value Is Nothing Then Return

                SyncLock _syncRoot

                    Dim adapter As EventHandler(Of ThemeManager.CurrentThemeChangedEventArgs) = Nothing

                    If Not _currentThemeChangedAdapters.TryGetValue(value, adapter) Then Return

                    ThemeManager.RemovePublicCurrentThemeChangedHandler(adapter)
                    _currentThemeChangedAdapters.Remove(value)

                End SyncLock

            End RemoveHandler

            RaiseEvent(sender As Object, e As NexamasUIChangedEventArgs)
                ' Forwarded by ThemeManager.
            End RaiseEvent

        End Event

        ''' <summary>
        ''' Internal notification raised only when Nexamas UI-owned theme registration occurs.
        ''' Public custom-theme registration is intentionally closed before v1; consumers select registered themes by id.
        ''' </summary>
        Friend Custom Event ThemeRegistered As Action(Of IMASAppTheme)

            AddHandler(value As Action(Of IMASAppTheme))

                If value Is Nothing Then Return

                SyncLock _syncRoot

                    If _themeRegisteredAdapters.ContainsKey(value) Then Return

                    Dim adapter As Action(Of IMASAppTheme) =
                        Sub(theme As IMASAppTheme)
                            value.Invoke(theme)
                        End Sub

                    _themeRegisteredAdapters(value) = adapter
                    ThemeManager.AddPublicThemeRegisteredHandler(adapter)

                End SyncLock

            End AddHandler

            RemoveHandler(value As Action(Of IMASAppTheme))

                If value Is Nothing Then Return

                SyncLock _syncRoot

                    Dim adapter As Action(Of IMASAppTheme) = Nothing

                    If Not _themeRegisteredAdapters.TryGetValue(value, adapter) Then Return

                    ThemeManager.RemovePublicThemeRegisteredHandler(adapter)
                    _themeRegisteredAdapters.Remove(value)

                End SyncLock

            End RemoveHandler

            RaiseEvent(theme As IMASAppTheme)
                ' Forwarded by ThemeManager.
            End RaiseEvent

        End Event

#End Region

        Friend Shared ReadOnly Property CurrentCore As IMASAppTheme
            Get
                Return ThemeManager.CurrentCore
            End Get
        End Property

        Friend Shared ReadOnly Property CurrentIdCore As String
            Get
                Dim theme As IMASAppTheme = CurrentCore
                If theme Is Nothing Then Return String.Empty
                Return theme.Id
            End Get
        End Property

        Friend Shared ReadOnly Property CurrentDisplayNameCore As String
            Get
                Dim theme As IMASAppTheme = CurrentCore
                If theme Is Nothing Then Return String.Empty
                Return theme.DisplayName
            End Get
        End Property

        Friend Shared Function GetAllCore() As IReadOnlyList(Of IMASAppTheme)
            Return ThemeManager.GetAllThemesCore()
        End Function

        Friend Shared Function GetByIdCore(id As String) As IMASAppTheme
            If String.IsNullOrWhiteSpace(id) Then Return Nothing
            Return ThemeManager.GetByIdCore(id)
        End Function

        Friend Shared Function TryUseCore(id As String) As Boolean
            If String.IsNullOrWhiteSpace(id) Then Return False
            Return ThemeManager.TryApplyThemeCore(id)
        End Function

        Friend Shared Sub UseCore(id As String)

            If String.IsNullOrWhiteSpace(id) Then
                Throw New ArgumentException("Theme id cannot be null, empty, or whitespace.", NameOf(id))
            End If

            If Not TryUseCore(id) Then
                Throw New InvalidOperationException("Theme '" & id & "' is not registered.")
            End If

        End Sub

        Friend Shared Sub RegisterCore(theme As IMASAppTheme)

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

            ThemeManager.RegisterCore(theme)

        End Sub

        Public ReadOnly Property Current As IMASAppTheme
            Get
                Return CurrentCore
            End Get
        End Property

        Public ReadOnly Property CurrentId As String
            Get
                Return CurrentIdCore
            End Get
        End Property

        Public ReadOnly Property CurrentDisplayName As String
            Get
                Return CurrentDisplayNameCore
            End Get
        End Property

        Public Function GetAll() As IReadOnlyList(Of IMASAppTheme)
            Return GetAllCore()
        End Function

        Public Function GetById(id As String) As IMASAppTheme
            Return GetByIdCore(id)
        End Function

        Public Function TryUse(id As String) As Boolean
            Return TryUseCore(id)
        End Function

        Public Sub Use(id As String)
            UseCore(id)
        End Sub

        ''' <summary>
        ''' Internal registration seam for Nexamas UI-owned themes only.
        ''' Public SDK consumers select already registered themes by id; arbitrary IMASAppTheme registration is closed before v1.
        ''' </summary>
        Friend Sub Register(theme As IMASAppTheme)
            RegisterCore(theme)
        End Sub

    End Class

End Namespace