Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.Windows.Forms
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Composition
Imports Nexamas.UI.General
Imports Nexamas.UI.Layout
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.TextRendering
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.Controls

    ''' <summary>
    ''' Official Nexamas UI expandable in-page container.  It owns only the disclosure
    ''' surface and routed child content; Accordion orchestration remains a later element
    ''' and must build on this control instead of duplicating collapse logic.
    ''' </summary>
    Partial Public NotInheritable Class MASExpander
        Inherits MASControlBase
        Implements IMASLayoutParticipant
        Implements IMASIntrinsicSizeContract

#Region "Fields"

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

        Private ReadOnly _primitives As New MASVisualPrimitivesPainter()
        Private ReadOnly _shadowPainter As New MASShadowPainter()
        Private ReadOnly _relativeChildBounds As New Dictionary(Of MASControlBase, SKRect)()

        Private _title As String = String.Empty
        Private _isExpanded As Boolean = True
        Private _hoverHeader As Boolean
        Private _pressedHeader As Boolean
        Private _disposedLocal As Boolean
        Private _lastLayoutPlan As MASExpanderLayoutPlan

#End Region

#Region "Events"

        ''' <summary>
        ''' Raised after the expanded/collapsed state changes through the official Expander gateway.
        ''' </summary>
        Public Event ExpandedChanged As EventHandler

#End Region

#Region "Constructor / Factory"

        Public Sub New()
            MyBase.New()
        End Sub

        Public Sub New(title As String)
            Me.New()
            _title = MASExpanderDisclosurePolicy.NormalizeTitle(title)
        End Sub

        Public Shared Function Create(Optional title As String = Nothing,
                                      Optional expanded As Boolean = True) As MASExpander
            Dim expander As New MASExpander(title)
            expander._isExpanded = expanded
            Return expander
        End Function

        Friend Shared Function [Default]() As MASExpander
            Return Create()
        End Function

#End Region

#Region "Public API"

        Public Property Title As String
            Get
                Return _title
            End Get
            Set(value As String)
                Dim normalized As String = MASExpanderDisclosurePolicy.NormalizeTitle(value)
                If String.Equals(_title, normalized, StringComparison.Ordinal) Then Return

                _title = normalized
                RaiseTextChanged()
                RequestSizeLayoutRefreshForSizeAffectingChange("MASExpander.Title")
                InvalidateVisual()
            End Set
        End Property

        Public Property IsExpanded As Boolean
            Get
                Return _isExpanded
            End Get
            Set(value As Boolean)
                SetExpandedInternal(value, True)
            End Set
        End Property

        Public Sub Expand()
            SetExpandedInternal(True, True)
        End Sub

        Public Sub Collapse()
            SetExpandedInternal(False, True)
        End Sub

        Public Sub Toggle()
            SetExpandedInternal(Not _isExpanded, True)
        End Sub

        Public Function WithTitle(value As String) As MASExpander
            Title = value
            Return Me
        End Function

        Public Function WithExpanded(value As Boolean) As MASExpander
            IsExpanded = value
            Return Me
        End Function

        Public Shadows Function WithSize(sizeIntent As MASSize) As MASExpander
            MyBase.SetSize(sizeIntent)
            Return Me
        End Function

        Public Function Add(control As MASControlBase) As MASExpander
            AddChild(control)
            Return Me
        End Function

        Public Function Add(control As MASControlBase,
                            relativeContentBounds As SKRect) As MASExpander
            AddChild(control, relativeContentBounds)
            Return Me
        End Function

        Public Function Remove(control As MASControlBase) As MASExpander
            RemoveChild(control)
            Return Me
        End Function

        Public Function Clear() As MASExpander
            ClearChildren()
            Return Me
        End Function

#End Region

#Region "Child content"

        Friend ReadOnly Property Controls As ControlsLayer
            Get
                Return ChildLayerInternal
            End Get
        End Property

        Friend Sub AddChild(child As MASControlBase)
            If child Is Nothing Then Throw New ArgumentNullException(NameOf(child))
            ChildLayerInternal.Add(child)
            ApplyChildVisibilityAndBounds(child)
            InvalidateVisual()
        End Sub

        Friend Sub AddChild(child As MASControlBase,
                            relativeContentBounds As SKRect)
            If child Is Nothing Then Throw New ArgumentNullException(NameOf(child))

            _relativeChildBounds(child) = MASExpanderDisclosurePolicy.NormalizeRelativeBounds(relativeContentBounds)
            ChildLayerInternal.Add(child)
            ApplyChildVisibilityAndBounds(child)
            InvalidateVisual()
        End Sub

        Friend Sub RemoveChild(child As MASControlBase)
            If child Is Nothing Then Return

            If HasChildLayerInternal() Then
                ChildLayerInternal.Remove(child)
            End If

            _relativeChildBounds.Remove(child)
            InvalidateVisual()
        End Sub

        Friend Sub ClearChildren()
            If HasChildLayerInternal() Then
                ChildLayerInternal.Clear()
            End If

            _relativeChildBounds.Clear()
            InvalidateVisual()
        End Sub

        Friend Function GetContentBoundsLogical() As SKRect
            If Not _isExpanded Then Return SKRect.Empty

            Dim slotContent As SKRect = GetLayoutContentBoundsLogicalInternal()
            If slotContent.Width > 1.0F AndAlso slotContent.Height > 1.0F Then
                Return slotContent
            End If

            Dim b As SKRect = BoundsLogical
            If b.Width <= 1.0F OrElse b.Height <= 1.0F Then Return SKRect.Empty

            Dim plan As MASExpanderLayoutPlan = MASExpanderLayoutPlan.CreateForLogical(New SKSize(b.Width, b.Height), SizeIntent)
            Dim r As New SKRect(
                b.Left + plan.PaddingLeftDip,
                b.Top + plan.HeaderHeightDip + plan.PaddingTopDip,
                b.Right - plan.PaddingRightDip,
                b.Bottom - plan.PaddingBottomDip)

            If r.Width <= 1.0F OrElse r.Height <= 1.0F Then Return SKRect.Empty
            Return r
        End Function

        Friend Sub SetChildBoundsInContent(child As MASControlBase,
                                           relativeContentBounds As SKRect)
            If child Is Nothing Then Throw New ArgumentNullException(NameOf(child))
            _relativeChildBounds(child) = MASExpanderDisclosurePolicy.NormalizeRelativeBounds(relativeContentBounds)
            ApplyChildVisibilityAndBounds(child)
        End Sub

        Protected Overrides Sub OnBoundsChanged()
            MyBase.OnBoundsChanged()
            ApplyAllChildPlacements()
        End Sub

#End Region

#Region "Layout / size"

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

        Friend Function GetMinLayoutSize(context As MASLayoutMeasureContext) As SKSize Implements IMASLayoutParticipant.GetMinLayoutSize
            Return MeasureIntrinsicSize(CreateSizeContext(context), 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 measuredTitle As MASTextMeasureResult =
                MASLayoutTextMeasureGateway.Measure(
                    safeContext.IntegrationContext,
                    ResolveTitleForMeasure(),
                    MASTypography.MASTextStyle.Button,
                    Single.PositiveInfinity,
                    allowWrap:=False)

            Dim desiredWidth As Single = Math.Max(ExpanderTokens.MinWidthDip,
                                                  ExpanderTokens.HeaderPaddingLeftDip +
                                                  ExpanderTokens.GlyphSlotDip +
                                                  ExpanderTokens.GlyphTextGapDip +
                                                  measuredTitle.Size.Width +
                                                  ExpanderTokens.HeaderPaddingRightDip)

            Dim desiredHeight As Single = If(_isExpanded,
                                             ExpanderTokens.DefaultExpandedHeightDip,
                                             ExpanderTokens.HeaderHeightDip)

            Dim desired As New SKSize(desiredWidth, desiredHeight)
            Dim minimum As New SKSize(ExpanderTokens.MinWidthDip, ExpanderTokens.HeaderHeightDip)
            Dim contentInset As New MASLayoutInset(
                ExpanderTokens.PaddingLeftDip,
                ExpanderTokens.HeaderHeightDip + ExpanderTokens.PaddingTopDip,
                ExpanderTokens.PaddingRightDip,
                ExpanderTokens.PaddingBottomDip)

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

        Private Shared Function CreateSizeContext(context As MASLayoutMeasureContext) As MASSizeContext
            If context Is Nothing Then Return MASIntrinsicControlSizeMetrics.EnsureContext(Nothing)
            Return context.ToSizeContext()
        End Function

#End Region

#Region "Rendering"

        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 <= 1.0F OrElse pixelBounds.Height <= 1.0F Then Return

            _contract.AssertConsumable()

            Dim plan As MASExpanderLayoutPlan = MASExpanderLayoutPlan.Create(ctx, pixelBounds, SizeIntent)
            If plan Is Nothing Then Return
            _lastLayoutPlan = plan
            Dim dpi As Single = plan.Dpi
            Dim bounds As SKRect = PixelSnap.SnapRect(pixelBounds, dpi)
            If bounds.Width <= 1.0F OrElse bounds.Height <= 1.0F Then Return

            If Enabled Then
                _shadowPainter.DrawLayeredShadow(
                    canvas:=canvas,
                    r:=bounds,
                    contract:=_contract,
                    dpi:=dpi,
                    surfaceTheme:=ctx.SurfaceTheme)
            End If

            Dim radius As Single = MASRadiusResolver.ResolveSurfacePx(_contract, bounds, dpi)
            radius = PixelSnap.SafeRadius(radius)

            Dim surfaceMaterialDrawn As Boolean = MASSurfaceMaterialComponentGateway.TryDrawGroupBoxSurface(
                canvas:=canvas,
                ctx:=ctx,
                boundsPx:=bounds,
                materialKey:=MASSurfaceMaterialIds.Auto,
                dpi:=dpi,
                cornerRadiusPx:=radius,
                surfaceStrengthLevel:=4,
                borderStrengthLevel:=3,
                frameStrength:=MASSurfaceFrameStrength.Level3)

            If Not surfaceMaterialDrawn Then
                _primitives.DrawAcrylicSurface(
                    canvas:=canvas,
                    ctx:=ctx,
                    r:=bounds,
                    dpi:=dpi,
                    contract:=_contract)
            End If

            DrawHeaderInteraction(canvas, ctx, bounds, plan)
            DrawSeparator(canvas, ctx, bounds, plan)
            DrawDisclosureGlyph(canvas, ctx, ResolveHeaderBoundsPx(bounds, dpi, plan), plan)
            DrawTitle(canvas, ctx, ResolveHeaderBoundsPx(bounds, dpi, plan), plan)
        End Sub

        Private Sub DrawHeaderInteraction(canvas As SKCanvas,
                                          ctx As MASThemeContext,
                                          bounds As SKRect,
                                          plan As MASExpanderLayoutPlan)
            If canvas Is Nothing OrElse ctx Is Nothing OrElse ctx.Theme Is Nothing Then Return

            Dim state As MASInteractionState = ResolveInteractionState()
            If state = MASInteractionState.Enabled Then Return

            If plan Is Nothing Then Return
            Dim dpi As Single = plan.Dpi
            Dim header As SKRect = ResolveHeaderBoundsPx(bounds, dpi, plan)
            If header.Width <= 1.0F OrElse header.Height <= 1.0F Then Return

            Dim spec As MASVisualInteractionOverlaySpec = MASInteractionResolver.Build(_contract, state, ctx.Theme)
            If Not spec.FillEnabled AndAlso Not spec.RingEnabled Then Return

            MASSurfaceInteractionRing.DrawOverlay(canvas, header, _contract, spec, dpi)
        End Sub

        Private Sub DrawSeparator(canvas As SKCanvas,
                                  ctx As MASThemeContext,
                                  bounds As SKRect,
                                  plan As MASExpanderLayoutPlan)
            If canvas Is Nothing Then Return
            If Not _isExpanded AndAlso Not IsDisclosureMotionActive() Then Return

            If plan Is Nothing Then Return
            Dim dpi As Single = plan.Dpi
            Dim y As Single = PixelSnap.Snap(bounds.Top + plan.HeaderHeightPx, dpi)
            Dim x1 As Single = bounds.Left + plan.SeparatorInsetPx
            Dim x2 As Single = bounds.Right - plan.SeparatorInsetPx
            If x2 <= x1 Then Return

            Dim color As SKColor = ApplyDisclosureMotionAlpha(ResolveSeparatorColor(ctx))
            If color.Alpha = 0 Then Return

            Using p As New SKPaint With {
                .IsAntialias = True,
                .Style = SKPaintStyle.Stroke,
                .StrokeWidth = plan.SeparatorStrokePx,
                .Color = color
            }
                canvas.DrawLine(x1, y, x2, y, p)
            End Using
        End Sub

        Private Sub DrawTitle(canvas As SKCanvas,
                              ctx As MASThemeContext,
                              header As SKRect,
                              plan As MASExpanderLayoutPlan)
            If canvas Is Nothing OrElse ctx Is Nothing OrElse plan Is Nothing Then Return
            Dim dpi As Single = plan.Dpi

            Dim isRtl As Boolean = MASExpanderDisclosurePolicy.ResolveRightToLeft()
            Dim textRect As SKRect
            If isRtl Then
                textRect = New SKRect(
                    header.Left + plan.HeaderPaddingLeftPx,
                    header.Top,
                    header.Right - (plan.HeaderPaddingRightPx + plan.GlyphSlotPx + plan.GlyphTextGapPx),
                    header.Bottom)
            Else
                textRect = New SKRect(
                    header.Left + (plan.HeaderPaddingLeftPx + plan.GlyphSlotPx + plan.GlyphTextGapPx),
                    header.Top,
                    header.Right - plan.HeaderPaddingRightPx,
                    header.Bottom)
            End If

            If textRect.Width <= 1.0F OrElse textRect.Height <= 1.0F Then Return

            TypographyTextPrimitives.DrawSingleLineText(
                canvas:=canvas,
                ctx:=ctx,
                text:=ResolveTitleForRender(),
                bounds:=textRect,
                dpi:=dpi,
                style:=MASTypography.MASTextStyle.Button,
                color:=ResolveTitleColor(ctx),
                align:=If(isRtl, TypographyTextPrimitives.TextAlign.Right, TypographyTextPrimitives.TextAlign.Left),
                drawShadow:=False)
        End Sub

        Private Sub DrawDisclosureGlyph(canvas As SKCanvas,
                                        ctx As MASThemeContext,
                                        header As SKRect,
                                        plan As MASExpanderLayoutPlan)
            If canvas Is Nothing OrElse plan Is Nothing Then Return
            Dim dpi As Single = plan.Dpi

            Dim size As Single = plan.GlyphSizePx
            Dim isRtl As Boolean = MASExpanderDisclosurePolicy.ResolveRightToLeft()
            Dim cx As Single = If(isRtl,
                                  header.Right - (plan.HeaderPaddingRightPx + (plan.GlyphSlotPx * 0.5F)),
                                  header.Left + (plan.HeaderPaddingLeftPx + (plan.GlyphSlotPx * 0.5F)))
            Dim cy As Single = header.MidY
            Dim half As Single = size * 0.5F
            Dim color As SKColor = ResolveGlyphColor(ctx)

            Using p As New SKPaint With {
                .IsAntialias = True,
                .Style = SKPaintStyle.Stroke,
                .StrokeWidth = plan.GlyphStrokePx,
                .StrokeCap = SKStrokeCap.Round,
                .StrokeJoin = SKStrokeJoin.Round,
                .Color = color
            }
                Using path As New SKPath()
                    Dim disclosureProgress As Single = ResolveDisclosureVisualProgress()
                    Dim c1x As Single = cx - half * 0.25F
                    Dim c1y As Single = cy - half
                    Dim c2x As Single = cx + half * 0.45F
                    Dim c2y As Single = cy
                    Dim c3x As Single = cx - half * 0.25F
                    Dim c3y As Single = cy + half
                    Dim e1x As Single = cx - half
                    Dim e1y As Single = cy - half * 0.35F
                    Dim e2x As Single = cx
                    Dim e2y As Single = cy + half * 0.35F
                    Dim e3x As Single = cx + half
                    Dim e3y As Single = cy - half * 0.35F

                    path.MoveTo(Lerp(c1x, e1x, disclosureProgress), Lerp(c1y, e1y, disclosureProgress))
                    path.LineTo(Lerp(c2x, e2x, disclosureProgress), Lerp(c2y, e2y, disclosureProgress))
                    path.LineTo(Lerp(c3x, e3x, disclosureProgress), Lerp(c3y, e3y, disclosureProgress))

                    canvas.DrawPath(path, p)
                End Using
            End Using
        End Sub

#End Region

#Region "Input"

        Protected Overrides Sub OnMouseMove(ctx As MASThemeContext, ptPx As SKPoint)
            Dim hit As Boolean = IsHeaderHit(ctx, ptPx)
            If hit <> _hoverHeader Then
                _hoverHeader = hit
                InvalidateVisual()
            End If
        End Sub

        Protected Overrides Function OnMouseDown(ctx As MASThemeContext,
                                                 ptPx As SKPoint,
                                                 button As Integer) As Boolean
            If Not Enabled Then Return False
            If button <> CInt(MouseButtons.Left) Then Return False
            If Not IsHeaderHit(ctx, ptPx) Then Return False

            RequestFocus()
            _pressedHeader = True
            _hoverHeader = True
            InvalidateVisual()
            Return True
        End Function

        Protected Overrides Function OnMouseUp(ctx As MASThemeContext,
                                               ptPx As SKPoint,
                                               button As Integer) As Boolean
            If Not _pressedHeader Then Return False

            Dim activate As Boolean = IsHeaderHit(ctx, ptPx)
            _pressedHeader = False
            _hoverHeader = activate
            If activate Then Toggle()
            InvalidateVisual()
            Return True
        End Function

        Protected Overrides Sub OnMouseLeave(ctx As MASThemeContext)
            If _hoverHeader OrElse _pressedHeader Then
                _hoverHeader = False
                _pressedHeader = False
                InvalidateVisual()
            End If
        End Sub

        Protected Overrides Sub OnMouseCancel(ctx As MASThemeContext)
            _hoverHeader = False
            _pressedHeader = False
            InvalidateVisual()
        End Sub

        Protected Overrides Function OnKeyDown(ctx As MASThemeContext,
                                               keyCode As Keys) As Boolean
            If Not Enabled Then Return False

            Dim nextState As Nullable(Of Boolean) = MASExpanderDisclosurePolicy.ResolveKeyboardExpandedState(
                keyCode,
                _isExpanded,
                MASExpanderDisclosurePolicy.ResolveRightToLeft())
            If nextState.HasValue Then
                SetExpandedInternal(nextState.Value, True)
                Return True
            End If

            Return False
        End Function

        Protected Overrides Function WantsKeyboardFocus() As Boolean
            Return True
        End Function

        Protected Overrides Function WantsPointerFocus() As Boolean
            Return True
        End Function

        Protected Overrides Function HitTest(ctx As MASThemeContext, ptPx As SKPoint) As Boolean
            If IsHeaderHit(ctx, ptPx) Then Return True
            If Not _isExpanded Then Return False
            Dim r As SKRect = GetPixelHitBounds(ctx)
            Return r.Contains(ptPx.X, ptPx.Y)
        End Function

#End Region

#Region "State / helpers"

        Private Sub SetExpandedInternal(value As Boolean,
                                        raiseChanged As Boolean)
            If _isExpanded = value Then Return

            Dim previousExpanded As Boolean = _isExpanded
            _isExpanded = value
            StartDisclosureMotion(previousExpanded, value)
            _pressedHeader = False
            ApplyAllChildPlacements()
            RequestSizeLayoutRefreshForSizeAffectingChange("MASExpander.IsExpanded")
            InvalidateVisual()

            If raiseChanged Then RaiseExpandedChangedSafe()
        End Sub

        Private Sub RaiseExpandedChangedSafe()
            Try
                RaiseEvent ExpandedChanged(Me, EventArgs.Empty)
            Catch masCaughtException1 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(masCaughtException1, "MASExpander.ExpandedChanged")
            End Try
        End Sub

        Private Function ResolveInteractionState() As MASInteractionState
            If Not Enabled Then Return MASInteractionState.Disabled
            If _pressedHeader Then Return MASInteractionState.Pressed
            If HasKeyboardFocus Then Return MASInteractionState.Focused
            If _hoverHeader Then Return MASInteractionState.Hovered
            Return MASInteractionState.Enabled
        End Function

        Private Function IsHeaderHit(ctx As MASThemeContext,
                                     ptPx As SKPoint) As Boolean
            If ctx Is Nothing Then Return False
            Dim bounds As SKRect = GetPixelHitBounds(ctx)
            If bounds.Width <= 1.0F OrElse bounds.Height <= 1.0F Then Return False

            Dim plan As MASExpanderLayoutPlan = MASExpanderLayoutPlan.Create(ctx, bounds, SizeIntent)
            Dim dpi As Single = If(plan Is Nothing, PixelSnap.SafeDpi(ctx.Dpi), plan.Dpi)
            Dim header As SKRect = ResolveHeaderBoundsPx(bounds, dpi, plan)
            Return header.Contains(ptPx.X, ptPx.Y)
        End Function

        Private Shared Function ResolveHeaderBoundsPx(bounds As SKRect,
                                                      dpi As Single,
                                                      plan As MASExpanderLayoutPlan) As SKRect
            If plan IsNot Nothing Then
                Return New SKRect(bounds.Left, bounds.Top, bounds.Right, Math.Min(bounds.Bottom, bounds.Top + plan.HeaderHeightPx))
            End If

            Return MASExpanderDisclosurePolicy.ResolveHeaderBoundsPx(bounds, dpi)
        End Function

        Private Sub ApplyAllChildPlacements()
            If Not HasChildLayerInternal() Then Return

            Dim children As MASControlBase() = ChildLayerInternal.GetSnapshot()
            For Each child As MASControlBase In children
                If child Is Nothing Then Continue For
                ApplyChildVisibilityAndBounds(child)
            Next
        End Sub

        Private Sub ApplyChildVisibilityAndBounds(child As MASControlBase)
            If child Is Nothing Then Return

            child.Visible = _isExpanded
            If Not _isExpanded Then
                child.SetLocalLayoutBoundsInternal(SKRect.Empty)
                Return
            End If

            Dim content As SKRect = GetContentBoundsLogical()
            If content.Width <= 1.0F OrElse content.Height <= 1.0F Then
                child.SetLocalLayoutBoundsInternal(SKRect.Empty)
                Return
            End If

            Dim relative As SKRect = SKRect.Empty
            If Not _relativeChildBounds.TryGetValue(child, relative) Then
                child.SetLocalLayoutBoundsInternal(content)
                Return
            End If

            child.SetLocalLayoutBoundsInternal(New SKRect(
                content.Left + relative.Left,
                content.Top + relative.Top,
                content.Left + relative.Right,
                content.Top + relative.Bottom))
        End Sub

        Friend Function CreateExpanderReadinessManifest() As MASExpanderReadinessManifest
            Return MASExpanderReadinessManifest.CreateCurrent()
        End Function

        Private Function ResolveTitleForRender() As String
            Return MASExpanderDisclosurePolicy.ResolveRenderTitle(_title)
        End Function

        Private Function ResolveTitleForMeasure() As String
            Return ResolveTitleForRender()
        End Function

        Private Shared Function ResolveTitleColor(ctx As MASThemeContext) As SKColor
            If ctx IsNot Nothing AndAlso ctx.TextColorTheme IsNot Nothing Then
                Return ctx.TextColorTheme.PrimaryText.WithAlpha(ExpanderTokens.TitleTextAlpha)
            End If

            Return Nexamas.UI.Values.Color.Text.Primary.WithAlpha(ExpanderTokens.TitleTextAlpha)
        End Function

        Private Shared Function ResolveGlyphColor(ctx As MASThemeContext) As SKColor
            If ctx IsNot Nothing AndAlso ctx.TextColorTheme IsNot Nothing Then
                Return ctx.TextColorTheme.SecondaryText.WithAlpha(ExpanderTokens.GlyphAlpha)
            End If

            Return Nexamas.UI.Values.Color.Text.Secondary.WithAlpha(ExpanderTokens.GlyphAlpha)
        End Function

        Private Shared Function ResolveSeparatorColor(ctx As MASThemeContext) As SKColor
            If ctx IsNot Nothing AndAlso ctx.TextColorTheme IsNot Nothing Then
                Return ctx.TextColorTheme.SecondaryText.WithAlpha(ExpanderTokens.SeparatorAlpha)
            End If

            Return Nexamas.UI.Values.Color.Text.Secondary.WithAlpha(ExpanderTokens.SeparatorAlpha)
        End Function

#End Region

#Region "Dispose"

        Protected Overrides Sub Dispose(disposing As Boolean)
            If _disposedLocal Then Return
            _disposedLocal = True

            If disposing Then
                Try
                    StopDisclosureMotion(resetToCurrentState:=True)
                    _primitives.Dispose()
                    _shadowPainter.Dispose()
                Catch masCaughtException2 As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(masCaughtException2)
                End Try
            End If

            MyBase.Dispose(disposing)
        End Sub

#End Region

    End Class

End Namespace
