Option Strict On
Option Explicit On

Imports System
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.General
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.Rendering

    Friend NotInheritable Class MASPanelShellLayoutEngine

        Private Sub New()
        End Sub

        Friend Shared Function Measure(dpi As Single,
                               spec As MASPanelShellSpec,
                               contract As MASResolvedComponentContract) As MASPanelShellResult

            If spec Is Nothing Then Return MASPanelShellResult.Empty

            If contract Is Nothing Then Throw New ArgumentNullException(NameOf(contract))
            contract.AssertConsumable()

            dpi = PixelSnap.SafeDpi(dpi)

            Dim baseRect As SKRect = PixelSnap.SnapRectHalf(spec.BoundsPx)
            If Not PixelSnap.HasArea(baseRect) Then Return MASPanelShellResult.Empty

            Dim radiusPx As Single =
    MASRadiusResolver.ResolveSurfacePx(
        contract:=contract,
        rPx:=baseRect,
        dpi:=dpi)

            Dim header As MASPanelShellHeaderSpec = If(spec.Header, MASPanelShellHeaderSpec.None)
            Dim hasHeader As Boolean = header.IsEnabled

            Dim headerRect As SKRect = SKRect.Empty
            If hasHeader Then
                Dim headerHeightPx As Single = ResolveHeaderHeightDip(header.Size, header.Density) * dpi
                headerHeightPx = Math.Min(headerHeightPx, Math.Max(0.0F, baseRect.Height * 0.45F))

                If headerHeightPx > 1.0F Then
                    headerRect = PixelSnap.SnapRectHalf(
                        New SKRect(baseRect.Left, baseRect.Top, baseRect.Right, baseRect.Top + headerHeightPx))
                End If
            End If

            Dim padL As Single
            Dim padT As Single
            Dim padR As Single
            Dim padB As Single
            ResolveContentPadding(spec, dpi, padL, padT, padR, padB)

            Dim contentTop As Single = If(headerRect.IsEmpty, baseRect.Top + padT, headerRect.Bottom + padT)
            Dim contentRect As SKRect = PixelSnap.SnapRectHalf(
                New SKRect(
                    baseRect.Left + padL,
                    contentTop,
                    baseRect.Right - padR,
                    baseRect.Bottom - padB))

            If Not PixelSnap.HasArea(contentRect) Then contentRect = SKRect.Empty

            Dim iconRect As SKRect = SKRect.Empty
            Dim titleRect As SKRect = SKRect.Empty
            Dim subtitleRect As SKRect = SKRect.Empty
            Dim closeRect As SKRect = SKRect.Empty

            If hasHeader AndAlso Not headerRect.IsEmpty Then
                ResolveHeaderParts(headerRect, header, dpi, iconRect, titleRect, subtitleRect, closeRect)
            End If

            Return New MASPanelShellResult(
                baseRectPx:=baseRect,
                radiusPx:=radiusPx,
                headerRectPx:=headerRect,
                contentRectPx:=contentRect,
                headerIconRectPx:=iconRect,
                headerTitleRectPx:=titleRect,
                headerSubtitleRectPx:=subtitleRect,
                headerCloseRectPx:=closeRect,
                hasHeader:=hasHeader AndAlso Not headerRect.IsEmpty)
        End Function

        Private Shared Sub ResolveHeaderParts(headerRect As SKRect,
                                              header As MASPanelShellHeaderSpec,
                                              dpi As Single,
                                              ByRef iconRect As SKRect,
                                              ByRef titleRect As SKRect,
                                              ByRef subtitleRect As SKRect,
                                              ByRef closeRect As SKRect)

            Dim r As SKRect = headerRect
            If r.IsEmpty Then Return

            Dim sidePad As Single = ResolveHeaderSidePaddingDip(header.Density) * dpi
            Dim gap As Single = ResolveHeaderGapDip(header.Density) * dpi
            Dim closeSize As Single = ResolveCloseSizeDip(header.Density) * dpi
            Dim iconSize As Single = ResolveIconSizeDip(header.Size, header.Density) * dpi

            Dim xLeft As Single = r.Left + sidePad
            Dim xRight As Single = r.Right - sidePad

            If header.ShowCloseButton Then
                closeRect = PixelSnap.SnapRectHalf(
                    New SKRect(
                        xRight - closeSize,
                        r.MidY - closeSize * 0.5F,
                        xRight,
                        r.MidY + closeSize * 0.5F))

                xRight = closeRect.Left - gap
            End If

            If header.ShowIcon Then
                iconRect = PixelSnap.SnapRectHalf(
                    New SKRect(
                        xLeft,
                        r.MidY - iconSize * 0.5F,
                        xLeft + iconSize,
                        r.MidY + iconSize * 0.5F))

                xLeft = iconRect.Right + gap
            End If

            Dim textRect As SKRect = PixelSnap.SnapRectHalf(New SKRect(xLeft, r.Top, xRight, r.Bottom))
            If textRect.Width <= 1.0F OrElse textRect.Height <= 1.0F Then Return

            Select Case header.Layout
                Case MASPanelShellHeaderLayout.TitleSubtitle,
                     MASPanelShellHeaderLayout.IconTitleSubtitle

                    Dim titleH As Single = Math.Max(18.0F * dpi, textRect.Height * 0.48F)
                    Dim subH As Single = Math.Max(14.0F * dpi, textRect.Height * 0.34F)
                    Dim totalH As Single = titleH + subH
                    Dim top As Single = textRect.MidY - totalH * 0.5F

                    titleRect = PixelSnap.SnapRectHalf(
                        New SKRect(textRect.Left, top, textRect.Right, top + titleH))

                    subtitleRect = PixelSnap.SnapRectHalf(
                        New SKRect(textRect.Left, top + titleH, textRect.Right, top + titleH + subH))

                Case Else
                    titleRect = textRect
                    subtitleRect = SKRect.Empty
            End Select
        End Sub

        Private Shared Function ResolveHeaderHeightDip(size As MASPanelShellHeaderSize,
                                                       density As MASPanelShellDensity) As Single

            Dim h As Single

            Select Case size
                Case MASPanelShellHeaderSize.None
                    h = 0.0F
                Case MASPanelShellHeaderSize.Thin
                    h = PanelShellTokens.HeaderThinHeightDip
                Case MASPanelShellHeaderSize.Compact
                    h = PanelShellTokens.HeaderCompactHeightDip
                Case MASPanelShellHeaderSize.Comfortable
                    h = PanelShellTokens.HeaderComfortableHeightDip
                Case MASPanelShellHeaderSize.Hero
                    h = PanelShellTokens.HeaderHeroHeightDip
                Case Else
                    h = PanelShellTokens.HeaderNormalHeightDip
            End Select

            Select Case density
                Case MASPanelShellDensity.Compact
                    h -= 4.0F
                Case MASPanelShellDensity.Comfortable
                    h += 4.0F
            End Select

            Return Math.Max(0.0F, h)
        End Function

        Private Shared Sub ResolveContentPadding(spec As MASPanelShellSpec,
                                                 dpi As Single,
                                                 ByRef l As Single,
                                                 ByRef t As Single,
                                                 ByRef r As Single,
                                                 ByRef b As Single)

            Dim padX As Single
            Dim padY As Single

            Select Case spec.Density
                Case MASPanelShellDensity.Compact
                    padX = PanelShellTokens.PaddingXCompactDip
                    padY = PanelShellTokens.PaddingYCompactDip

                Case MASPanelShellDensity.Comfortable
                    padX = PanelShellTokens.PaddingXComfortableDip
                    padY = PanelShellTokens.PaddingYComfortableDip

                Case Else
                    padX = PanelShellTokens.PaddingXNormalDip
                    padY = PanelShellTokens.PaddingYNormalDip
            End Select

            l = If(spec.Padding.LeftDip >= 0.0F, spec.Padding.LeftDip, padX) * dpi
            t = If(spec.Padding.TopDip >= 0.0F, spec.Padding.TopDip, padY) * dpi
            r = If(spec.Padding.RightDip >= 0.0F, spec.Padding.RightDip, padX) * dpi
            b = If(spec.Padding.BottomDip >= 0.0F, spec.Padding.BottomDip, padY) * dpi
        End Sub

        Private Shared Function ResolveHeaderSidePaddingDip(density As MASPanelShellDensity) As Single
            Select Case density
                Case MASPanelShellDensity.Compact
                    Return PanelShellTokens.HeaderEdgeCompactDip
                Case MASPanelShellDensity.Comfortable
                    Return PanelShellTokens.HeaderEdgeComfortableDip
                Case Else
                    Return PanelShellTokens.HeaderEdgeNormalDip
            End Select
        End Function

        Private Shared Function ResolveHeaderGapDip(density As MASPanelShellDensity) As Single
            Select Case density
                Case MASPanelShellDensity.Compact
                    Return PanelShellTokens.HeaderGapCompactDip
                Case MASPanelShellDensity.Comfortable
                    Return PanelShellTokens.HeaderGapComfortableDip
                Case Else
                    Return PanelShellTokens.HeaderGapNormalDip
            End Select
        End Function

        Private Shared Function ResolveCloseSizeDip(density As MASPanelShellDensity) As Single
            Select Case density
                Case MASPanelShellDensity.Compact
                    Return 26.0F
                Case MASPanelShellDensity.Comfortable
                    Return 34.0F
                Case Else
                    Return 30.0F
            End Select
        End Function

        Private Shared Function ResolveIconSizeDip(size As MASPanelShellHeaderSize,
                                                   density As MASPanelShellDensity) As Single

            Dim v As Single

            Select Case size
                Case MASPanelShellHeaderSize.Thin
                    v = 18.0F
                Case MASPanelShellHeaderSize.Compact
                    v = 22.0F
                Case MASPanelShellHeaderSize.Comfortable
                    v = 30.0F
                Case MASPanelShellHeaderSize.Hero
                    v = 38.0F
                Case Else
                    v = 26.0F
            End Select

            If density = MASPanelShellDensity.Compact Then v -= 2.0F
            If density = MASPanelShellDensity.Comfortable Then v += 2.0F

            Return Math.Max(12.0F, v)
        End Function

    End Class

End Namespace