Option Strict On
Option Explicit On

Imports System
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.Values

Namespace Nexamas.UI.Architecture

    ''' <summary>
    ''' Resolves runtime shadow specifications from MAS shadow contracts.
    ''' ShadowRole and SurfaceFamily from MASResolvedComponentContract are the authority.
    ''' This class is the official ShadowSystem resolver from architecture contracts to MAS shadow specs.
    ''' </summary>
    Friend NotInheritable Class MASShadowResolver

        Private Sub New()
        End Sub

        Friend Shared Function ResolveOuter(contract As MASResolvedComponentContract) As MASShadowSpec

            If contract Is Nothing Then
                Throw New ArgumentNullException(NameOf(contract))
            End If

            contract.AssertConsumable()

            Return ResolveOuter(contract.SurfaceFamily, contract.ShadowRole)

        End Function

        Friend Shared Function ResolveLayered(contract As MASResolvedComponentContract) As MASLayeredShadowSpec

            If contract Is Nothing Then
                Throw New ArgumentNullException(NameOf(contract))
            End If

            contract.AssertConsumable()

            Return ResolveLayered(contract.SurfaceFamily, contract.ShadowRole)

        End Function

        Friend Shared Function ResolveCard(contract As MASResolvedComponentContract,
                                           Optional secondary As Boolean = False) As MASCardShadowSpec

            If contract Is Nothing Then
                Throw New ArgumentNullException(NameOf(contract))
            End If

            contract.AssertConsumable()

            Return ResolveCard(contract.SurfaceFamily, contract.ShadowRole, secondary)

        End Function

        Friend Shared Function ResolveOuter(fam As CommonEnums.SurfaceFamily,
                                            role As MASShadowRole) As MASShadowSpec

            If Not MASShadowContractRegistry.IsSupported(role, fam) Then
                Return EmptyOuter()
            End If

            Dim tier As MASShadowHierarchyProfiles.ElevationTier = ResolveTierFromRole(role)
            Dim p As MASShadowHierarchyProfiles.OuterProfile =
                MASShadowHierarchyProfiles.ResolveOuterProfile(tier)

            Return New MASShadowSpec With {
                .SigmaDip = SafeNonNegative(p.SigmaDip),
                .Alpha = ClampByte(p.Alpha),
                .OffsetYDip = SafeNonNegative(p.OffsetYDip),
                .PadMinPx = SafePositiveInt(p.PadMinPx, 10),
                .PadSigmaMul = SafePositive(p.PadSigmaMul, 3.0F)
            }

        End Function

        Friend Shared Function ResolveLayered(fam As CommonEnums.SurfaceFamily,
                                              role As MASShadowRole) As MASLayeredShadowSpec

            If Not MASShadowContractRegistry.IsSupported(role, fam) Then
                Return EmptyLayered()
            End If

            Dim tier As MASShadowHierarchyProfiles.ElevationTier = ResolveTierFromRole(role)
            Dim p As MASShadowHierarchyProfiles.LayeredProfile =
                MASShadowHierarchyProfiles.ResolveLayeredProfile(tier)

            Return New MASLayeredShadowSpec With {
                .SigmaA_Dip = SafeNonNegative(p.SigmaA_Dip),
                .AlphaA = ClampByte(p.AlphaA),
                .OffY_A_Dip = SafeNonNegative(p.OffY_A_Dip),
                .SigmaB_Dip = SafeNonNegative(p.SigmaB_Dip),
                .AlphaB = ClampByte(p.AlphaB),
                .OffY_B_Dip = SafeNonNegative(p.OffY_B_Dip),
                .PadMinPx = SafePositiveInt(p.PadMinPx, 10),
                .PadSigmaMul = SafePositive(p.PadSigmaMul, 3.0F)
            }

        End Function

        Friend Shared Function ResolveCard(fam As CommonEnums.SurfaceFamily,
                                           role As MASShadowRole,
                                           Optional secondary As Boolean = False) As MASCardShadowSpec

            If role = MASShadowRole.None Then
                Return EmptyCard()
            End If

            If fam <> CommonEnums.SurfaceFamily.Card Then

                Return EmptyCard()
            End If

            Dim layered As MASLayeredShadowSpec =
                ResolveLayered(fam, role)

            Return New MASCardShadowSpec With {
                .SigmaA_Dip = layered.SigmaA_Dip,
                .AlphaA = layered.AlphaA,
                .OffY_A_Dip = layered.OffY_A_Dip,
                .SigmaB_Dip = layered.SigmaB_Dip,
                .AlphaB = layered.AlphaB,
                .OffY_B_Dip = layered.OffY_B_Dip,
                .PadMinPx = layered.PadMinPx,
                .PadSigmaMul = layered.PadSigmaMul
            }

        End Function

        Private Shared Function ResolveTierFromRole(role As MASShadowRole) As MASShadowHierarchyProfiles.ElevationTier

            Select Case role

                Case MASShadowRole.None
                    Return MASShadowHierarchyProfiles.ElevationTier.None

                Case MASShadowRole.Soft
                    Return MASShadowHierarchyProfiles.ElevationTier.Hairline

                Case MASShadowRole.Control
                    Return MASShadowHierarchyProfiles.ElevationTier.Compact

                Case MASShadowRole.Floating
                    Return MASShadowHierarchyProfiles.ElevationTier.Floating

                Case MASShadowRole.Popup,
                     MASShadowRole.Shell,
                     MASShadowRole.Dialog,
                     MASShadowRole.Toast,
                     MASShadowRole.Tooltip

                    Return MASShadowHierarchyProfiles.ElevationTier.Overlay

                Case Else
                    Throw New NotSupportedException(
                        "Unsupported shadow role: " & role.ToString())

            End Select

        End Function

        Private Shared Function EmptyOuter() As MASShadowSpec
            Return New MASShadowSpec With {
                .SigmaDip = 0.0F,
                .Alpha = 0,
                .OffsetYDip = 0.0F,
                .PadMinPx = 10,
                .PadSigmaMul = 3.0F
            }
        End Function

        Private Shared Function EmptyLayered() As MASLayeredShadowSpec
            Return New MASLayeredShadowSpec With {
                .SigmaA_Dip = 0.0F,
                .AlphaA = 0,
                .OffY_A_Dip = 0.0F,
                .SigmaB_Dip = 0.0F,
                .AlphaB = 0,
                .OffY_B_Dip = 0.0F,
                .PadMinPx = 10,
                .PadSigmaMul = 3.0F
            }
        End Function

        Private Shared Function EmptyCard() As MASCardShadowSpec
            Return New MASCardShadowSpec With {
                .SigmaA_Dip = 0.0F,
                .AlphaA = 0,
                .OffY_A_Dip = 0.0F,
                .SigmaB_Dip = 0.0F,
                .AlphaB = 0,
                .OffY_B_Dip = 0.0F,
                .PadMinPx = 10,
                .PadSigmaMul = 3.0F
            }
        End Function

        Private Shared Function ClampByte(v As Integer) As Byte
            If v < 0 Then Return 0
            If v > 255 Then Return 255
            Return CByte(v)
        End Function

        Private Shared Function SafeNonNegative(v As Single) As Single
            If Single.IsNaN(v) OrElse Single.IsInfinity(v) OrElse v < 0.0F Then
                Return 0.0F
            End If

            Return v
        End Function

        Private Shared Function SafePositive(v As Single,
                                             fallback As Single) As Single
            If Single.IsNaN(v) OrElse Single.IsInfinity(v) OrElse v <= 0.0F Then
                Return fallback
            End If

            Return v
        End Function

        Private Shared Function SafePositiveInt(v As Integer,
                                                fallback As Integer) As Integer
            If v <= 0 Then Return fallback
            Return v
        End Function

    End Class

End Namespace