Option Strict On
Option Explicit On

Imports System
Imports Nexamas.UI.General
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.Components

    ''' <summary>
    ''' Builds pixel-space layout rectangles for MASFilePickerControl.
    ''' </summary>
    ''' <remarks>
    ''' This builder is intentionally host-neutral.
    '''
    ''' It does not know about Overlay.
    ''' It does not know about Float.
    ''' It does not own focus, input, closing, modal behavior, or shield behavior.
    '''
    ''' Input:
    ''' - boundsPx must be physical pixels.
    ''' - dpi is normalized through PixelSnap.SafeDpi.
    '''
    ''' Output:
    ''' - All returned rectangles are physical pixels.
    ''' </remarks>
    Friend NotInheritable Class MASFilePickerLayoutBuilder

        Private Sub New()
        End Sub

        Friend Shared Function Build(boundsPx As SKRect,
                                     dpi As Single,
                                     isSaveMode As Boolean,
                                     Optional layoutScale As Single = 1.0F) As FilePickerLayoutInfo

            Dim info As New FilePickerLayoutInfo()

            If boundsPx.IsEmpty OrElse boundsPx.Width <= 1.0F OrElse boundsPx.Height <= 1.0F Then
                Return info
            End If

            Dim safeDpi As Single = PixelSnap.SafeDpi(dpi)
            Dim metricDpi As Single = safeDpi * NormalizeScale(layoutScale)

            Dim outerPad As Single = FilePickerTokens.OuterPad * metricDpi * FilePickerTokens.ShellOuterPadScale
            Dim shellSideInset As Single = Math.Max(FilePickerTokens.ShellSideInsetMin * safeDpi, outerPad * FilePickerTokens.ShellSideInsetOuterPadScale)

            Dim contentLeft As Single = boundsPx.Left + shellSideInset
            Dim contentRight As Single = boundsPx.Right - shellSideInset
            Dim contentTop As Single = boundsPx.Top + outerPad
            Dim contentBottom As Single = boundsPx.Bottom - outerPad

            If contentRight <= contentLeft + (1.0F * safeDpi) OrElse
               contentBottom <= contentTop + (1.0F * safeDpi) Then

                Return info
            End If

            info.ContentRect = New SKRect(
                contentLeft,
                contentTop,
                contentRight,
                contentBottom
            )

            info.TitleBarRect = SKRect.Empty

            Dim navigationH As Single = FilePickerTokens.NavigationBarHeight * metricDpi
            Dim browserTopGap As Single = FilePickerTokens.HeaderBottomGap * metricDpi * FilePickerTokens.BrowserTopGapScale

            info.NavigationBarRect = New SKRect(
                contentLeft,
                info.ContentRect.Top,
                contentRight,
                Math.Min(info.ContentRect.Bottom, info.ContentRect.Top + navigationH)
            )

            Dim footerH As Single = FilePickerTokens.FooterHeight * metricDpi
            Dim footerBottomInset As Single = outerPad * FilePickerTokens.FooterBottomInsetScale

            Dim footerBottom As Single = boundsPx.Bottom - footerBottomInset
            Dim footerTop As Single = footerBottom - footerH

            If footerTop < info.ContentRect.Top Then
                footerTop = info.ContentRect.Top
            End If

            If footerBottom > info.ContentRect.Bottom Then
                footerBottom = info.ContentRect.Bottom
            End If

            If footerBottom <= footerTop Then
                info.FooterVisualRect = SKRect.Empty
            Else
                info.FooterVisualRect = New SKRect(
                    contentLeft,
                    footerTop,
                    contentRight,
                    footerBottom
                )
            End If

            info.FileNameLabelRect = SKRect.Empty
            info.FileNameBoxRect = SKRect.Empty

            Dim footerTopLimit As Single =
                If(info.FooterVisualRect.IsEmpty, info.ContentRect.Bottom, info.FooterVisualRect.Top)

            If isSaveMode Then
                Dim fileLabelH As Single = FilePickerTokens.FileNameLabelHeight * metricDpi
                Dim fileLabelGap As Single = FilePickerTokens.FileNameLabelGap * metricDpi
                Dim fileRowH As Single = FilePickerTokens.FileNameRowHeight * metricDpi
                Dim fileRowGap As Single = FilePickerTokens.FileNameRowGap * metricDpi

                Dim totalFileBlockH As Single = fileLabelH + fileLabelGap + fileRowH
                Dim fileBlockBottom As Single = footerTopLimit - fileRowGap
                Dim fileBlockTop As Single = fileBlockBottom - totalFileBlockH

                If fileBlockTop > info.NavigationBarRect.Bottom Then
                    info.FileNameLabelRect = New SKRect(
                        contentLeft,
                        fileBlockTop,
                        contentRight,
                        fileBlockTop + fileLabelH
                    )

                    info.FileNameBoxRect = New SKRect(
                        contentLeft,
                        info.FileNameLabelRect.Bottom + fileLabelGap,
                        contentRight,
                        info.FileNameLabelRect.Bottom + fileLabelGap + fileRowH
                    )
                End If
            End If

            Dim footerTopGapPx As Single = FilePickerTokens.FooterTopGap * metricDpi * 0.22F

            Dim browserBottom As Single =
                If(isSaveMode AndAlso Not info.FileNameLabelRect.IsEmpty,
                   info.FileNameLabelRect.Top - footerTopGapPx,
                   footerTopLimit - footerTopGapPx)

            Dim browserTop As Single = info.NavigationBarRect.Bottom + browserTopGap

            If browserBottom < browserTop Then
                browserBottom = browserTop
            End If

            Dim minBrowserHeight As Single = FilePickerTokens.BodyMinHeight * metricDpi

            If browserBottom < browserTop + minBrowserHeight Then
                browserBottom = browserTop + minBrowserHeight
            End If

            If browserBottom > footerTopLimit - footerTopGapPx Then
                browserBottom = Math.Max(browserTop, footerTopLimit - footerTopGapPx)
            End If

            If browserBottom <= browserTop Then
                info.BrowserRect = SKRect.Empty
            Else
                info.BrowserRect = New SKRect(
                    contentLeft,
                    browserTop,
                    contentRight,
                    browserBottom
                )
            End If

            If Not info.FooterVisualRect.IsEmpty Then
                Dim footerInnerX As Single = FilePickerTokens.FooterInnerPadX * metricDpi
                Dim footerInnerY As Single = FilePickerTokens.FooterInnerPadY * metricDpi * FilePickerTokens.FooterInnerPadYScale

                Dim buttonsLeft As Single = info.FooterVisualRect.Left + footerInnerX
                Dim buttonsTop As Single = info.FooterVisualRect.Top + footerInnerY
                Dim buttonsRight As Single = info.FooterVisualRect.Right - footerInnerX
                Dim buttonsBottom As Single = info.FooterVisualRect.Bottom - footerInnerY

                If buttonsRight > buttonsLeft AndAlso buttonsBottom > buttonsTop Then
                    info.ButtonsBarRect = New SKRect(
                        buttonsLeft,
                        buttonsTop,
                        buttonsRight,
                        buttonsBottom
                    )
                End If
            End If

            If Not info.ButtonsBarRect.IsEmpty Then
                Dim footerLabelPadX As Single = FilePickerTokens.FooterLabelPadX * metricDpi
                Dim footerButtonAreaW As Single = ((FilePickerTokens.FooterButtonWidth * 2.0F) + FilePickerTokens.FooterButtonGap + FilePickerTokens.FooterButtonOuterInsetX + FilePickerTokens.FooterLabelSideGap) * metricDpi

                info.FooterLabelRect = New SKRect(
                    info.ButtonsBarRect.Left + footerButtonAreaW,
                    info.ButtonsBarRect.Top,
                    info.ButtonsBarRect.Right - footerLabelPadX,
                    info.ButtonsBarRect.Bottom
                )

                If info.FooterLabelRect.Right <= info.FooterLabelRect.Left + (FilePickerTokens.FooterLabelMinWidth * metricDpi) Then
                    info.FooterLabelRect = SKRect.Empty
                End If
            End If

            Return info
        End Function

        Private Shared Function NormalizeScale(value As Single) As Single
            If Single.IsNaN(value) OrElse Single.IsInfinity(value) OrElse value <= 0.0F Then Return 1.0F
            Return Math.Max(0.62F, Math.Min(1.22F, value))
        End Function

    End Class

End Namespace