Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic

Namespace Nexamas.UI.FloatRuntime

    ' ============================================================
    ' MASFloatRuntime Contract
    ' Class: MASFloatRegistry
    '
    ' Purpose:
    '   Stores and resolves registered MASFloatDescriptor instances by
    '   Float key.
    '
    ' Responsibility:
    '   Provides the official registration table for Float types.
    '   Validates descriptor identity.
    '   Prevents duplicate Float keys.
    '   Returns descriptor snapshots to protect registry state.
    '
    ' Owns:
    '   Registered Float type descriptors.
    '
    ' Does Not Own:
    '   Open Float instances.
    '   MASFloatRequest instances.
    '   MASFloatHandle instances.
    '   Runtime lifecycle.
    '   Current slot state.
    '   Queue state.
    '   Host state.
    '   Policy decisions.
    '   Presentation calculations.
    '   Input routing.
    '   Focus ownership.
    '   Result delivery.
    '
    ' Allowed Dependencies:
    '   MASFloatDescriptor.
    '   System.Collections.Generic.Dictionary.
    '   System.StringComparer.
    '
    ' Forbidden Dependencies:
    '   Legacy Overlay classes.
    '   OverlayHost.
    '   OverlayManager.
    '   OverlayContent.
    '   OverlayLayer.
    '   OverlayService.
    '   BlockingControlsOverlayContent.
    '   MASFloatCoordinator.
    '   MASFloatPolicyEngine.
    '   MASFloatRuntimeState.
    '   MASFloatHost.
    '   Feature-owned lifecycle services.
    '
    ' Lifecycle Role:
    '   Registry is initialized before Floats are shown.
    '   Every Float type must be registered before it can be requested.
    '   Registry does not participate in open/close lifecycle transitions.
    '
    ' Threading:
    '   Registry operations are synchronized internally.
    '   Registration should normally happen during UI/runtime bootstrap.
    '   Runtime lifecycle code may read from the registry after bootstrap.
    '
    ' Mutation Rules:
    '   Descriptors are cloned on registration.
    '   Descriptors are cloned when returned to callers.
    '   External mutation of a descriptor after Register must not mutate
    '   registry state.
    '
    ' Invariants:
    '   No empty key may be registered.
    '   No duplicate key may be registered.
    '   Registry describes Float types only.
    '   Registry never owns Float instances.
    '
    ' Failure Rules:
    '   Register throws for Nothing descriptors, empty keys, or duplicate keys.
    '   Find throws for missing keys.
    '   TryFind returns False for missing or invalid keys.
    ' ============================================================
    ''' <summary>
    ''' Advanced extension registry for MAS Float descriptors.
    ''' It is Friend-only runtime infrastructure; normal SDK code should use typed window facades.
    ''' </summary>
    Friend NotInheritable Class MASFloatRegistry

        Private ReadOnly _syncRoot As New Object()

        Private ReadOnly _descriptors As New Dictionary(Of String, MASFloatDescriptor)(
            StringComparer.Ordinal
        )

        Friend ReadOnly Property Count As Integer
            Get
                SyncLock _syncRoot
                    Return _descriptors.Count
                End SyncLock
            End Get
        End Property

        Friend Sub Register(descriptor As MASFloatDescriptor)
            If descriptor Is Nothing Then
                Throw New ArgumentNullException(NameOf(descriptor))
            End If

            ValidateDescriptorForRegistration(descriptor)

            Dim key As String = NormalizeKey(descriptor.Key)

            SyncLock _syncRoot
                If _descriptors.ContainsKey(key) Then
                    Throw New InvalidOperationException(
                        "A Float descriptor with key '" & key & "' is already registered."
                    )
                End If

                Dim stored As MASFloatDescriptor = descriptor.Clone()
                stored.Key = key

                If stored.DefaultPresentation Is Nothing Then
                    stored.DefaultPresentation = New MASFloatPresentation()
                End If

                _descriptors.Add(key, stored)
            End SyncLock
        End Sub

        Friend Function Contains(floatKey As String) As Boolean
            Dim key As String = NormalizeKey(floatKey)

            If key.Length = 0 Then
                Return False
            End If

            SyncLock _syncRoot
                Return _descriptors.ContainsKey(key)
            End SyncLock
        End Function

        Friend Function Find(floatKey As String) As MASFloatDescriptor
            Dim key As String = NormalizeKey(floatKey)

            If key.Length = 0 Then
                Throw New ArgumentException("Float key is required.", NameOf(floatKey))
            End If

            SyncLock _syncRoot
                Dim descriptor As MASFloatDescriptor = Nothing

                If Not _descriptors.TryGetValue(key, descriptor) Then
                    Throw New KeyNotFoundException(
                        "No Float descriptor is registered with key '" & key & "'."
                    )
                End If

                Return descriptor.Clone()
            End SyncLock
        End Function

        Friend Function TryFind(
            floatKey As String,
            ByRef descriptor As MASFloatDescriptor
        ) As Boolean
            descriptor = Nothing

            Dim key As String = NormalizeKey(floatKey)

            If key.Length = 0 Then
                Return False
            End If

            SyncLock _syncRoot
                Dim stored As MASFloatDescriptor = Nothing

                If Not _descriptors.TryGetValue(key, stored) Then
                    Return False
                End If

                descriptor = stored.Clone()
                Return True
            End SyncLock
        End Function

        Friend Function GetRegisteredKeys() As IReadOnlyList(Of String)
            SyncLock _syncRoot
                Dim keys As New List(Of String)(_descriptors.Keys)
                keys.Sort(StringComparer.Ordinal)
                Return keys.AsReadOnly()
            End SyncLock
        End Function

        Friend Sub Unregister(floatKey As String)
            Dim key As String = NormalizeKey(floatKey)

            If key.Length = 0 Then
                Throw New ArgumentException("Float key is required.", NameOf(floatKey))
            End If

            SyncLock _syncRoot
                If Not _descriptors.Remove(key) Then
                    Throw New KeyNotFoundException(
                        "No Float descriptor is registered with key '" & key & "'."
                    )
                End If
            End SyncLock
        End Sub

        Friend Sub Clear()
            SyncLock _syncRoot
                _descriptors.Clear()
            End SyncLock
        End Sub

        Friend Shared Function NormalizeKey(floatKey As String) As String
            If floatKey Is Nothing Then
                Return String.Empty
            End If

            Return floatKey.Trim()
        End Function

        Friend Shared Sub ValidateKey(floatKey As String, argumentName As String)
            Dim key As String = NormalizeKey(floatKey)

            If key.Length = 0 Then
                Throw New ArgumentException("Float key is required.", argumentName)
            End If
        End Sub

        Private Shared Sub ValidateDescriptorForRegistration(descriptor As MASFloatDescriptor)
            ValidateKey(descriptor.Key, NameOf(descriptor.Key))

            If descriptor.DefaultPresentation Is Nothing Then
                Return
            End If

            If descriptor.DefaultPresentation.DurationMs < 0 Then
                Throw New ArgumentOutOfRangeException(
                    NameOf(descriptor.DefaultPresentation.DurationMs),
                    "Presentation duration cannot be negative."
                )
            End If

            If descriptor.DefaultPresentation.DimOpacity < 0.0F OrElse
               descriptor.DefaultPresentation.DimOpacity > 1.0F Then
                Throw New ArgumentOutOfRangeException(
                    NameOf(descriptor.DefaultPresentation.DimOpacity),
                    "Dim opacity must be between 0 and 1."
                )
            End If
        End Sub

    End Class

End Namespace