'========================================================
' File: MASTileBox.vb  (SSOT + Fixes + Fluent SDK API)
'========================================================
Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.Windows.Forms
Imports Nexamas.UI.Controls
Imports Nexamas.UI.Composition
Imports Nexamas.UI.General
Imports Nexamas.UI.Layout
Imports Nexamas.UI.Motion
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.Scroll
Imports Nexamas.UI.SkiaScroll
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Values
Imports Nexamas.UI.Virtualization
Imports SkiaSharp


Namespace Nexamas.UI.Components

    Partial Public NotInheritable Class MASTileBox

        Friend Function SetBounds(bounds As SKRect) As MASTileBox
            Me.SetLocalLayoutBoundsInternal(bounds)
            Return Me
        End Function

        Friend Function SetBounds(x As Single,
                                  y As Single,
                                  width As Single,
                                  height As Single) As MASTileBox

            Me.SetLocalLayoutBoundsInternal(New SKRect(x, y, x + width, y + height))
            Return Me

        End Function

        Friend Function WithBounds(bounds As SKRect) As MASTileBox
            Return SetBounds(bounds)
        End Function

        Friend Function WithBounds(x As Single,
                                   y As Single,
                                   width As Single,
                                   height As Single) As MASTileBox

            Return SetBounds(x, y, width, height)

        End Function

        Private Function ComputeTileRect(index As Integer, offsetY As Single) As SKRect

            If index < 0 OrElse index >= _items.Count Then Return SKRect.Empty
            If _lastCols <= 0 OrElse _rowStepPx <= 0 OrElse _lastTileSize <= 1 Then Return SKRect.Empty
            If _lastWork.IsEmpty Then Return SKRect.Empty

            Dim cols As Integer = Math.Max(1, _lastCols)
            Dim row As Integer = index \ cols
            Dim col As Integer = index Mod cols

            Dim x As Single = _workLeftPx + _contentLeftPadPx + col * (_lastFoot + _lastGapX)
            Dim y As Single = _workTopPx + _contentTopPadPx + row * _rowStepPx - offsetY

            Return New SKRect(x, y, x + _lastTileSize, y + _lastTileSize)

        End Function

        Private Function HitTestIndex(ptPx As SKPoint) As Integer

            Dim total As Integer = _items.Count

            If total <= 0 Then Return -1
            If _lastWork.IsEmpty Then Return -1
            If _lastCols <= 0 OrElse _rowStepPx <= 0 Then Return -1

            Dim off As Single = If(_scroll Is Nothing, 0.0F, _scroll.Offset)

            If Single.IsNaN(off) OrElse Single.IsInfinity(off) OrElse off < 0 Then
                off = 0.0F
            End If

            If Not _lastWork.Contains(ptPx.X, ptPx.Y) Then Return -1

            Dim idx As Integer = _virtualization.ResolveGridHitIndex(
                ptPx:=ptPx,
                viewportRectPx:=_lastWork,
                gridLeftPx:=_workLeftPx + _contentLeftPadPx,
                gridTopPx:=_workTopPx + _contentTopPadPx,
                columnStepPx:=_lastFoot + _lastGapX,
                rowExtentPx:=_rowStepPx,
                viewportOffsetPx:=off,
                columnCount:=_lastCols,
                itemCount:=total)

            If idx < 0 OrElse idx >= total Then Return -1

            Dim rr As SKRect = ComputeTileRect(idx, off)

            If rr.IsEmpty Then Return -1
            If Not rr.Contains(ptPx.X, ptPx.Y) Then Return -1

            Return idx

        End Function

        Private Function ComputeRowStepPx(ctx As MASThemeContext) As Single

            Dim dpi As Single = PixelSnap.SafeDpi(If(ctx Is Nothing, 1.0F, ctx.Dpi))

            Dim stepPx As Single = _rowStepPx

            If stepPx <= 0 OrElse Single.IsNaN(stepPx) OrElse Single.IsInfinity(stepPx) Then
                Dim foot As Single = Math.Max(1.0F, _lastFoot)
                Dim gapY As Single = Math.Max(0.0F, _lastGapY)
                stepPx = foot + gapY
            End If

            Dim minStep As Single = 24.0F * dpi
            If stepPx < minStep Then stepPx = minStep

            Return stepPx

        End Function

        Private Sub ResetLayoutCache()

            StopSelectionMotion()
            _lastWork = SKRect.Empty
            _lastCols = 1
            _lastRows = 0
            _lastTileSize = 80.0F
            _lastShadowPad = 3.0F
            _lastFoot = 86.0F
            _lastGapX = 12.0F
            _lastGapY = 12.0F
            _lastContentH = 0.0F

            _rowStepPx = 0.0F
            _contentTopPadPx = 0.0F
            _contentLeftPadPx = 0.0F
            _workTopPx = 0.0F
            _workLeftPx = 0.0F

            _lastHost = SKRect.Empty
            _lastContent = SKRect.Empty
            _scrollHost = SKRect.Empty
            _lastVirtualizationPlan = Nothing

        End Sub

        Private Sub MarkTileLayoutChanged()
            If _layoutRevision = Integer.MaxValue Then
                _layoutRevision = 0
            Else
                _layoutRevision += 1
            End If

            _lastVirtualizationPlan = Nothing
        End Sub

        Friend Function GetPreferredLayoutSize(context As MASLayoutMeasureContext) As SKSize Implements IMASLayoutParticipant.GetPreferredLayoutSize
            Dim sizeContext As MASSizeContext = CreateSizeContext(context)
            Return MeasureIntrinsicSize(sizeContext, SizeIntent).DesiredSize
        End Function

        Friend Function GetMinLayoutSize(context As MASLayoutMeasureContext) As SKSize Implements IMASLayoutParticipant.GetMinLayoutSize
            Dim sizeContext As MASSizeContext = CreateSizeContext(context)
            Return MeasureIntrinsicSize(sizeContext, SizeIntent).MinSize
        End Function

        Friend Function MeasureIntrinsicSize(context As MASSizeContext, intent As MASSize) As MASSizeResult Implements IMASIntrinsicSizeContract.MeasureIntrinsicSize
            Dim safeContext As MASSizeContext = MASIntrinsicControlSizeMetrics.EnsureContext(context)
            Dim desired As SKSize = MASIntrinsicControlSizeMetrics.ResolveTileBoxDesiredSize(
                itemCount:=_items.Count,
                availableSize:=safeContext.AvailableSize)

            Return MASSizeResolver.FromMeasured(
                desiredSize:=desired,
                minSize:=MASIntrinsicControlSizeMetrics.ResolveTileBoxMinimumSize(),
                maxSize:=New SKSize(Single.PositiveInfinity, Single.PositiveInfinity),
                intent:=intent,
                context:=safeContext,
                canGrowWidth:=True,
                canGrowHeight:=True,
                contentInset:=MASIntrinsicControlSizeMetrics.CreateTileBoxContentInset(),
                visualOverflowInset:=MASIntrinsicControlSizeMetrics.CreateTileBoxVisualOverflowInset(),
                hitOverflowInset:=MASLayoutInset.Empty,
                isFallback:=False)
        End Function

        Friend Function GetDesiredScrollContentSize(context As MASSizeContext, viewportSize As SKSize) As SKSize Implements IMASScrollContentSizeContract.GetDesiredScrollContentSize
            Dim safeContext As MASSizeContext = MASIntrinsicControlSizeMetrics.EnsureContext(context)
            Dim viewport As SKSize = ResolveSafeViewportSize(safeContext, viewportSize)
            Dim profile As MASResponsiveLayoutProfile = MASResponsiveLayoutResolver.FromLogicalSize(safeContext.IntegrationContext, viewport, SizeIntent, MASTileBoxLayoutPlan.Thresholds)
            Dim plan As MASTileBoxLayoutPlan = MASTileBoxLayoutPlan.Create(profile)
            Dim planDpi As Single = If(plan Is Nothing OrElse plan.Dpi <= 0.0F, 1.0F, plan.Dpi)
            Dim viewportPx As New SKRect(0.0F, 0.0F, viewport.Width * planDpi, viewport.Height * planDpi)
            Dim metrics As IconGridLayout_Size80_EqualSpace.Metrics = IconGridLayout_Size80_EqualSpace.Compute(viewportPx, planDpi, _items.Count, plan)
            If metrics.ContentH <= 0.0F Then Return SKSize.Empty
            Return New SKSize(viewport.Width, metrics.ContentH / planDpi)
        End Function

        Private Function ResolveIntrinsicViewportSize(context As MASSizeContext) As SKSize
            Dim safeContext As MASSizeContext = MASIntrinsicControlSizeMetrics.EnsureContext(context)
            Return MASIntrinsicControlSizeMetrics.ResolveTileBoxDesiredSize(
                itemCount:=_items.Count,
                availableSize:=safeContext.AvailableSize)
        End Function

        Private Shared Function ResolveSafeViewportSize(context As MASSizeContext, viewportSize As SKSize) As SKSize
            Dim width As Single = viewportSize.Width
            Dim height As Single = viewportSize.Height
            If Single.IsNaN(width) OrElse Single.IsInfinity(width) OrElse width <= 0.0F Then
                width = If(context.AvailableSize.Width > 0.0F AndAlso Not Single.IsInfinity(context.AvailableSize.Width), context.AvailableSize.Width, TileTokens.IntrinsicFallbackWidthDip)
            End If
            If Single.IsNaN(height) OrElse Single.IsInfinity(height) OrElse height <= 0.0F Then
                height = If(context.AvailableSize.Height > 0.0F AndAlso Not Single.IsInfinity(context.AvailableSize.Height), context.AvailableSize.Height, TileTokens.IntrinsicFallbackHeightDip)
            End If
            Return New SKSize(width, height)
        End Function

        Private Shared Function CreateSizeContext(context As MASLayoutMeasureContext) As MASSizeContext
            If context Is Nothing Then
                Return New MASSizeContext(New SKSize(Single.PositiveInfinity, Single.PositiveInfinity))
            End If

            Return context.ToSizeContext()
        End Function

    End Class

End Namespace
