Option Strict On
Option Explicit On

Imports System
Imports System.Globalization
Imports System.Windows.Forms
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Components.DropDownMenu
Imports Nexamas.UI.Controls
Imports Nexamas.UI.FloatRuntime
Imports Nexamas.UI.General
Imports Nexamas.UI.Layout
Imports Nexamas.UI.Motion
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.TextRendering
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Composition
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.Components

    Partial Public NotInheritable Class MASSplitButton

#Region "Helpers"

        Private Function HitTestSplit(ctx As MASThemeContext, ptPx As SKPoint) As MASSplitButtonSegment
            If ctx Is Nothing Then Return MASSplitButtonSegment.None
            Dim bounds As SKRect = GetPixelBounds(ctx)
            Return MASSplitButtonSegmentPolicy.HitTest(bounds, ptPx, PixelSnap.SafeDpi(ctx.Dpi), IsRightToLeft())
        End Function

        Private Shared Function IsRightToLeft() As Boolean
            Return MASSplitButtonSegmentPolicy.IsRightToLeftCulture()
        End Function

        Private Function EnsureMenuService() As DropDownMenuPopupService
            Dim runtime As MASFloatRuntime = ResolveFloatRuntime()
            If runtime Is Nothing Then Return Nothing

            If _menuService IsNot Nothing AndAlso Object.ReferenceEquals(_menuRuntime, runtime) Then Return _menuService

            _menuRuntime = runtime
            _menuService = New DropDownMenuPopupService(
                runtime:=runtime,
                getContext:=AddressOf ResolveThemeContext,
                getViewportBoundsPx:=AddressOf ResolveViewportBoundsPx,
                ownerKey:=ButtonTokens.SplitButtonMenuOwnerKey,
                floatKey:=MASFloatKeys.DropDownMenu)

            AddHandler _menuService.Closed, AddressOf MenuServiceClosed
            Return _menuService
        End Function

        Private Function ResolveFloatRuntime() As MASFloatRuntime
            If _runtimeServices IsNot Nothing Then Return _runtimeServices.TryGetFloatRuntime()
            Return Nothing
        End Function

        Private Function ResolveThemeContext() As MASThemeContext
            If _lastContext IsNot Nothing Then Return _lastContext

            Dim h As ThemeHost = Me.Host
            If h Is Nothing Then Return Nothing

            Try
                Return h.Context
            Catch masCaughtException6 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowRecoverable(masCaughtException6, "MASSplitButton.ResolveThemeContext")
                Return Nothing
            End Try
        End Function

        Private Function ResolveViewportBoundsPx() As SKRect
            Dim size As SKSizeI = SKSizeI.Empty
            If _runtimeServices IsNot Nothing Then size = _runtimeServices.TryGetSurfaceSizePx()

            If size.Width > 1 AndAlso size.Height > 1 Then
                Return New SKRect(0.0F, 0.0F, CSng(size.Width), CSng(size.Height))
            End If

            Dim anchor As SKRect = GetPixelBoundsSafe()
            If anchor.Width <= 1.0F OrElse anchor.Height <= 1.0F Then Return SKRect.Empty

            Return New SKRect(0.0F, 0.0F, Math.Max(anchor.Right + 4.0F, anchor.Width), Math.Max(anchor.Bottom + 4.0F, anchor.Height))
        End Function

        Private Function GetPixelBoundsSafe() As SKRect
            Dim ctx As MASThemeContext = ResolveThemeContext()
            If ctx Is Nothing Then Return SKRect.Empty

            Try
                Return GetPixelBounds(ctx)
            Catch masCaughtException7 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowRecoverable(masCaughtException7, "MASSplitButton.GetPixelBoundsSafe")
                Return SKRect.Empty
            End Try
        End Function

        Private Function ResolveDpi() As Single
            Dim ctx As MASThemeContext = ResolveThemeContext()
            If ctx IsNot Nothing Then Return PixelSnap.SafeDpi(ctx.Dpi)

            Dim h As ThemeHost = Me.Host
            If h Is Nothing Then Return 1.0F

            Try
                Return PixelSnap.SafeDpi(h.Dpi)
            Catch masCaughtException8 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowRecoverable(masCaughtException8, "MASSplitButton.ResolveDpi")
                Return 1.0F
            End Try
        End Function

        Private Sub InvokeMenuCommand(commandId As String)
            Dim id As String = If(commandId, String.Empty).Trim()
            If id.Length = 0 Then Return

            Try
                If _commandHandler IsNot Nothing Then _commandHandler.Invoke(id)
            Catch masCaughtException9 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(masCaughtException9, "MASSplitButton.CommandHandler")
            End Try
        End Sub

        Private Sub MenuServiceClosed(sender As Object, e As EventArgs)
            OnMenuClosedInternal()
        End Sub

        Private Sub OnMenuClosedInternal()
            Dim wasOpen As Boolean = _isMenuOpen
            _isMenuOpen = False
            _pressedMenu = False
            _keyboardSegment = MASSplitButtonSegment.Main

            If wasOpen Then
                Try
                    RaiseEvent MenuClosed(Me, EventArgs.Empty)
                Catch masCaughtException11 As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(masCaughtException11, "MASSplitButton.MenuClosed")
                End Try
            End If

            InvalidateVisual()
        End Sub

        Private Sub ResetPointerState()
            _hoverMain = False
            _hoverMenu = False
            _pressedMain = False
            _pressedMenu = False
            StopPressMotion(clearSegment:=True)
        End Sub

        Private Sub PulsePressReleaseMotion(segment As MASSplitButtonSegment)
            If Not CanActivate() Then Return
            If segment <> MASSplitButtonSegment.Main AndAlso segment <> MASSplitButtonSegment.Menu Then Return

            StopPressMotion(clearSegment:=False)
            _motionPressedSegment = segment

            Dim plan As MASMotionTransitionPlan = MASMotionSystem.CreateTransitionPlan(MASMotionTokenId.PressRelease, 0.0F, 1.0F)
            _pressPulseRunner = MASMotionSystem.CreateTimelineRunner(
                owner:=Me,
                consumerName:="MASSplitButton.PressReleasePulse",
                plan:=plan,
                requestFrame:=AddressOf InvalidateVisual,
                applyFrame:=AddressOf ApplyPressMotionFrame,
                completed:=AddressOf CompletePressMotionFrame)
            _pressPulseRunner.Start()

            InvalidateVisual()
        End Sub

        Private Sub ApplyPressMotionFrame(frame As MASMotionFrame)
            If frame Is Nothing Then Return
            InvalidateVisual()
        End Sub

        Private Sub CompletePressMotionFrame(frame As MASMotionFrame)
            StopPressMotion(clearSegment:=True)
            InvalidateVisual()
        End Sub

        Private Sub StopPressMotion(clearSegment As Boolean)
            If _pressPulseRunner IsNot Nothing Then
                _pressPulseRunner.Dispose()
                _pressPulseRunner = Nothing
            End If

            If clearSegment Then
                _motionPressedSegment = MASSplitButtonSegment.None
            End If
        End Sub

        Private Shared Function NormalizeIntent(value As MASButtonIntent) As MASButtonIntent
            Return MASSplitButtonSegmentPolicy.NormalizeIntent(value)
        End Function

        Private Shared Function NormalizeIconPlacement(value As MASButtonIconPlacement) As MASButtonIconPlacement
            Return MASSplitButtonSegmentPolicy.NormalizeIconPlacement(value)
        End Function

#End Region

    End Class

End Namespace
