Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.General
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.Components.DropDownMenu

    Friend NotInheritable Class DropDownMenuRenderer
        Implements IDisposable

        Private ReadOnly _icon As New SKPaint With {
            .IsAntialias = True,
            .IsDither = True,
            .Style = SKPaintStyle.Stroke,
            .StrokeCap = SKStrokeCap.Round,
            .StrokeJoin = SKStrokeJoin.Round
        }

        Private Shared ReadOnly _contract As MASResolvedComponentContract =
    MASArchitectureRuntime.ResolveContractOrThrow(GetType(MASDropDownMenuFloatContract))

        Private Shared ReadOnly _scope As MASContractRenderScope =
    MASArchitectureRuntime.CreateRenderScopeForComponent(Of MASDropDownMenuFloatContract)(
        NameOf(DropDownMenuRenderer),
        _contract,
        MASRenderLayer.Surface,
        MASRenderLayer.FloatingChrome,
        MASRenderLayer.Shadow,
        MASRenderLayer.Text,
        MASRenderLayer.Hover,
        MASRenderLayer.Pressed,
        MASRenderLayer.Focus)

        Private ReadOnly _frameSurface As New MASSurfaceFramePainter()
        Private ReadOnly _interactionResolver As New DropDownMenuInteractionResolver()

        Private _disposed As Boolean

        Public Sub Dispose() Implements IDisposable.Dispose
            If _disposed Then Return
            _disposed = True

            Try
                _icon.Dispose()
            Catch masCaughtException1 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(masCaughtException1)
            End Try

            Try
                _frameSurface.Dispose()
            Catch masCaughtException2 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(masCaughtException2)
            End Try
        End Sub

        Friend Sub Render(canvas As SKCanvas,
                          ctx As MASThemeContext,
                          dpi As Single,
                          m As DropDownMenuState)

            If _disposed Then Return
            If canvas Is Nothing OrElse ctx Is Nothing OrElse m Is Nothing Then Return

            _scope.RequireAnyLayer(
                MASRenderLayer.Surface,
                MASRenderLayer.FloatingChrome,
                MASRenderLayer.Shadow,
                MASRenderLayer.Text)

            dpi = PixelSnap.SafeDpi(dpi)

            If m.IsOpen Then
                DrawPanels(canvas, ctx, dpi, m)
            End If
        End Sub

        Private Sub DrawPanels(canvas As SKCanvas,
                               ctx As MASThemeContext,
                               dpi As Single,
                               m As DropDownMenuState)

            If canvas Is Nothing OrElse ctx Is Nothing OrElse m Is Nothing Then Return
            If m.PanelRects Is Nothing OrElse m.PanelRects.Count = 0 Then Return

            Dim appTheme As IMASAppTheme = ctx.Theme
            If appTheme Is Nothing Then Return

            Dim menuTheme As IMASMenuTheme = MASAppThemeContracts.Families(appTheme).Menu
            If menuTheme Is Nothing Then Return

            Dim padX As Single = MenuTokens.DropDownPanelPadX * dpi
            Dim padY As Single = MenuTokens.DropDownPanelPadY * dpi
            Dim rowH As Single = MenuTokens.DropDownRowHeight * dpi
            Dim sepH As Single = MenuTokens.DropDownSeparatorHeight * dpi
            Dim glyphReserve As Single = MenuTokens.DropDownRightGlyphReserve * dpi

            Dim mm As MASSurfaceInteractionRing.Metrics = MASSurfaceInteractionRing.GetMetrics(dpi)

            For level As Integer = 0 To m.PanelRects.Count - 1
                Dim pr As SKRect = m.PanelRects(level)

                If pr.IsEmpty OrElse pr.Width <= 1.0F OrElse pr.Height <= 1.0F Then Continue For

                pr = MASSurfaceInteractionRing.SnapBaseRect(pr, mm)

                If pr.IsEmpty OrElse pr.Width <= 1.0F OrElse pr.Height <= 1.0F Then Continue For

                Dim panelCorner As Single =
                    MASRadiusResolver.ResolveSurfacePx(
                        contract:=_contract,
                        rPx:=pr,
                        dpi:=dpi)

                panelCorner = PixelSnap.SafeRadius(panelCorner)

                Dim rows As List(Of MASDropDownMenuNode) = ResolveRowsForLevel(m, level)
                Dim firstAction As Integer = FindFirstNonSeparatorRow(rows)
                Dim lastAction As Integer = FindLastNonSeparatorRow(rows)

                Dim frameSpec As MASSurfaceFrameSpec =
                    BuildMenuFrameSpec(menuTheme.Surface)

                If Not _frameSurface.TryDrawSwitchableFloatingPopupSurface(
                    canvas:=canvas,
                    ctx:=ctx,
                    r:=pr,
                    radius:=panelCorner,
                    dpi:=dpi) Then

                    _frameSurface.Draw(
                        canvas:=canvas,
                        r:=pr,
                        radius:=panelCorner,
                        dpi:=dpi,
                        spec:=frameSpec)
                End If

                canvas.Save()

                Try
                    Dim rrPanel As New SKRoundRect()

                    rrPanel.SetRectRadii(
                        rect:=pr,
                        radii:=New SKPoint() {
                            New SKPoint(panelCorner, panelCorner),
                            New SKPoint(panelCorner, panelCorner),
                            New SKPoint(panelCorner, panelCorner),
                            New SKPoint(panelCorner, panelCorner)
                        })

                    canvas.ClipRoundRect(rrPanel)

                    Dim scrollOffset As Single = m.GetScrollOffsetPx(level)
                    Dim visibleTop As Single = pr.Top + padY
                    Dim visibleBottom As Single = pr.Bottom - padY
                    Dim y As Single = visibleTop - scrollOffset

                    For i As Integer = 0 To rows.Count - 1
                        Dim row As MASDropDownMenuNode = rows(i)

                        If row Is Nothing Then
                            y += rowH
                            Continue For
                        End If

                        If row.Kind = MASDropDownMenuNodeKind.Separator Then
                            Dim separatorBottom As Single = y + sepH

                            If separatorBottom >= visibleTop AndAlso y <= visibleBottom Then
                                DrawSeparator(canvas, pr, y, dpi, menuTheme)
                            End If

                            y += sepH
                            Continue For
                        End If

                        Dim rowTop As Single = y
                        Dim rowBot As Single = y + rowH

                        If rowBot < visibleTop Then
                            y += rowH
                            Continue For
                        End If

                        If rowTop > visibleBottom Then
                            Exit For
                        End If

                        Dim isFirst As Boolean = (i = firstAction)
                        Dim isLast As Boolean = (i = lastAction)

                        Dim overlayRowRect As SKRect =
                            BuildMenuRowOverlayRect(
                                panelRect:=pr,
                                rowTop:=rowTop,
                                rowBottom:=rowBot,
                                isFirst:=isFirst,
                                isLast:=isLast,
                                dpi:=dpi,
                                separatorHeight:=sepH)

                        Dim rowDisabled As Boolean = Not m.Enabled OrElse Not row.Enabled

                        Dim rowState As MASInteractionState =
                            If(rowDisabled,
                               MASInteractionState.Disabled,
                               _interactionResolver.ResolveRowState(
                                   m:=m,
                                   level:=level,
                                   rowIndex:=i))

                        If _interactionResolver.ShouldDrawRowOverlay(rowState) Then
                            _interactionResolver.DrawRowOverlay(
                                canvas:=canvas,
                                ctx:=ctx,
                                dpi:=dpi,
                                r:=overlayRowRect,
                                panelCornerPx:=panelCorner,
                                isFirst:=isFirst,
                                isLast:=isLast,
                                state:=rowState,
                                isDisabled:=Not m.Enabled,
                                itemSurface:=menuTheme.ItemSurface)
                        End If

                        DrawContentMotionPulse(
                            canvas:=canvas,
                            ctx:=ctx,
                            dpi:=dpi,
                            m:=m,
                            level:=level,
                            rowIndex:=i,
                            r:=overlayRowRect,
                            radius:=Math.Max(4.0F * dpi, Math.Min(panelCorner, overlayRowRect.Height * 0.36F)),
                            menuTheme:=menuTheme)

                        Dim needsRightGlyph As Boolean =
                            row.Kind = MASDropDownMenuNodeKind.SubMenu OrElse row.IsChecked

                        Dim rightReserve As Single =
                            If(needsRightGlyph, glyphReserve, 0.0F)

                        Dim shortcutReserve As Single =
                            If(String.IsNullOrWhiteSpace(row.ShortcutText), 0.0F, 72.0F * dpi)

                        Dim textBounds As SKRect =
                            PixelSnap.SnapRectHalf(
                                New SKRect(
                                    pr.Left + padX,
                                    rowTop,
                                    pr.Right - padX - rightReserve - shortcutReserve,
                                    rowBot))

                        Dim shortcutBounds As SKRect =
                            PixelSnap.SnapRectHalf(
                                New SKRect(
                                    Math.Max(pr.Left + padX, pr.Right - padX - rightReserve - shortcutReserve),
                                    rowTop,
                                    pr.Right - padX - rightReserve,
                                    rowBot))

                        DrawRowText(
                            canvas:=canvas,
                            ctx:=ctx,
                            dpi:=dpi,
                            rowState:=rowState,
                            menuEnabled:=Not rowDisabled,
                            menuTheme:=menuTheme,
                            row:=row,
                            bounds:=textBounds)

                        DrawShortcutText(
                            canvas:=canvas,
                            ctx:=ctx,
                            dpi:=dpi,
                            rowState:=rowState,
                            menuEnabled:=Not rowDisabled,
                            menuTheme:=menuTheme,
                            text:=row.ShortcutText,
                            bounds:=shortcutBounds)

                        If row.Kind = MASDropDownMenuNodeKind.SubMenu Then
                            DrawChevron(canvas, ctx, dpi, rowState, Not rowDisabled, menuTheme, pr, rowTop, rowH, padX)
                        ElseIf row.IsChecked Then
                            DrawCheckMark(canvas, ctx, dpi, rowState, Not rowDisabled, menuTheme, pr, rowTop, rowH, padX)
                        End If

                        y += rowH
                    Next

                    DrawScrollEdgeIndicators(
                        canvas:=canvas,
                        panelRect:=pr,
                        dpi:=dpi,
                        menuTheme:=menuTheme,
                        scrollOffsetPx:=scrollOffset,
                        contentHeightPx:=ResolveContentHeightForLevel(m, level))

                Finally
                    canvas.Restore()
                End Try
            Next
        End Sub

        Private Shared Sub DrawContentMotionPulse(canvas As SKCanvas,
                                                           ctx As MASThemeContext,
                                                           dpi As Single,
                                                           m As DropDownMenuState,
                                                           level As Integer,
                                                           rowIndex As Integer,
                                                           r As SKRect,
                                                           radius As Single,
                                                           menuTheme As IMASMenuTheme)
            If canvas Is Nothing OrElse ctx Is Nothing OrElse m Is Nothing OrElse menuTheme Is Nothing Then Return
            If Not m.HasContentMotion Then Return
            If m.ContentMotionLevel <> level OrElse m.ContentMotionRowIndex <> rowIndex Then Return
            If r.Width <= 1.0F OrElse r.Height <= 1.0F Then Return

            Dim progress As Single = Math.Max(0.0F, Math.Min(1.0F, m.ContentMotionProgress))
            If progress >= 1.0F Then Return

            Dim strength As Single = 1.0F - progress
            Dim alpha As Byte = CByte(Math.Max(0, Math.Min(255, CInt(Math.Round(42.0R * CDbl(strength))))))
            If alpha <= 0 Then Return

            Dim pulseRect As SKRect = PixelSnap.SnapRectHalf(New SKRect(r.Left + (2.0F * dpi), r.Top + (1.0F * dpi), r.Right - (2.0F * dpi), r.Bottom - (1.0F * dpi)))
            If pulseRect.Width <= 1.0F OrElse pulseRect.Height <= 1.0F Then Return

            Dim pulseColor As SKColor = menuTheme.ItemText.Icon.WithAlpha(alpha)
            Using paint As New SKPaint With {
                .IsAntialias = True,
                .IsDither = True,
                .Style = SKPaintStyle.Fill,
                .Color = pulseColor,
                .BlendMode = SKBlendMode.SrcOver
            }
                canvas.DrawRoundRect(pulseRect, radius, radius, paint)
            End Using
        End Sub

        Private Shared Function BuildMenuFrameSpec(surface As MASMenuSurfaceTheme) As MASSurfaceFrameSpec

            Dim topCol As SKColor =
                ColorMath.Mix(surface.SurfaceTop, surface.SurfaceMid, 0.06F)

            Dim midCol As SKColor = surface.SurfaceMid

            Dim botCol As SKColor =
                ColorMath.Mix(
                    surface.SurfaceBot,
                    ColorMath.TowardBlack(surface.SurfaceBot, 0.08F),
                    0.12F)

            Dim washCol As SKColor =
                ColorMath.Mix(surface.SurfaceMid, surface.SurfaceTop, 0.04F)

            Dim outerBorder As SKColor =
                ColorMath.WithAlpha(
                    ColorMath.Mix(surface.BorderOuter, surface.SurfaceMid, 0.18F),
                    44)

            Dim innerBorder As SKColor =
                ColorMath.WithAlpha(
                    ColorMath.Mix(surface.BorderInner, surface.SurfaceTop, 0.12F),
                    10)

            Dim hairline As SKColor =
                ColorMath.WithAlpha(
                    ColorMath.Mix(surface.BorderOuter, surface.BorderInner, 0.18F),
                    36)

            Dim liftTop As SKColor =
                ColorMath.WithAlpha(
                    ColorMath.Mix(surface.SurfaceTop, surface.SurfaceMid, 0.12F),
                    18)

            Dim liftMid As SKColor =
                ColorMath.WithAlpha(
                    ColorMath.Mix(surface.SurfaceTop, surface.SurfaceMid, 0.22F),
                    10)

            Return New MASSurfaceFrameSpec With {
                .Body = New MASSurfaceFrameBodySpec With {
                    .TopColor = topCol,
                    .MidColor = midCol,
                    .BottomColor = botCol,
                    .WashColor = washCol,
                    .DrawColdTint = False,
                    .ColdTintColor = SKColors.Transparent,
                    .ColdTintAlpha = 0
                },
                .Lift = New MASSurfaceFrameLiftSpec With {
                    .Draw = True,
                    .TopColor = liftTop,
                    .MidColor = liftMid,
                    .InsetXPx = 5.0F,
                    .InsetTopPx = 2.0F,
                    .HeightPx = 0.0F
                },
                .Border = New MASSurfaceFrameBorderSpec With {
                    .OuterColor = outerBorder,
                    .InnerColor = innerBorder,
                    .HairlineColor = hairline,
                    .GlowColor = SKColors.Transparent,
                    .OuterStrokePx = 0.9F,
                    .InnerStrokePx = 0.7F,
                    .HairlineStrokePx = SurfaceTokens.FxRingStroke,
                    .GlowStrokePx = 0.0F,
                    .InnerInsetPx = 1.0F,
                    .HairlineAlpha = 36,
                    .GlowAlpha = 0,
                    .GlowBlurSigmaPx = 0.0F,
                    .DrawOuter = True,
                    .DrawInner = True,
                    .DrawHairline = True,
                    .DrawGlow = False
                }
            }
        End Function

        Private Sub DrawShortcutText(canvas As SKCanvas,
                                     ctx As MASThemeContext,
                                     dpi As Single,
                                     rowState As MASInteractionState,
                                     menuEnabled As Boolean,
                                     menuTheme As IMASMenuTheme,
                                     text As String,
                                     bounds As SKRect)

            If String.IsNullOrWhiteSpace(text) Then Return
            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If menuTheme Is Nothing Then Return
            If bounds.Width <= 4.0F OrElse bounds.Height <= 4.0F Then Return

            Dim colorToDraw As SKColor =
                _interactionResolver.ResolveChevronColor(
                    ctx:=ctx,
                    rowState:=rowState,
                    menuEnabled:=menuEnabled,
                    menuTheme:=menuTheme)

            TypographyTextPrimitives.DrawSingleLineText(
                canvas:=canvas,
                ctx:=ctx,
                text:=text,
                bounds:=bounds,
                dpi:=dpi,
                style:=MASTypography.MASTextStyle.Body,
                color:=colorToDraw,
                align:=TypographyTextPrimitives.TextAlign.Right,
                drawShadow:=False)
        End Sub

        Private Shared Sub DrawSeparator(canvas As SKCanvas,
                                         panelRect As SKRect,
                                         y As Single,
                                         dpi As Single,
                                         menuTheme As IMASMenuTheme)

            If canvas Is Nothing OrElse menuTheme Is Nothing Then Return

            Dim x1 As Single = panelRect.Left + (10.0F * dpi)
            Dim x2 As Single = panelRect.Right - (10.0F * dpi)

            Dim yy As Single =
                PixelSnap.SnapHalf(y + (MenuTokens.DropDownSeparatorHeight * dpi * 0.5F))

            Using p As New SKPaint With {
                .IsAntialias = False,
                .IsDither = False,
                .Style = SKPaintStyle.Stroke,
                .StrokeWidth = Math.Max(1.0F, 1.0F * dpi),
                .Color = menuTheme.Surface.BorderOuter.WithAlpha(MenuTokens.DropDownRowSeparatorAlpha)
            }
                canvas.DrawLine(x1, yy, x2, yy, p)
            End Using
        End Sub


        Private Shared Function ResolveContentHeightForLevel(m As DropDownMenuState,
                                                             level As Integer) As Single

            If m Is Nothing OrElse m.LevelContentHeightsPx Is Nothing Then Return 0.0F
            If level < 0 OrElse level >= m.LevelContentHeightsPx.Count Then Return 0.0F
            Return Math.Max(0.0F, m.LevelContentHeightsPx(level))
        End Function

        Private Shared Sub DrawScrollEdgeIndicators(canvas As SKCanvas,
                                                    panelRect As SKRect,
                                                    dpi As Single,
                                                    menuTheme As IMASMenuTheme,
                                                    scrollOffsetPx As Single,
                                                    contentHeightPx As Single)

            If canvas Is Nothing OrElse menuTheme Is Nothing Then Return
            If panelRect.IsEmpty OrElse panelRect.Width <= 1.0F OrElse panelRect.Height <= 1.0F Then Return

            dpi = PixelSnap.SafeDpi(dpi)

            Dim padY As Single = MenuTokens.DropDownPanelPadY * dpi
            Dim visibleHeight As Single = Math.Max(0.0F, panelRect.Height - (padY * 2.0F))

            If contentHeightPx <= visibleHeight + 0.5F Then Return

            Dim canScrollUp As Boolean = scrollOffsetPx > 0.5F
            Dim canScrollDown As Boolean = scrollOffsetPx < (contentHeightPx - visibleHeight - 0.5F)

            If Not canScrollUp AndAlso Not canScrollDown Then Return

            Dim x1 As Single = panelRect.Left + (12.0F * dpi)
            Dim x2 As Single = panelRect.Right - (12.0F * dpi)
            Dim topY As Single = PixelSnap.SnapHalf(panelRect.Top + padY - (1.0F * dpi))
            Dim bottomY As Single = PixelSnap.SnapHalf(panelRect.Bottom - padY + (1.0F * dpi))

            Using p As New SKPaint With {
                .IsAntialias = True,
                .IsDither = True,
                .Style = SKPaintStyle.Stroke,
                .StrokeCap = SKStrokeCap.Round,
                .StrokeWidth = Math.Max(1.0F, 1.0F * dpi),
                .Color = menuTheme.Surface.BorderOuter.WithAlpha(48)
            }
                If canScrollUp Then
                    canvas.DrawLine(x1, topY, x2, topY, p)
                End If

                If canScrollDown Then
                    canvas.DrawLine(x1, bottomY, x2, bottomY, p)
                End If
            End Using
        End Sub

        Private Shared Function ResolveRowsForLevel(m As DropDownMenuState,
                                                    level As Integer) As List(Of MASDropDownMenuNode)

            If m Is Nothing Then Return New List(Of MASDropDownMenuNode)()
            If m.LevelRows Is Nothing Then Return New List(Of MASDropDownMenuNode)()
            If level < 0 OrElse level >= m.LevelRows.Count Then Return New List(Of MASDropDownMenuNode)()

            Dim rows As List(Of MASDropDownMenuNode) = m.LevelRows(level)
            If rows Is Nothing Then Return New List(Of MASDropDownMenuNode)()

            Return rows
        End Function

        Private Sub DrawRowText(canvas As SKCanvas,
                                ctx As MASThemeContext,
                                dpi As Single,
                                rowState As MASInteractionState,
                                menuEnabled As Boolean,
                                menuTheme As IMASMenuTheme,
                                row As MASDropDownMenuNode,
                                bounds As SKRect)

            If row Is Nothing Then Return
            If String.IsNullOrEmpty(row.Text) Then Return
            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If menuTheme Is Nothing Then Return

            Dim colorToDraw As SKColor =
                _interactionResolver.ResolveTextColor(
                    ctx:=ctx,
                    rowState:=rowState,
                    menuEnabled:=menuEnabled,
                    menuTheme:=menuTheme)

            If row.Intent = MASDropDownMenuItemIntent.Danger AndAlso ctx.Theme IsNot Nothing Then
                Dim dangerCol As SKColor = MASAppThemeContracts.Foundation(ctx.Theme).Semantics.Danger

                If dangerCol.Alpha > 0 Then
                    If Not menuEnabled Then
                        colorToDraw = ColorMath.Mix(colorToDraw, dangerCol, 0.14F)
                    Else
                        Select Case rowState
                            Case MASInteractionState.Hovered
                                colorToDraw = ColorMath.Mix(colorToDraw, dangerCol, 0.34F)

                            Case MASInteractionState.Selected
                                colorToDraw = ColorMath.Mix(colorToDraw, dangerCol, 0.42F)

                            Case MASInteractionState.Pressed
                                colorToDraw = ColorMath.Mix(colorToDraw, dangerCol, 0.38F)

                            Case Else
                                colorToDraw = ColorMath.Mix(colorToDraw, dangerCol, 0.24F)
                        End Select
                    End If
                End If
            End If

            TypographyTextPrimitives.DrawSingleLineText(
                canvas:=canvas,
                ctx:=ctx,
                text:=row.Text,
                bounds:=bounds,
                dpi:=dpi,
                style:=MASTypography.MASTextStyle.Body,
                color:=colorToDraw,
                align:=TypographyTextPrimitives.TextAlign.Left,
                drawShadow:=False)
        End Sub

        Private Sub DrawChevron(canvas As SKCanvas,
                                ctx As MASThemeContext,
                                dpi As Single,
                                rowState As MASInteractionState,
                                menuEnabled As Boolean,
                                menuTheme As IMASMenuTheme,
                                pr As SKRect,
                                rowTop As Single,
                                rowH As Single,
                                padX As Single)

            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If menuTheme Is Nothing Then Return

            Dim size As Single = Math.Max(7.0F, 8.0F * dpi)
            Dim strokeW As Single =
                Math.Max(SurfaceTokens.FxRingMinStrokePx, 1.35F * dpi)

            Dim cx As Single =
                PixelSnap.SnapHalf(pr.Right - padX - (MenuTokens.DropDownRightGlyphReserve * 0.5F * dpi))

            Dim cy As Single =
                PixelSnap.SnapHalf(rowTop + (rowH * 0.5F))

            Dim col As SKColor =
                _interactionResolver.ResolveChevronColor(
                    ctx:=ctx,
                    rowState:=rowState,
                    menuEnabled:=menuEnabled,
                    menuTheme:=menuTheme)

            _icon.StrokeWidth = strokeW
            _icon.Shader = Nothing
            _icon.Color = col

            Dim a As New SKPoint(cx - (size * 0.35F), cy - (size * 0.35F))
            Dim b As New SKPoint(cx + (size * 0.2F), cy)
            Dim c As New SKPoint(cx - (size * 0.35F), cy + (size * 0.35F))

            canvas.DrawLine(a, b, _icon)
            canvas.DrawLine(b, c, _icon)
        End Sub

        Private Sub DrawCheckMark(canvas As SKCanvas,
                                  ctx As MASThemeContext,
                                  dpi As Single,
                                  rowState As MASInteractionState,
                                  menuEnabled As Boolean,
                                  menuTheme As IMASMenuTheme,
                                  pr As SKRect,
                                  rowTop As Single,
                                  rowH As Single,
                                  padX As Single)

            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If menuTheme Is Nothing Then Return

            Dim size As Single = Math.Max(7.0F, 8.0F * dpi)
            Dim strokeW As Single =
                Math.Max(SurfaceTokens.FxRingMinStrokePx, 1.45F * dpi)

            Dim cx As Single =
                PixelSnap.SnapHalf(pr.Right - padX - (MenuTokens.DropDownRightGlyphReserve * 0.5F * dpi))

            Dim cy As Single =
                PixelSnap.SnapHalf(rowTop + (rowH * 0.5F))

            Dim col As SKColor =
                _interactionResolver.ResolveTextColor(
                    ctx:=ctx,
                    rowState:=rowState,
                    menuEnabled:=menuEnabled,
                    menuTheme:=menuTheme)

            _icon.StrokeWidth = strokeW
            _icon.Shader = Nothing
            _icon.Color = col

            Dim a As New SKPoint(cx - (size * 0.42F), cy + (size * 0.02F))
            Dim b As New SKPoint(cx - (size * 0.12F), cy + (size * 0.32F))
            Dim c As New SKPoint(cx + (size * 0.42F), cy - (size * 0.34F))

            canvas.DrawLine(a, b, _icon)
            canvas.DrawLine(b, c, _icon)
        End Sub

        Private Shared Function FindFirstNonSeparatorRow(rows As List(Of MASDropDownMenuNode)) As Integer
            If rows Is Nothing OrElse rows.Count = 0 Then Return -1

            For i As Integer = 0 To rows.Count - 1
                Dim n As MASDropDownMenuNode = rows(i)
                If n Is Nothing Then Continue For
                If n.Kind <> MASDropDownMenuNodeKind.Separator Then Return i
            Next

            Return -1
        End Function

        Private Shared Function FindLastNonSeparatorRow(rows As List(Of MASDropDownMenuNode)) As Integer
            If rows Is Nothing OrElse rows.Count = 0 Then Return -1

            For i As Integer = rows.Count - 1 To 0 Step -1
                Dim n As MASDropDownMenuNode = rows(i)
                If n Is Nothing Then Continue For
                If n.Kind <> MASDropDownMenuNodeKind.Separator Then Return i
            Next

            Return -1
        End Function

        Private Shared Function BuildMenuRowOverlayRect(panelRect As SKRect,
                                                        rowTop As Single,
                                                        rowBottom As Single,
                                                        isFirst As Boolean,
                                                        isLast As Boolean,
                                                        dpi As Single,
                                                        separatorHeight As Single) As SKRect

            dpi = PixelSnap.SafeDpi(dpi)

            Dim separatorHalf As Single = Math.Max(0.0F, separatorHeight) * 0.5F

            Dim left As Single = panelRect.Left
            Dim right As Single = panelRect.Right

            Dim top As Single = If(isFirst, panelRect.Top, rowTop - separatorHalf)
            Dim bottom As Single = If(isLast, panelRect.Bottom, rowBottom + separatorHalf)

            If right <= left Then right = left + 1.0F
            If bottom <= top Then bottom = top + 1.0F

            Return PixelSnap.SnapRectHalf(New SKRect(left, top, right, bottom))
        End Function

    End Class

End Namespace