Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports Nexamas.UI.Controls
Imports Nexamas.UI.Composition
Imports Nexamas.UI.General
Imports Nexamas.UI.Icons
Imports Nexamas.UI.Layout
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Values
Imports SkiaSharp
Imports Nexamas.UI.Architecture

Namespace Nexamas.UI.Components


    Partial Public Class MASToolbar
#Region "Render"

        Protected Overrides Sub Render(canvas As SKCanvas, ctx As MASThemeContext, pixelBounds As SKRect)
            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If pixelBounds.Width <= ToolbarTokens.MinimumUsablePixelSize OrElse pixelBounds.Height <= ToolbarTokens.MinimumUsablePixelSize Then Return

            Dim plan As MASToolbarLayoutPlan = MASToolbarLayoutPlan.Create(ctx, pixelBounds, SizeIntent, _horizontalPaddingDip, _verticalInsetDip, _gapDip, _itemHeightDip)
            _lastLayoutPlan = plan
            Dim dpi As Single = plan.Dpi
            Dim usesSlotContentRect As Boolean = False

            MASSurfaceMaterialComponentGateway.TryDrawToolbarSurface(
                canvas:=canvas,
                ctx:=ctx,
                boundsPx:=pixelBounds,
                materialKey:=_surfaceMaterialKey,
                dpi:=dpi,
                cornerRadiusPx:=plan.CornerRadiusPx,
                surfaceStrengthLevel:=3,
                borderStrengthLevel:=2,
                frameStrength:=MASSurfaceFrameStrength.Level3)

            _layout.BarRectPx = PixelSnap.SnapRectHalf(ResolveToolbarLayoutRectPx(pixelBounds, dpi, usesSlotContentRect))
            _layout.Slots = Array.Empty(Of MASToolbarLayoutSlot)()

            ApplySmartLayout(plan, usesSlotContentRect)
        End Sub

        Private Function ResolveToolbarLayoutRectPx(pixelBounds As SKRect,
                                                    dpi As Single,
                                                    ByRef usesSlotContentRect As Boolean) As SKRect

            usesSlotContentRect = False
            If dpi <= 0.0F Then Return pixelBounds

            Dim contentLogical As SKRect = GetLayoutContentBoundsLogicalInternal()
            If contentLogical.IsEmpty OrElse contentLogical.Width <= 1.0F OrElse contentLogical.Height <= 1.0F Then
                Return pixelBounds
            End If

            usesSlotContentRect = True
            Return New SKRect(
                contentLogical.Left * dpi,
                contentLogical.Top * dpi,
                contentLogical.Right * dpi,
                contentLogical.Bottom * dpi)
        End Function

        Private Sub ClearLayoutItemBuckets()
            _layoutLeftItems.Clear()
            _layoutRightItems.Clear()
            _layoutFillItems.Clear()
        End Sub

        Private Sub ApplySmartLayout(plan As MASToolbarLayoutPlan,
                                     layoutRectIsSlotContent As Boolean)
            If plan Is Nothing Then Return
            Dim dpi As Single = plan.Dpi
            If dpi <= 0.0F Then Return
            Dim metricDpi As Single = plan.MetricDpi

            ClearLayoutItemBuckets()

            For Each item As ToolbarEntry In _items
                If item IsNot Nothing AndAlso item.Control IsNot Nothing Then
                    item.Control.SetLocalLayoutBoundsInternal(SKRect.Empty)
                End If
            Next

            If _items.Count = 0 Then Return
            If _layout.BarRectPx.Width <= ToolbarTokens.MinimumUsablePixelSize OrElse _layout.BarRectPx.Height <= ToolbarTokens.MinimumUsablePixelSize Then Return

            Dim effectiveHorizontalPaddingPx As Single = If(layoutRectIsSlotContent, 0.0F, plan.HorizontalPaddingPx)
            Dim effectiveVerticalInsetPx As Single = If(layoutRectIsSlotContent, 0.0F, plan.VerticalInsetPx)

            Dim usableWidthPx As Single = _layout.BarRectPx.Width - (effectiveHorizontalPaddingPx * 2.0F)
            If usableWidthPx <= ToolbarTokens.MinimumUsablePixelSize Then Return

            Dim leftItems As List(Of ToolbarEntry) = _layoutLeftItems
            Dim rightItems As List(Of ToolbarEntry) = _layoutRightItems
            Dim fillItems As List(Of ToolbarEntry) = _layoutFillItems

            For Each item As ToolbarEntry In _items
                If Not IsEntryVisible(item) Then Continue For

                If item.Sizing = MASToolbarItemSizing.Fill Then
                    fillItems.Add(item)
                ElseIf item.Alignment = MASToolbarItemAlignment.Right Then
                    rightItems.Add(item)
                Else
                    leftItems.Add(item)
                End If
            Next

            Dim fixedLeftPx As Single = SumFixedWidthPx(leftItems, metricDpi)
            Dim fixedRightPx As Single = SumFixedWidthPx(rightItems, metricDpi)

            Dim totalVisibleCount As Integer = leftItems.Count + rightItems.Count + fillItems.Count
            If totalVisibleCount = 0 Then Return

            Dim totalGapPx As Single = Math.Max(0, totalVisibleCount - 1) * plan.GapPx
            Dim remainingPx As Single = usableWidthPx - fixedLeftPx - fixedRightPx - totalGapPx

            If remainingPx < 0.0F Then
                remainingPx = 0.0F
            End If

            Dim fillWidthPx As Single = 0.0F
            If fillItems.Count > 0 Then
                fillWidthPx = remainingPx / fillItems.Count
            End If

            Dim itemHeightPx As Single = Math.Min(
                plan.ItemHeightPx,
                _layout.BarRectPx.Height - (effectiveVerticalInsetPx * 2.0F)
            )

            If itemHeightPx <= ToolbarTokens.MinimumUsablePixelSize Then Return

            Dim yPx As Single = _layout.BarRectPx.Top + ((_layout.BarRectPx.Height - itemHeightPx) * 0.5F)
            Dim leftX As Single = _layout.BarRectPx.Left + effectiveHorizontalPaddingPx
            Dim rightX As Single = _layout.BarRectPx.Right - effectiveHorizontalPaddingPx

            For Each item As ToolbarEntry In leftItems
                leftX = PlaceLeftItem(item, leftX, yPx, itemHeightPx, dpi, metricDpi, plan.GapPx, rightX)
            Next

            For i As Integer = rightItems.Count - 1 To 0 Step -1
                rightX = PlaceRightItem(rightItems(i), rightX, yPx, itemHeightPx, dpi, metricDpi, plan.GapPx, leftX)
            Next

            For Each item As ToolbarEntry In fillItems
                Dim widthPx As Single = ClampWidthPx(fillWidthPx, item.MinWidthDip, item.MaxWidthDip, metricDpi)

                If widthPx <= ToolbarTokens.MinimumUsablePixelSize Then
                    item.Control.SetLocalLayoutBoundsInternal(SKRect.Empty)
                    Continue For
                End If

                If leftX + widthPx > rightX + ToolbarTokens.PixelOverlapTolerance Then
                    item.Control.SetLocalLayoutBoundsInternal(SKRect.Empty)
                    Continue For
                End If

                Dim rectPx As SKRect = PixelSnap.SnapRectHalf(New SKRect(
                    leftX,
                    yPx,
                    leftX + widthPx,
                    yPx + itemHeightPx
                ))

                SetControlBoundsFromPixelRect(item.Control, rectPx, dpi)

                leftX = rectPx.Right + plan.GapPx
            Next
        End Sub

        Private Shared Function IsEntryVisible(item As ToolbarEntry) As Boolean
            If item Is Nothing OrElse item.Control Is Nothing Then Return False
            Return item.Control.Visible
        End Function

        Private Function SumFixedWidthPx(items As List(Of ToolbarEntry),
                                         metricDpi As Single) As Single
            Dim result As Single = 0.0F
            Dim safeMetricDpi As Single = If(metricDpi > 0.0F, metricDpi, 1.0F)

            For Each item As ToolbarEntry In items
                If Not IsEntryVisible(item) Then Continue For
                result += Math.Max(0.0F, item.WidthDip) * safeMetricDpi
            Next

            Return result
        End Function

        Private Function PlaceLeftItem(item As ToolbarEntry,
                                       xPx As Single,
                                       yPx As Single,
                                       heightPx As Single,
                                       dpi As Single,
                                       metricDpi As Single,
                                       gapPx As Single,
                                       maxRightPx As Single) As Single

            If Not IsEntryVisible(item) Then Return xPx

            Dim widthPx As Single = Math.Max(0.0F, item.WidthDip) * Math.Max(0.01F, metricDpi)

            If widthPx <= ToolbarTokens.MinimumUsablePixelSize Then
                item.Control.SetLocalLayoutBoundsInternal(SKRect.Empty)
                Return xPx
            End If

            If xPx + widthPx > maxRightPx + ToolbarTokens.PixelOverlapTolerance Then
                item.Control.SetLocalLayoutBoundsInternal(SKRect.Empty)
                Return xPx
            End If

            Dim rectPx As SKRect = PixelSnap.SnapRectHalf(New SKRect(
                xPx,
                yPx,
                xPx + widthPx,
                yPx + heightPx
            ))

            SetControlBoundsFromPixelRect(item.Control, rectPx, dpi)

            Return rectPx.Right + gapPx
        End Function

        Private Function PlaceRightItem(item As ToolbarEntry,
                                        rightPx As Single,
                                        yPx As Single,
                                        heightPx As Single,
                                        dpi As Single,
                                        metricDpi As Single,
                                        gapPx As Single,
                                        minLeftPx As Single) As Single

            If Not IsEntryVisible(item) Then Return rightPx

            Dim widthPx As Single = Math.Max(0.0F, item.WidthDip) * Math.Max(0.01F, metricDpi)

            If widthPx <= ToolbarTokens.MinimumUsablePixelSize Then
                item.Control.SetLocalLayoutBoundsInternal(SKRect.Empty)
                Return rightPx
            End If

            Dim leftPx As Single = rightPx - widthPx

            If leftPx < minLeftPx - ToolbarTokens.PixelOverlapTolerance Then
                item.Control.SetLocalLayoutBoundsInternal(SKRect.Empty)
                Return rightPx
            End If

            Dim rectPx As SKRect = PixelSnap.SnapRectHalf(New SKRect(
                leftPx,
                yPx,
                rightPx,
                yPx + heightPx
            ))

            SetControlBoundsFromPixelRect(item.Control, rectPx, dpi)

            Return rectPx.Left - gapPx
        End Function

        Private Shared Sub SetControlBoundsFromPixelRect(control As MASControlBase,
                                                         rectPx As SKRect,
                                                         dpi As Single)

            If control Is Nothing Then Return
            If dpi <= 0.0F Then Return

            If rectPx.Width <= ToolbarTokens.MinimumUsablePixelSize OrElse rectPx.Height <= ToolbarTokens.MinimumUsablePixelSize Then
                control.SetLocalLayoutBoundsInternal(SKRect.Empty)
                Return
            End If

            control.SetLocalLayoutBoundsInternal(New SKRect(
                rectPx.Left / dpi,
                rectPx.Top / dpi,
                rectPx.Right / dpi,
                rectPx.Bottom / dpi
            ))
        End Sub

        Private Shared Function ClampWidthPx(valuePx As Single,
                                             minWidthDip As Single,
                                             maxWidthDip As Single,
                                             metricDpi As Single) As Single

            Dim safeMetricDpi As Single = If(metricDpi > 0.0F, metricDpi, 1.0F)
            Dim result As Single = Math.Max(0.0F, valuePx)

            If minWidthDip > 0.0F Then
                result = Math.Max(minWidthDip * safeMetricDpi, result)
            End If

            If maxWidthDip > 0.0F Then
                result = Math.Min(maxWidthDip * safeMetricDpi, result)
            End If

            Return result
        End Function

#End Region

    End Class

End Namespace
