Option Strict On
Option Explicit On

Imports System
Imports System.Globalization
Imports System.Windows.Forms
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Composition
Imports Nexamas.UI.Controls
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.Components

    ''' <summary>
    ''' Official Nexamas UI date-range input composed from the existing MASDatePicker
    ''' controls.  It does not create a second calendar, popup, or FloatRuntime path;
    ''' each endpoint uses the owned DatePicker calendar route and this control owns
    ''' only range validation and same-surface endpoint layout.
    ''' </summary>
    Partial Public NotInheritable Class MASDateRangePicker
        Inherits MASControlBase
        Implements IMASLayoutParticipant
        Implements IMASIntrinsicSizeContract

#Region "Fields"

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

        Private ReadOnly _startPicker As MASDatePicker
        Private ReadOnly _endPicker As MASDatePicker

        Private _rangeStackedLayout As Boolean
        Private _lastSeparatorBoundsLogical As SKRect = SKRect.Empty
        Private _minimumDate As Nullable(Of Date) = Nothing
        Private _maximumDate As Nullable(Of Date) = Nothing
        Private _synchronizingEndpoints As Boolean
        Private _disposedLocal As Boolean

#End Region

#Region "Events"

        ''' <summary>
        ''' Raised after the effective start or end date changes through the official range gateway.
        ''' Handler exceptions are contained at the Nexamas UI boundary.
        ''' </summary>
        Public Event RangeChanged As EventHandler

#End Region

#Region "Constructor / Factory"

        Public Sub New()
            MyBase.New()

            _startPicker = MASDatePicker.Create().WithPlaceholder(DateRangePickerTokens.StartPlaceholder)
            _endPicker = MASDatePicker.Create().WithPlaceholder(DateRangePickerTokens.EndPlaceholder)

            AddHandler _startPicker.SelectedDateChanged, AddressOf OnStartPickerSelectedDateChanged
            AddHandler _endPicker.SelectedDateChanged, AddressOf OnEndPickerSelectedDateChanged

            ChildLayerInternal.Add(_startPicker)
            ChildLayerInternal.Add(_endPicker)
            ApplyEndpointConstraints()
        End Sub

        Public Shared Function Create(Optional startDate As Nullable(Of Date) = Nothing,
                                      Optional endDate As Nullable(Of Date) = Nothing) As MASDateRangePicker
            Dim picker As New MASDateRangePicker()
            picker.SetRangeInternal(startDate, endDate, False)
            Return picker
        End Function

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

#End Region

#Region "Public API"

        Public Property StartDate As Nullable(Of Date)
            Get
                Return _startPicker.SelectedDate
            End Get
            Set(value As Nullable(Of Date))
                SetStartDateInternal(value, True)
            End Set
        End Property

        Public Property EndDate As Nullable(Of Date)
            Get
                Return _endPicker.SelectedDate
            End Get
            Set(value As Nullable(Of Date))
                SetEndDateInternal(value, True)
            End Set
        End Property

        Public Property MinimumDate As Nullable(Of Date)
            Get
                Return _minimumDate
            End Get
            Set(value As Nullable(Of Date))
                Dim normalized As Nullable(Of Date) = NormalizeNullableDate(value)
                If NullableDatesEqual(_minimumDate, normalized) Then Return

                _minimumDate = normalized
                If _minimumDate.HasValue AndAlso _maximumDate.HasValue AndAlso _minimumDate.Value > _maximumDate.Value Then
                    _maximumDate = _minimumDate
                End If

                CloseEndpointCalendarsForPolicyChange()
                NormalizeCurrentRange(True)
            End Set
        End Property

        Public Property MaximumDate As Nullable(Of Date)
            Get
                Return _maximumDate
            End Get
            Set(value As Nullable(Of Date))
                Dim normalized As Nullable(Of Date) = NormalizeNullableDate(value)
                If NullableDatesEqual(_maximumDate, normalized) Then Return

                _maximumDate = normalized
                If _minimumDate.HasValue AndAlso _maximumDate.HasValue AndAlso _maximumDate.Value < _minimumDate.Value Then
                    _minimumDate = _maximumDate
                End If

                CloseEndpointCalendarsForPolicyChange()
                NormalizeCurrentRange(True)
            End Set
        End Property

        Public Property StartPlaceholder As String
            Get
                Return _startPicker.Placeholder
            End Get
            Set(value As String)
                _startPicker.Placeholder = NormalizePlaceholder(value, DateRangePickerTokens.StartPlaceholder)
                InvalidateVisual()
            End Set
        End Property

        Public Property EndPlaceholder As String
            Get
                Return _endPicker.Placeholder
            End Get
            Set(value As String)
                _endPicker.Placeholder = NormalizePlaceholder(value, DateRangePickerTokens.EndPlaceholder)
                InvalidateVisual()
            End Set
        End Property

        Public ReadOnly Property DisplayText As String
            Get
                Return FormatRangeText(StartDate, EndDate, CultureInfo.CurrentCulture)
            End Get
        End Property

        Friend ReadOnly Property HasCompleteRange As Boolean
            Get
                Return StartDate.HasValue AndAlso EndDate.HasValue
            End Get
        End Property

        Friend ReadOnly Property RangeAccessibilityText As String
            Get
                Return BuildRangeAccessibilityText(StartDate, EndDate, CultureInfo.CurrentCulture)
            End Get
        End Property

        Public Function WithRange(startDate As Nullable(Of Date),
                                  endDate As Nullable(Of Date)) As MASDateRangePicker
            SetRangeInternal(startDate, endDate, True)
            Return Me
        End Function

        Public Function WithDateBounds(minimumDate As Nullable(Of Date),
                                       maximumDate As Nullable(Of Date)) As MASDateRangePicker
            _minimumDate = NormalizeNullableDate(minimumDate)
            _maximumDate = NormalizeNullableDate(maximumDate)

            If _minimumDate.HasValue AndAlso _maximumDate.HasValue AndAlso _minimumDate.Value > _maximumDate.Value Then
                Dim swap As Date = _minimumDate.Value
                _minimumDate = _maximumDate
                _maximumDate = swap
            End If

            NormalizeCurrentRange(True)
            Return Me
        End Function

        Public Function WithPlaceholders(startPlaceholder As String,
                                         endPlaceholder As String) As MASDateRangePicker
            StartPlaceholder = startPlaceholder
            EndPlaceholder = endPlaceholder
            Return Me
        End Function

        Public Function ClearRange() As MASDateRangePicker
            SetRangeInternal(Nothing, Nothing, True)
            Return Me
        End Function

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

#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 endpointDesired As SKSize = MASIntrinsicControlSizeMetrics.ResolveComboBoxDesiredSize(DateRangePickerTokens.EndpointTextDesiredWidthDip)
            Dim endpointMinimum As SKSize = MASIntrinsicControlSizeMetrics.ResolveComboBoxMinimumSize()

            Dim desired As New SKSize((endpointDesired.Width * 2.0F) + DateRangePickerTokens.SeparatorSlotWidthDip + (DateRangePickerTokens.EndpointGapDip * 2.0F),
                                      endpointDesired.Height)
            Dim minimum As New SKSize(Math.Min((endpointMinimum.Width * 2.0F) + DateRangePickerTokens.SeparatorSlotWidthDip + (DateRangePickerTokens.EndpointGapDip * 2.0F), DateRangePickerTokens.ContractMinWidthCapDip),
                                      endpointMinimum.Height)

            Return MASSizeResolver.FromMeasured(
                desiredSize:=desired,
                minSize:=minimum,
                maxSize:=New SKSize(Single.PositiveInfinity, Single.PositiveInfinity),
                intent:=MASSize.Normalize(intent),
                context:=safeContext,
                canGrowWidth:=False,
                canGrowHeight:=False,
                contentInset:=MASIntrinsicControlSizeMetrics.CreateComboBoxContentInset(),
                visualOverflowInset:=MASIntrinsicControlSizeMetrics.InputVisualOverflowInset,
                hitOverflowInset:=MASIntrinsicControlSizeMetrics.CreateTouchHitOverflowInset(desired, safeContext.Density),
                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

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

        Private Sub ApplyEndpointLayout()
            Dim bounds As SKRect = BoundsLogical
            If bounds.Width <= 1.0F OrElse bounds.Height <= 1.0F Then
                _startPicker.SetLocalLayoutBoundsInternal(SKRect.Empty)
                _endPicker.SetLocalLayoutBoundsInternal(SKRect.Empty)
                Return
            End If

            Dim integration As MASSizeLayoutIntegrationContext = MASSizeLayoutIntegrationGateway.Create(Nothing, MASRuntimeSizeProfileService.Current, 1.0F)
            Dim responsiveProfile As MASResponsiveLayoutProfile = MASResponsiveLayoutResolver.FromLogicalSize(integration, New SKSize(bounds.Width, bounds.Height), SizeIntent)
            Dim gap As Single = DateRangePickerTokens.EndpointGapDip * responsiveProfile.GapScale
            Dim separatorWidth As Single = DateRangePickerTokens.SeparatorSlotWidthDip * responsiveProfile.ChromeScale
            Dim minimumEndpointHeight As Single = MASIntrinsicControlSizeMetrics.ResolveComboBoxMinimumSize().Height
            Dim shouldStack As Boolean = (responsiveProfile.IsCompactOrSmaller OrElse bounds.Width < 360.0F) AndAlso bounds.Height >= (minimumEndpointHeight * 2.0F + gap)
            _rangeStackedLayout = shouldStack

            If shouldStack Then
                Dim stackedEndpointHeight As Single = Math.Max(1.0F, (bounds.Height - gap) * 0.5F)
                Dim startBounds As New SKRect(bounds.Left, bounds.Top, bounds.Right, bounds.Top + stackedEndpointHeight)
                Dim endTop As Single = startBounds.Bottom + gap
                Dim endBounds As New SKRect(bounds.Left, endTop, bounds.Right, Math.Min(bounds.Bottom, endTop + stackedEndpointHeight))
                _lastSeparatorBoundsLogical = SKRect.Empty
                _startPicker.SetLocalLayoutBoundsInternal(startBounds)
                _endPicker.SetLocalLayoutBoundsInternal(endBounds)
                Return
            End If

            Dim endpointWidth As Single = Math.Max(1.0F, (bounds.Width - separatorWidth - (gap * 2.0F)) * 0.5F)
            Dim horizontalEndpointHeight As Single = Math.Max(1.0F, bounds.Height)
            Dim top As Single = bounds.Top

            Dim startBoundsHorizontal As New SKRect(bounds.Left,
                                                   top,
                                                   bounds.Left + endpointWidth,
                                                   top + horizontalEndpointHeight)
            Dim separatorLeft As Single = startBoundsHorizontal.Right + gap
            _lastSeparatorBoundsLogical = New SKRect(separatorLeft,
                                                     top,
                                                     separatorLeft + separatorWidth,
                                                     top + horizontalEndpointHeight)
            Dim endLeft As Single = _lastSeparatorBoundsLogical.Right + gap
            Dim endBoundsHorizontal As New SKRect(endLeft,
                                                 top,
                                                 Math.Min(bounds.Right, endLeft + endpointWidth),
                                                 top + horizontalEndpointHeight)

            _startPicker.SetLocalLayoutBoundsInternal(startBoundsHorizontal)
            _endPicker.SetLocalLayoutBoundsInternal(endBoundsHorizontal)
        End Sub

#End Region

#Region "Keyboard / focus"

        Protected Overrides Function WantsKeyboardFocus() As Boolean
            Return Visible AndAlso Enabled
        End Function

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

            Select Case keyCode
                Case Keys.Delete, Keys.Back
                    If StartDate.HasValue OrElse EndDate.HasValue Then
                        ClearRange()
                        Return True
                    End If

                Case Keys.Home
                    If _minimumDate.HasValue Then
                        SetStartDateInternal(_minimumDate, True)
                        Return True
                    End If

                Case Keys.End
                    If _maximumDate.HasValue Then
                        SetEndDateInternal(_maximumDate, True)
                        Return True
                    End If

                Case Keys.Escape
                    NormalizeCurrentRange(False)
                    Return True
            End Select

            Return False
        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 dpi As Single = PixelSnap.SafeDpi(ctx.Dpi)
            If _rangeStackedLayout Then
                DrawDateRangeMotionOverlay(canvas, ctx, pixelBounds, dpi)
                Return
            End If
            Dim separatorBounds As SKRect = ResolveSeparatorBoundsPx(pixelBounds, dpi)
            If separatorBounds.Width <= 1.0F OrElse separatorBounds.Height <= 1.0F Then Return

            TypographyTextPrimitives.DrawSingleLineText(
                canvas:=canvas,
                ctx:=ctx,
                text:=DateRangePickerTokens.RangeSeparatorText,
                bounds:=separatorBounds,
                dpi:=dpi,
                style:=MASTypography.MASTextStyle.SelectionText,
                color:=ResolveSeparatorColor(ctx),
                align:=TypographyTextPrimitives.TextAlign.Center,
                drawShadow:=False)

            DrawDateRangeMotionOverlay(canvas, ctx, pixelBounds, dpi)
        End Sub

        Private Function ResolveSeparatorBoundsPx(pixelBounds As SKRect,
                                                 dpi As Single) As SKRect
            If _lastSeparatorBoundsLogical.Width > 1.0F AndAlso _lastSeparatorBoundsLogical.Height > 1.0F Then
                Return PixelSnap.SnapRectHalf(New SKRect(_lastSeparatorBoundsLogical.Left * dpi,
                                                         _lastSeparatorBoundsLogical.Top * dpi,
                                                         _lastSeparatorBoundsLogical.Right * dpi,
                                                         _lastSeparatorBoundsLogical.Bottom * dpi))
            End If

            Dim integration As MASSizeLayoutIntegrationContext = MASSizeLayoutIntegrationGateway.Create(themeContext:=Nothing, sizeProfile:=MASRuntimeSizeProfileService.Current, dpiScale:=dpi)
            Dim responsiveProfile As MASResponsiveLayoutProfile = MASResponsiveLayoutResolver.FromIntegration(integration, pixelBounds, SizeIntent)
            Dim gap As Single = DateRangePickerTokens.EndpointGapDip * dpi * responsiveProfile.GapScale
            Dim separatorWidth As Single = DateRangePickerTokens.SeparatorSlotWidthDip * dpi * responsiveProfile.ChromeScale
            Dim endpointWidth As Single = Math.Max(1.0F, (pixelBounds.Width - separatorWidth - (gap * 2.0F)) * 0.5F)
            Dim left As Single = pixelBounds.Left + endpointWidth + gap
            Return PixelSnap.SnapRectHalf(New SKRect(left,
                                                     pixelBounds.Top,
                                                     left + separatorWidth,
                                                     pixelBounds.Bottom))
        End Function

        Private Shared Function ResolveSeparatorColor(ctx As MASThemeContext) As SKColor
            If ctx Is Nothing OrElse ctx.TextColorTheme Is Nothing Then Return SKColors.Gray
            Return ctx.TextColorTheme.SecondaryText
        End Function

#End Region

#Region "Range state"

        Private Sub OnStartPickerSelectedDateChanged(sender As Object,
                                                     e As EventArgs)
            If _synchronizingEndpoints Then Return
            SetStartDateInternal(_startPicker.SelectedDate, True)
        End Sub

        Private Sub OnEndPickerSelectedDateChanged(sender As Object,
                                                   e As EventArgs)
            If _synchronizingEndpoints Then Return
            SetEndDateInternal(_endPicker.SelectedDate, True)
        End Sub

        Private Sub SetStartDateInternal(value As Nullable(Of Date),
                                         raiseChanged As Boolean)
            Dim currentEnd As Nullable(Of Date) = _endPicker.SelectedDate
            Dim normalizedStart As Nullable(Of Date) = ClampDate(NormalizeNullableDate(value))
            Dim normalizedEnd As Nullable(Of Date) = ClampDate(currentEnd)

            If normalizedStart.HasValue AndAlso normalizedEnd.HasValue AndAlso normalizedStart.Value > normalizedEnd.Value Then
                normalizedEnd = normalizedStart
            End If

            SetRangeNormalized(normalizedStart, normalizedEnd, raiseChanged)
        End Sub

        Private Sub SetEndDateInternal(value As Nullable(Of Date),
                                       raiseChanged As Boolean)
            Dim currentStart As Nullable(Of Date) = _startPicker.SelectedDate
            Dim normalizedStart As Nullable(Of Date) = ClampDate(currentStart)
            Dim normalizedEnd As Nullable(Of Date) = ClampDate(NormalizeNullableDate(value))

            If normalizedStart.HasValue AndAlso normalizedEnd.HasValue AndAlso normalizedEnd.Value < normalizedStart.Value Then
                normalizedStart = normalizedEnd
            End If

            SetRangeNormalized(normalizedStart, normalizedEnd, raiseChanged)
        End Sub

        Private Sub SetRangeInternal(startDate As Nullable(Of Date),
                                     endDate As Nullable(Of Date),
                                     raiseChanged As Boolean)
            Dim normalizedStart As Nullable(Of Date) = ClampDate(NormalizeNullableDate(startDate))
            Dim normalizedEnd As Nullable(Of Date) = ClampDate(NormalizeNullableDate(endDate))

            If normalizedStart.HasValue AndAlso normalizedEnd.HasValue AndAlso normalizedStart.Value > normalizedEnd.Value Then
                Dim swap As Date = normalizedStart.Value
                normalizedStart = normalizedEnd
                normalizedEnd = swap
            End If

            SetRangeNormalized(normalizedStart, normalizedEnd, raiseChanged)
        End Sub

        Private Sub NormalizeCurrentRange(raiseChanged As Boolean)
            ApplyEndpointConstraints()
            SetRangeInternal(_startPicker.SelectedDate, _endPicker.SelectedDate, raiseChanged)
        End Sub

        Private Sub SetRangeNormalized(startDate As Nullable(Of Date),
                                       endDate As Nullable(Of Date),
                                       raiseChanged As Boolean)
            Dim previousStart As Nullable(Of Date) = _startPicker.SelectedDate
            Dim previousEnd As Nullable(Of Date) = _endPicker.SelectedDate
            Dim changed As Boolean = Not NullableDatesEqual(previousStart, startDate) OrElse
                                     Not NullableDatesEqual(previousEnd, endDate)

            _synchronizingEndpoints = True
            Try
                _startPicker.SelectedDate = startDate
                _endPicker.SelectedDate = endDate
                ApplyEndpointConstraints()
            Finally
                _synchronizingEndpoints = False
            End Try

            If changed Then
                StartDateRangeMotion(previousStart, previousEnd, startDate, endDate)
                RequestSizeLayoutRefreshForSizeAffectingChange("MASDateRangePicker.Range")
                InvalidateVisual()
                If raiseChanged Then RaiseRangeChangedSafe()
            End If
        End Sub

        Private Sub ApplyEndpointConstraints()
            If _startPicker Is Nothing OrElse _endPicker Is Nothing Then Return

            _startPicker.MinimumDate = _minimumDate
            _startPicker.MaximumDate = If(_endPicker.SelectedDate.HasValue, _endPicker.SelectedDate, _maximumDate)
            _endPicker.MinimumDate = If(_startPicker.SelectedDate.HasValue, _startPicker.SelectedDate, _minimumDate)
            _endPicker.MaximumDate = _maximumDate
        End Sub

        Private Function ClampDate(value As Nullable(Of Date)) As Nullable(Of Date)
            If Not value.HasValue Then Return Nothing

            Dim normalized As Date = value.Value.Date
            If _minimumDate.HasValue AndAlso normalized < _minimumDate.Value Then normalized = _minimumDate.Value
            If _maximumDate.HasValue AndAlso normalized > _maximumDate.Value Then normalized = _maximumDate.Value
            Return normalized
        End Function

        Private Shared Function NormalizeNullableDate(value As Nullable(Of Date)) As Nullable(Of Date)
            If Not value.HasValue Then Return Nothing
            Return value.Value.Date
        End Function

        Private Shared Function NullableDatesEqual(a As Nullable(Of Date),
                                                   b As Nullable(Of Date)) As Boolean
            If a.HasValue <> b.HasValue Then Return False
            If Not a.HasValue Then Return True
            Return a.Value.Date = b.Value.Date
        End Function

        Private Shared Function NormalizePlaceholder(value As String,
                                                     fallback As String) As String
            Dim normalized As String = If(value, String.Empty).Trim()
            If normalized.Length = 0 Then Return fallback
            Return normalized
        End Function

        Friend Shared Function FormatRangeText(startDate As Nullable(Of Date),
                                               endDate As Nullable(Of Date),
                                               culture As CultureInfo) As String
            If Not startDate.HasValue AndAlso Not endDate.HasValue Then Return String.Empty

            Dim effectiveCulture As CultureInfo = If(culture, CultureInfo.CurrentCulture)
            Dim startText As String = If(startDate.HasValue, startDate.Value.Date.ToString("d", effectiveCulture), String.Empty)
            Dim endText As String = If(endDate.HasValue, endDate.Value.Date.ToString("d", effectiveCulture), String.Empty)

            Return startText & " " & DateRangePickerTokens.RangeSeparatorText & " " & endText
        End Function

        Friend Shared Function BuildRangeAccessibilityText(startDate As Nullable(Of Date),
                                                           endDate As Nullable(Of Date),
                                                           culture As CultureInfo) As String
            Dim effectiveCulture As CultureInfo = If(culture, CultureInfo.CurrentCulture)

            If startDate.HasValue AndAlso endDate.HasValue Then
                Return "Date range " & FormatRangeText(startDate, endDate, effectiveCulture)
            End If

            If startDate.HasValue Then
                Return "Date range start " & startDate.Value.Date.ToString("d", effectiveCulture) & "; end date not selected"
            End If

            If endDate.HasValue Then
                Return "Date range end " & endDate.Value.Date.ToString("d", effectiveCulture) & "; start date not selected"
            End If

            Return "Date range not selected"
        End Function

        Private Sub CloseEndpointCalendarsForPolicyChange()
            ' Endpoint calendar floats are owned by MASDatePicker. Re-applying endpoint
            ' constraints intentionally routes stale-popup closure through the existing
            ' DatePicker policy instead of creating a second calendar/popup path here.
            ApplyEndpointConstraints()
        End Sub

        Private Sub RaiseRangeChangedSafe()
            Try
                RaiseEvent RangeChanged(Me, EventArgs.Empty)
            Catch masCaughtException1 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(masCaughtException1, "MASDateRangePicker.RangeChanged")
            End Try
        End Sub

#End Region

#Region "Dispose"

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

            If disposing Then
                Try
                    StopDateRangeMotion(resetState:=True)
                    RemoveHandler _startPicker.SelectedDateChanged, AddressOf OnStartPickerSelectedDateChanged
                    RemoveHandler _endPicker.SelectedDateChanged, AddressOf OnEndPickerSelectedDateChanged
                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
