Option Strict On
Option Explicit On

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

Namespace Nexamas.UI.Components

    ''' <summary>
    ''' Shared Media toolbar geometry resolver. It owns title, floating strip, and command lane
    ''' sizing so controls only provide actions and labels.
    ''' </summary>
    Friend NotInheritable Class MASMediaToolbarLayoutResolver
        Private Sub New()
        End Sub

        Friend Shared Function ResolveTitleRect(toolbarRect As SKRect,
                                                dpi As Single,
                                                isRtl As Boolean,
                                                Optional chromeScale As Single = 1.0F) As SKRect
            If toolbarRect.Width <= 1.0F OrElse toolbarRect.Height <= 1.0F Then Return SKRect.Empty
            dpi = PixelSnap.SafeDpi(dpi)
            Dim unit As Single = ResolveChromeUnitPx(dpi, chromeScale)

            Dim widthDip As Single = toolbarRect.Width / unit
            Dim heightDip As Single = toolbarRect.Height / unit
            If heightDip <= MediaToolbarTokens.TitleCollapseHeightDip OrElse widthDip <= MediaToolbarTokens.TitleCollapseWidthDip Then Return SKRect.Empty

            Dim sideInset As Single = ResolvePinnedSideInsetPx(toolbarRect, dpi, chromeScale)
            Dim availableWidth As Single = Math.Max(0.0F, toolbarRect.Width - sideInset * 2.0F)
            If availableWidth <= MediaToolbarTokens.TitleAdaptiveMinWidthDip * unit Then Return SKRect.Empty

            Dim titleWidth As Single = Math.Min(availableWidth * MediaToolbarTokens.TitleWidthRatio,
                                                MediaToolbarTokens.TitleMaxWidthDip * unit)
            Dim maxAdaptiveWidth As Single = availableWidth * MediaToolbarTokens.TitleCompactMaxWidthRatio
            titleWidth = Math.Min(titleWidth, maxAdaptiveWidth)
            titleWidth = Math.Max(MediaToolbarTokens.TitleAdaptiveMinWidthDip * unit, titleWidth)
            titleWidth = Math.Min(titleWidth, availableWidth)

            Dim topInset As Single = ResolveTitleVerticalInsetPx(toolbarRect, dpi, chromeScale, True)
            Dim bottomInset As Single = ResolveTitleVerticalInsetPx(toolbarRect, dpi, chromeScale, False)
            If toolbarRect.Height - topInset - bottomInset <= 1.0F Then Return SKRect.Empty

            If isRtl Then
                Return New SKRect(toolbarRect.Right - sideInset - titleWidth,
                                  toolbarRect.Top + topInset,
                                  toolbarRect.Right - sideInset,
                                  toolbarRect.Bottom - bottomInset)
            End If

            Return New SKRect(toolbarRect.Left + sideInset,
                              toolbarRect.Top + topInset,
                              toolbarRect.Left + sideInset + titleWidth,
                              toolbarRect.Bottom - bottomInset)
        End Function

        Friend Shared Function ResolveCommandLane(toolbarRect As SKRect,
                                                  titleRect As SKRect,
                                                  dpi As Single,
                                                  isRtl As Boolean,
                                                  floating As Boolean,
                                                  Optional chromeScale As Single = 1.0F) As SKRect
            If toolbarRect.Width <= 1.0F OrElse toolbarRect.Height <= 1.0F Then Return SKRect.Empty
            dpi = PixelSnap.SafeDpi(dpi)
            Dim unit As Single = ResolveChromeUnitPx(dpi, chromeScale)

            Dim sideInset As Single = If(floating,
                                         MediaToolbarTokens.FloatingSideInsetDip * unit,
                                         ResolvePinnedSideInsetPx(toolbarRect, dpi, chromeScale))
            Dim commandLeft As Single = toolbarRect.Left + sideInset
            Dim commandRight As Single = toolbarRect.Right - sideInset

            If Not floating AndAlso titleRect.Width > 1.0F AndAlso titleRect.Height > 1.0F Then
                If isRtl Then
                    commandRight = titleRect.Left - ResolveGapPx(toolbarRect, dpi, chromeScale)
                Else
                    commandLeft = titleRect.Right + ResolveGapPx(toolbarRect, dpi, chromeScale)
                End If
            End If

            If commandRight <= commandLeft Then Return SKRect.Empty
            Return New SKRect(commandLeft, toolbarRect.Top, commandRight, toolbarRect.Bottom)
        End Function

        Friend Shared Function ResolveFloatingToolbarRect(stageRect As SKRect,
                                                          dpi As Single,
                                                          actionCount As Integer,
                                                          Optional groupBreakCount As Integer = 1,
                                                          Optional chromeScale As Single = 1.0F) As SKRect
            If stageRect.Width <= 1.0F OrElse stageRect.Height <= 1.0F Then Return SKRect.Empty
            dpi = PixelSnap.SafeDpi(dpi)
            Dim unit As Single = ResolveChromeUnitPx(dpi, chromeScale)

            If stageRect.Height / unit < MediaToolbarTokens.FloatingMinVisibleHeightDip Then Return SKRect.Empty

            Dim safeActionCount As Integer = Math.Max(0, actionCount)
            If safeActionCount = 0 Then Return SKRect.Empty

            Dim inset As Single = Math.Min(MediaToolbarTokens.FloatingInsetDip * unit, Math.Max(0.0F, stageRect.Width * 0.08F))
            Dim h As Single = Math.Min(MediaToolbarTokens.FloatingHeightDip * unit, Math.Max(1.0F, stageRect.Height - inset * 2.0F))
            If h <= 1.0F Then Return SKRect.Empty

            Dim safeGroupBreakCount As Integer = Math.Max(0, Math.Min(Math.Max(0, safeActionCount - 1), groupBreakCount))
            Dim sameGroupGapCount As Integer = Math.Max(0, safeActionCount - 1 - safeGroupBreakCount)
            Dim sideInset As Single = MediaToolbarTokens.FloatingSideInsetDip * unit

            Dim preferredContentWidth As Single = safeActionCount * MediaButtonTokens.CompactSizeDip * unit + _
                                                  sameGroupGapCount * MediaButtonTokens.CompactGapDip * unit + _
                                                  safeGroupBreakCount * MediaButtonTokens.CompactGroupGapDip * unit
            Dim minimumContentWidth As Single = safeActionCount * MediaButtonTokens.MicroCompactSizeDip * unit + _
                                                sameGroupGapCount * MediaButtonTokens.MicroCompactGapDip * unit + _
                                                safeGroupBreakCount * MediaButtonTokens.MicroCompactGroupGapDip * unit
            Dim maxWidth As Single = Math.Min(MediaToolbarTokens.FloatingMaxWidthDip * unit, stageRect.Width - inset * 2.0F)
            Dim minimumWidth As Single = minimumContentWidth + sideInset * 2.0F
            If maxWidth < minimumWidth - 0.5F Then Return SKRect.Empty

            Dim desiredWidth As Single = Math.Min(maxWidth, preferredContentWidth + sideInset * 2.0F)
            Dim w As Single = Math.Max(minimumWidth, desiredWidth)
            If w <= 1.0F Then Return SKRect.Empty

            Dim left As Single = stageRect.MidX - w / 2.0F
            If left < stageRect.Left + inset Then left = stageRect.Left + inset
            If left + w > stageRect.Right - inset Then left = stageRect.Right - inset - w
            Return PixelSnap.SnapRectHalf(New SKRect(left, stageRect.Top + inset, left + w, stageRect.Top + inset + h))
        End Function

        Private Shared Function ResolvePinnedSideInsetPx(toolbarRect As SKRect,
                                                         dpi As Single,
                                                         chromeScale As Single) As Single
            dpi = PixelSnap.SafeDpi(dpi)
            Dim unit As Single = ResolveChromeUnitPx(dpi, chromeScale)
            Dim widthDip As Single = toolbarRect.Width / unit
            If widthDip <= MediaContainerTokens.MinimalChromeWidthDip Then Return MediaToolbarTokens.MinimalSideInsetDip * unit
            If widthDip <= MediaContainerTokens.CompactWidthBreakpointDip Then Return MediaToolbarTokens.CompactSideInsetDip * unit
            Return MediaToolbarTokens.SideInsetDip * unit
        End Function

        Private Shared Function ResolveGapPx(toolbarRect As SKRect,
                                             dpi As Single,
                                             chromeScale As Single) As Single
            dpi = PixelSnap.SafeDpi(dpi)
            Dim unit As Single = ResolveChromeUnitPx(dpi, chromeScale)
            If toolbarRect.Width / unit <= MediaContainerTokens.CompactWidthBreakpointDip Then Return Math.Max(3.0F * unit, MediaToolbarTokens.GapDip * unit * 0.55F)
            Return MediaToolbarTokens.GapDip * unit
        End Function

        Private Shared Function ResolveTitleVerticalInsetPx(toolbarRect As SKRect,
                                                           dpi As Single,
                                                           chromeScale As Single,
                                                           top As Boolean) As Single
            dpi = PixelSnap.SafeDpi(dpi)
            Dim unit As Single = ResolveChromeUnitPx(dpi, chromeScale)
            Dim normal As Single = If(top, MediaToolbarTokens.TitleTopInsetDip, MediaToolbarTokens.TitleBottomInsetDip) * unit
            Dim maxInset As Single = Math.Max(0.0F, (toolbarRect.Height - 24.0F * unit) * 0.5F)
            Return Math.Min(normal, maxInset)
        End Function

        Private Shared Function ResolveChromeUnitPx(dpi As Single,
                                                    chromeScale As Single) As Single
            Dim safeDpi As Single = PixelSnap.SafeDpi(dpi)
            Return safeDpi * SanitizeChromeScale(chromeScale)
        End Function

        Private Shared Function SanitizeChromeScale(value As Single) As Single
            If Single.IsNaN(value) OrElse Single.IsInfinity(value) OrElse value <= 0.0F Then Return 1.0F
            Return Math.Min(1.24F, Math.Max(0.72F, value))
        End Function
    End Class

End Namespace
