Option Strict On
Option Explicit On

Imports System
Imports Nexamas.UI.Controls
Imports Nexamas.UI.Values
Imports Nexamas.UI.Layout
Imports SkiaSharp

Namespace Nexamas.UI.Application

    ''' <summary>
    ''' Internal themed inner frame for Nexamas UI-owned PanelShell content bodies.
    ''' It can frame either the whole body or only the internally defined scrollable viewport.
    ''' Public SDK callers do not own shell chrome, frame offsets, scrollbar gutters, or
    ''' PanelShell group-box styling directly.
    ''' </summary>
    Friend NotInheritable Class MASApplicationPanelShellContentGroupBox

        Friend Enum SurfaceKind
            Acrylic = 0
            Plain = 1
            TransparentFrame = 2
        End Enum

        Friend Enum BorderRecipeKind
            ListSurface = 0
            AcrylicSurface = 1
        End Enum

        Friend Enum BorderStrengthKind
            Level1 = 1
            Level2 = 2
            Level3 = 3
            Level4 = 4
            Level5 = 5
            Level6 = 6
        End Enum

        Friend Enum PlacementKind
            ContentBody = 0
            ScrollableArea = 1
        End Enum

        Friend Sub New()
        End Sub

        ''' <summary>
        ''' Enables the inner themed content frame. Disabled by default so normal panels stay flat.
        ''' </summary>
        Friend Property Enabled As Boolean = False

        ''' <summary>
        ''' Optional floating title drawn by the underlying MASGroupBox renderer.
        ''' Empty titles produce a simple border/frame without reserving title height unless
        ''' ReserveHeaderWhenEmpty is explicitly enabled.
        ''' </summary>
        Friend Property Title As String = String.Empty

        Friend Property Surface As SurfaceKind = SurfaceKind.Acrylic
        Friend Property BorderRecipe As BorderRecipeKind = BorderRecipeKind.ListSurface
        Friend Property BorderStrength As BorderStrengthKind = BorderStrengthKind.Level3

        ''' <summary>
        ''' Defines which part of the official content body receives the inner frame.
        ''' ContentBody frames the whole body and gives ContentBuilder the inner size.
        ''' ScrollableArea frames only the later SetScrollableArea(...) viewport so callers can keep
        ''' descriptions, counters, or helper text outside the visual group.
        ''' </summary>
        Friend Property Placement As PlacementKind = PlacementKind.ContentBody
        Friend Property DrawShadow As Boolean = False
        Friend Property DrawPremiumBorder As Boolean = True
        Friend Property DrawSeparator As Boolean = False
        Friend Property ReserveHeaderWhenEmpty As Boolean = False

        Friend Property OuterInsetLeftPx As Single = 0.0F
        Friend Property OuterInsetTopPx As Single = 0.0F
        Friend Property OuterInsetRightPx As Single = 0.0F
        Friend Property OuterInsetBottomPx As Single = 0.0F

        Friend Property PaddingLeftPx As Single = MASApplicationSurfaceSizeTokens.PanelShellContentGroupBoxPaddingHorizontalPx
        Friend Property PaddingTopPx As Single = MASApplicationSurfaceSizeTokens.PanelShellContentGroupBoxPaddingTopPx
        Friend Property PaddingRightPx As Single = MASApplicationSurfaceSizeTokens.PanelShellContentGroupBoxPaddingHorizontalPx
        Friend Property PaddingBottomPx As Single = MASApplicationSurfaceSizeTokens.PanelShellContentGroupBoxPaddingBottomPx

        Friend Function Use(Optional title As String = Nothing) As MASApplicationPanelShellContentGroupBox

            Enabled = True

            If title IsNot Nothing Then
                Me.Title = title
            End If

            Return Me

        End Function

        Friend Function UseForScrollableArea(Optional title As String = Nothing) As MASApplicationPanelShellContentGroupBox

            Use(title)
            Placement = PlacementKind.ScrollableArea
            Return Me

        End Function

        Friend Function UseForContentBody(Optional title As String = Nothing) As MASApplicationPanelShellContentGroupBox

            Use(title)
            Placement = PlacementKind.ContentBody
            Return Me

        End Function

        Friend Function Disable() As MASApplicationPanelShellContentGroupBox

            Enabled = False
            Return Me

        End Function

        Friend Sub SetOuterInset(allPx As Single)
            SetOuterInset(allPx, allPx, allPx, allPx)
        End Sub

        Friend Sub SetOuterInset(horizontalPx As Single,
                                 verticalPx As Single)

            SetOuterInset(horizontalPx, verticalPx, horizontalPx, verticalPx)

        End Sub

        Friend Sub SetOuterInset(leftPx As Single,
                                 topPx As Single,
                                 rightPx As Single,
                                 bottomPx As Single)

            OuterInsetLeftPx = SafeNonNegative(leftPx)
            OuterInsetTopPx = SafeNonNegative(topPx)
            OuterInsetRightPx = SafeNonNegative(rightPx)
            OuterInsetBottomPx = SafeNonNegative(bottomPx)

        End Sub

        Friend Sub SetPadding(allPx As Single)
            SetPadding(allPx, allPx, allPx, allPx)
        End Sub

        Friend Sub SetPadding(horizontalPx As Single,
                              verticalPx As Single)

            SetPadding(horizontalPx, verticalPx, horizontalPx, verticalPx)

        End Sub

        Friend Sub SetPadding(leftPx As Single,
                              topPx As Single,
                              rightPx As Single,
                              bottomPx As Single)

            PaddingLeftPx = SafeNonNegative(leftPx)
            PaddingTopPx = SafeNonNegative(topPx)
            PaddingRightPx = SafeNonNegative(rightPx)
            PaddingBottomPx = SafeNonNegative(bottomPx)

        End Sub

        Friend Function ApplyOuterInset(boundsPx As SKRect) As SKRect

            If Not IsUsableBounds(boundsPx) Then Return SKRect.Empty

            Dim result As New SKRect(
                boundsPx.Left + SafeNonNegative(OuterInsetLeftPx),
                boundsPx.Top + SafeNonNegative(OuterInsetTopPx),
                boundsPx.Right - SafeNonNegative(OuterInsetRightPx),
                boundsPx.Bottom - SafeNonNegative(OuterInsetBottomPx))

            If Not IsUsableBounds(result) Then Return SKRect.Empty
            Return result

        End Function

        Friend Function ResolveInnerContentBoundsPx(groupBoxBoundsPx As SKRect,
                                                    dpi As Single) As SKRect

            If Not IsUsableBounds(groupBoxBoundsPx) Then Return SKRect.Empty

            Dim safeDpi As Single = NormalizeDpi(dpi)
            Dim headerHeightPx As Single = ResolveHeaderHeightPx(safeDpi)

            Dim result As New SKRect(
                groupBoxBoundsPx.Left + SafeNonNegative(PaddingLeftPx),
                groupBoxBoundsPx.Top + headerHeightPx + SafeNonNegative(PaddingTopPx),
                groupBoxBoundsPx.Right - SafeNonNegative(PaddingRightPx),
                groupBoxBoundsPx.Bottom - SafeNonNegative(PaddingBottomPx))

            If Not IsUsableBounds(result) Then Return SKRect.Empty
            Return result

        End Function

        Friend Function ResolveInnerContentBoundsLogical(groupBoxBoundsLogical As SKRect,
                                                          dpi As Single) As SKRect

            If Not IsUsableBounds(groupBoxBoundsLogical) Then Return SKRect.Empty

            Dim safeDpi As Single = NormalizeDpi(dpi)

            Dim groupBoxBoundsPx As New SKRect(
                groupBoxBoundsLogical.Left * safeDpi,
                groupBoxBoundsLogical.Top * safeDpi,
                groupBoxBoundsLogical.Right * safeDpi,
                groupBoxBoundsLogical.Bottom * safeDpi)

            Dim innerPx As SKRect = ResolveInnerContentBoundsPx(groupBoxBoundsPx, safeDpi)
            If Not IsUsableBounds(innerPx) Then Return SKRect.Empty

            Return New SKRect(
                innerPx.Left / safeDpi,
                innerPx.Top / safeDpi,
                innerPx.Right / safeDpi,
                innerPx.Bottom / safeDpi)

        End Function

        Friend Function CreateControl(dpi As Single) As MASGroupBox

            Dim safeDpi As Single = NormalizeDpi(dpi)

            Dim groupBox As MASGroupBox = MASGroupBox.Create(If(Title, String.Empty))
            groupBox.SurfaceSource = ToGroupBoxSurface(Surface)
            groupBox.BorderRecipe = ToGroupBoxBorderRecipe(BorderRecipe)
            groupBox.BorderStrength = ToGroupBoxBorderStrength(BorderStrength)
            groupBox.DrawShadow = DrawShadow
            groupBox.DrawPremiumBorder = DrawPremiumBorder
            groupBox.DrawSeparator = DrawSeparator
            groupBox.ReserveHeaderWhenEmpty = ReserveHeaderWhenEmpty
            groupBox.PaddingLeft = SafeNonNegative(PaddingLeftPx) / safeDpi
            groupBox.PaddingTop = SafeNonNegative(PaddingTopPx) / safeDpi
            groupBox.PaddingRight = SafeNonNegative(PaddingRightPx) / safeDpi
            groupBox.PaddingBottom = SafeNonNegative(PaddingBottomPx) / safeDpi

            Return groupBox

        End Function

        Private Function ResolveHeaderHeightPx(dpi As Single) As Single

            If ReserveHeaderWhenEmpty Then Return GroupBoxTokens.HeaderHeight * dpi
            If Not String.IsNullOrWhiteSpace(Title) Then Return GroupBoxTokens.HeaderHeight * dpi
            Return 0.0F

        End Function

        Private Shared Function ToGroupBoxSurface(value As SurfaceKind) As MASGroupBoxSurfaceSource

            Select Case value
                Case SurfaceKind.Plain
                    Return MASGroupBoxSurfaceSource.Plain
                Case SurfaceKind.TransparentFrame
                    Return MASGroupBoxSurfaceSource.TransparentFrame
                Case Else
                    Return MASGroupBoxSurfaceSource.Acrylic
            End Select

        End Function

        Private Shared Function ToGroupBoxBorderRecipe(value As BorderRecipeKind) As MASGroupBoxBorderRecipe

            Select Case value
                Case BorderRecipeKind.AcrylicSurface
                    Return MASGroupBoxBorderRecipe.AcrylicSurface
                Case Else
                    Return MASGroupBoxBorderRecipe.ListSurface
            End Select

        End Function

        Private Shared Function ToGroupBoxBorderStrength(value As BorderStrengthKind) As MASGroupBoxBorderStrength

            Select Case value
                Case BorderStrengthKind.Level1
                    Return MASGroupBoxBorderStrength.Level1
                Case BorderStrengthKind.Level2
                    Return MASGroupBoxBorderStrength.Level2
                Case BorderStrengthKind.Level3
                    Return MASGroupBoxBorderStrength.Level3
                Case BorderStrengthKind.Level4
                    Return MASGroupBoxBorderStrength.Level4
                Case BorderStrengthKind.Level5
                    Return MASGroupBoxBorderStrength.Level5
                Case Else
                    Return MASGroupBoxBorderStrength.Level6
            End Select

        End Function

        Private Shared Function SafeNonNegative(value As Single) As Single

            If value < 0.0F OrElse Single.IsNaN(value) OrElse Single.IsInfinity(value) Then
                Return 0.0F
            End If

            Return value

        End Function

        Private Shared Function NormalizeDpi(value As Single) As Single

            If value <= 0.0F OrElse Single.IsNaN(value) OrElse Single.IsInfinity(value) Then
                Return 1.0F
            End If

            Return value

        End Function

        Private Shared Function IsUsableBounds(bounds As SKRect) As Boolean

            Return Not bounds.IsEmpty AndAlso
                   bounds.Width > 1.0F AndAlso
                   bounds.Height > 1.0F

        End Function

    End Class

End Namespace
