Option Strict On
Option Explicit On

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

Namespace Nexamas.UI.Components

    ''' <summary>
    ''' Reusable horizontal MediaButton strip layout. Owners provide command specs and
    ''' the available lane; this resolver decides tile profile, spacing, compact fallback,
    ''' RTL/floating alignment, and returns render-ready button models.
    ''' </summary>
    Friend NotInheritable Class MASMediaButtonStripLayoutResolver
        Private Sub New()
        End Sub

        Friend Shared Function Build(specs As MASMediaButtonSpec(),
                                     laneRect As SKRect,
                                     hostRect As SKRect,
                                     dpi As Single,
                                     isRtl As Boolean,
                                     floating As Boolean,
                                     Optional nonCompactVerticalOffsetPx As Single = 0.0F,
                                     Optional chromeScale As Single = 1.0F) As MASMediaButtonModel()
            If specs Is Nothing OrElse specs.Length = 0 Then Return Array.Empty(Of MASMediaButtonModel)()
            If laneRect.Width <= 1.0F OrElse laneRect.Height <= 1.0F Then Return Array.Empty(Of MASMediaButtonModel)()
            If hostRect.Width <= 1.0F OrElse hostRect.Height <= 1.0F Then hostRect = laneRect

            dpi = PixelSnap.SafeDpi(dpi)

            Dim profile As MASMediaButtonStripLayoutProfile = ResolveProfile(specs, laneRect.Width, hostRect.Height, dpi, floating, chromeScale)
            Dim required As Single = Measure(specs, profile)
            If required <= 1.0F Then Return Array.Empty(Of MASMediaButtonModel)()
            If required > laneRect.Width + 0.5F Then Return Array.Empty(Of MASMediaButtonModel)()

            Dim y As Single = hostRect.MidY - profile.Height / 2.0F
            If Not floating AndAlso Not profile.Compact Then
                y += Math.Max(0.0F, nonCompactVerticalOffsetPx)
            End If

            Dim cursor As Single
            If floating Then
                cursor = hostRect.MidX - required / 2.0F
                If cursor < laneRect.Left Then cursor = laneRect.Left
                If cursor + required > laneRect.Right Then cursor = laneRect.Right - required
            ElseIf isRtl Then
                cursor = laneRect.Left
            Else
                cursor = laneRect.Right - required
            End If
            If cursor < laneRect.Left Then cursor = laneRect.Left

            Dim result As New List(Of MASMediaButtonModel)()
            Dim lastGroup As Integer = Integer.MinValue
            For i As Integer = 0 To specs.Length - 1
                Dim spec As MASMediaButtonSpec = specs(i)
                If i > 0 Then cursor += If(spec.GroupIndex = lastGroup, profile.Gap, profile.GroupGap)

                Dim rect As New SKRect(cursor, y, cursor + profile.Width, y + profile.Height)
                If rect.Right > laneRect.Right + 0.5F Then Exit For

                result.Add(New MASMediaButtonModel(spec.ActionId, spec.Text, rect, profile.Compact, spec.GroupIndex, spec.GlyphKind))
                cursor = rect.Right
                lastGroup = spec.GroupIndex
            Next

            Return result.ToArray()
        End Function

        Friend Shared Function ResolveProfile(specs As MASMediaButtonSpec(),
                                             availableWidth As Single,
                                             hostHeight As Single,
                                             dpi As Single,
                                             floating As Boolean,
                                             Optional chromeScale As Single = 1.0F) As MASMediaButtonStripLayoutProfile
            dpi = PixelSnap.SafeDpi(dpi)
            Dim unit As Single = ResolveChromeUnitPx(dpi, chromeScale)

            If floating OrElse hostHeight <= MediaButtonTokens.CompactToolbarHeightThresholdDip * unit Then
                Dim compactOnly As MASMediaButtonStripLayoutProfile = CreateCompactProfile(dpi, chromeScale)
                If Measure(specs, compactOnly) <= availableWidth Then Return compactOnly

                Dim tightOnly As MASMediaButtonStripLayoutProfile = CreateTightCompactProfile(dpi, chromeScale)
                If Measure(specs, tightOnly) <= availableWidth Then Return tightOnly

                Return CreateMicroCompactProfile(dpi, chromeScale)
            End If

            Dim preferred As New MASMediaButtonStripLayoutProfile(
                width:=MediaButtonTokens.PreferredTileWidthDip * unit,
                height:=MediaButtonTokens.PreferredTileHeightDip * unit,
                gap:=MediaButtonTokens.PreferredTileGapDip * unit,
                groupGap:=MediaButtonTokens.PreferredTileGroupGapDip * unit,
                compact:=False)
            If Measure(specs, preferred) <= availableWidth Then Return preferred

            Dim dense As New MASMediaButtonStripLayoutProfile(
                width:=MediaButtonTokens.DenseTileWidthDip * unit,
                height:=MediaButtonTokens.DenseTileHeightDip * unit,
                gap:=MediaButtonTokens.DenseTileGapDip * unit,
                groupGap:=MediaButtonTokens.DenseTileGroupGapDip * unit,
                compact:=False)
            If Measure(specs, dense) <= availableWidth Then Return dense

            Dim compact As MASMediaButtonStripLayoutProfile = CreateCompactProfile(dpi, chromeScale)
            If Measure(specs, compact) <= availableWidth Then Return compact

            Dim tight As MASMediaButtonStripLayoutProfile = CreateTightCompactProfile(dpi, chromeScale)
            If Measure(specs, tight) <= availableWidth Then Return tight

            Return CreateMicroCompactProfile(dpi, chromeScale)
        End Function

        Friend Shared Function Measure(specs As MASMediaButtonSpec(),
                                       profile As MASMediaButtonStripLayoutProfile) As Single
            If specs Is Nothing OrElse specs.Length = 0 Then Return 0.0F
            Dim width As Single = 0.0F
            Dim lastGroup As Integer = Integer.MinValue
            For i As Integer = 0 To specs.Length - 1
                If i > 0 Then width += If(specs(i).GroupIndex = lastGroup, profile.Gap, profile.GroupGap)
                width += profile.Width
                lastGroup = specs(i).GroupIndex
            Next
            Return width
        End Function


        Friend Shared Function CountGroupBreaks(specs As MASMediaButtonSpec()) As Integer
            If specs Is Nothing OrElse specs.Length < 2 Then Return 0
            Dim breaks As Integer = 0
            Dim lastGroup As Integer = specs(0).GroupIndex
            For i As Integer = 1 To specs.Length - 1
                If specs(i).GroupIndex <> lastGroup Then breaks += 1
                lastGroup = specs(i).GroupIndex
            Next
            Return breaks
        End Function

        Friend Shared Function CreateCompactProfile(dpi As Single, Optional chromeScale As Single = 1.0F) As MASMediaButtonStripLayoutProfile
            dpi = PixelSnap.SafeDpi(dpi)
            Dim unit As Single = ResolveChromeUnitPx(dpi, chromeScale)
            Return New MASMediaButtonStripLayoutProfile(
                width:=MediaButtonTokens.CompactSizeDip * unit,
                height:=MediaButtonTokens.CompactSizeDip * unit,
                gap:=MediaButtonTokens.CompactGapDip * unit,
                groupGap:=MediaButtonTokens.CompactGroupGapDip * unit,
                compact:=True)
        End Function

        Friend Shared Function CanFitAll(specs As MASMediaButtonSpec(),
                                         availableWidth As Single,
                                         hostHeight As Single,
                                         dpi As Single,
                                         floating As Boolean,
                                         Optional chromeScale As Single = 1.0F) As Boolean
            If specs Is Nothing OrElse specs.Length = 0 Then Return False
            If availableWidth <= 1.0F OrElse hostHeight <= 1.0F Then Return False
            Dim profile As MASMediaButtonStripLayoutProfile = ResolveProfile(specs, availableWidth, hostHeight, dpi, floating, chromeScale)
            Return Measure(specs, profile) <= availableWidth + 0.5F
        End Function

        Friend Shared Function CreateTightCompactProfile(dpi As Single, Optional chromeScale As Single = 1.0F) As MASMediaButtonStripLayoutProfile
            dpi = PixelSnap.SafeDpi(dpi)
            Dim unit As Single = ResolveChromeUnitPx(dpi, chromeScale)
            Return New MASMediaButtonStripLayoutProfile(
                width:=MediaButtonTokens.TightCompactSizeDip * unit,
                height:=MediaButtonTokens.TightCompactSizeDip * unit,
                gap:=MediaButtonTokens.TightCompactGapDip * unit,
                groupGap:=MediaButtonTokens.TightCompactGroupGapDip * unit,
                compact:=True)
        End Function

        Friend Shared Function CreateMicroCompactProfile(dpi As Single, Optional chromeScale As Single = 1.0F) As MASMediaButtonStripLayoutProfile
            dpi = PixelSnap.SafeDpi(dpi)
            Dim unit As Single = ResolveChromeUnitPx(dpi, chromeScale)
            Return New MASMediaButtonStripLayoutProfile(
                width:=MediaButtonTokens.MicroCompactSizeDip * unit,
                height:=MediaButtonTokens.MicroCompactSizeDip * unit,
                gap:=MediaButtonTokens.MicroCompactGapDip * unit,
                groupGap:=MediaButtonTokens.MicroCompactGroupGapDip * unit,
                compact:=True)
        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
