Option Strict On
Option Explicit On

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

Namespace Nexamas.UI.Components

    Friend NotInheritable Class MASTreeHitTestService

        Private Sub New()
        End Sub

        Friend Structure TreeRowHitInfo
            Friend Index As Integer
            Friend Node As MASTreeNode
            Friend Depth As Integer

            Friend IconRect As SKRect
            Friend BodyRect As SKRect
            Friend ExpanderRect As SKRect
            Friend TextRect As SKRect

            Friend HasExpander As Boolean
        End Structure

        Friend Shared Function BuildRowHitInfo(entry As MASTreeLayoutEngine.VisibleNodeEntry,
                                               index As Integer,
                                               metrics As ListRowLayout.RowMetrics,
                                               dpi As Single,
                                               showNodeIcons As Boolean,
                                               nodeIconSizeDip As Single,
                                               iconSizeMode As MASListTreeIconSizeMode) As TreeRowHitInfo

            Dim safeDpi As Single = PixelSnap.SafeDpi(dpi)

            Dim rowVisualInsetLeft As Single =
                CollectionTokens.TreeRowVisualInsetLeftDip * safeDpi

            Dim rowVisualInsetRight As Single =
                CollectionTokens.TreeRowVisualInsetRightDip * safeDpi

            Dim body As New SKRect(
                metrics.BodyRect.Left + rowVisualInsetLeft,
                metrics.BodyRect.Top,
                metrics.BodyRect.Right - rowVisualInsetRight,
                metrics.BodyRect.Bottom
            )

            If body.Right < body.Left + 1.0F Then
                body.Right = body.Left + 1.0F
            End If

            Dim contentLeft As Single =
                body.Left +
                (CollectionTokens.TreeContentPadLeftDip * safeDpi) +
                (entry.Depth * CollectionTokens.TreeIndentWidthDip * safeDpi)

            Dim expSize As Single =
                CollectionTokens.TreeExpanderBoxDip * safeDpi

            Dim expRect As New SKRect(
                contentLeft,
                body.MidY - (expSize * 0.5F),
                contentLeft + expSize,
                body.MidY + (expSize * 0.5F)
            )

            Dim nextLeft As Single =
                expRect.Right + (CollectionTokens.TreeExpanderGapDip * safeDpi)

            Dim iconRect As SKRect = SKRect.Empty

            If showNodeIcons Then
                Dim iconSizeDip As Single =
                    CollectionTokens.ClampIconSizeDip(nodeIconSizeDip, iconSizeMode)

                Dim iconSizePx As Single = iconSizeDip * safeDpi

                iconRect = New SKRect(
                    nextLeft,
                    body.MidY - (iconSizePx * 0.5F),
                    nextLeft + iconSizePx,
                    body.MidY + (iconSizePx * 0.5F)
                )

                nextLeft = iconRect.Right + (CollectionTokens.TreeIconTextGapDip * safeDpi)
            End If

            Dim textRight As Single =
                body.Right - (CollectionTokens.TreeContentPadRightDip * safeDpi)

            Dim textRect As New SKRect(
                nextLeft,
                body.Top,
                Math.Max(nextLeft, textRight),
                body.Bottom
            )

            Return New TreeRowHitInfo With {
                .Index = index,
                .Node = entry.Node,
                .Depth = entry.Depth,
                .BodyRect = body,
                .ExpanderRect = expRect,
                .IconRect = iconRect,
                .TextRect = textRect,
                .HasExpander = entry.Node.HasChildren
            }
        End Function

        Friend Shared Function HitTestRow(rows As IReadOnlyList(Of TreeRowHitInfo),
                                          ptPx As SKPoint) As Nullable(Of TreeRowHitInfo)

            If rows Is Nothing OrElse rows.Count = 0 Then Return Nothing

            For Each r As TreeRowHitInfo In rows
                If r.BodyRect.Contains(ptPx.X, ptPx.Y) Then
                    Return r
                End If
            Next

            Return Nothing
        End Function

    End Class

End Namespace