Option Strict On
Option Explicit On

Imports System
Imports System.Windows.Forms
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Components
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


    Partial Public NotInheritable Class MASButton
#Region "Public Properties"

        Public Property Text As String
            Get
                Return _text
            End Get
            Set(value As String)
                Dim v As String = MASButtonActivationPolicy.NormalizeText(value)
                If String.Equals(_text, v, StringComparison.Ordinal) Then Return

                Dim needsLayout As Boolean = MASButtonActivationPolicy.RequiresLayoutInvalidation(_text, v)
                _text = v
                RaiseTextChanged()
                If needsLayout Then InvalidateLayoutSlotInternal()
                InvalidateVisual()
            End Set
        End Property

        Public Property BusyText As String
            Get
                Return _busyText
            End Get
            Set(value As String)
                Dim v As String = MASButtonActivationPolicy.NormalizeText(value)
                If String.Equals(_busyText, v, StringComparison.Ordinal) Then Return

                Dim needsLayout As Boolean = MASButtonActivationPolicy.RequiresLayoutInvalidation(_busyText, v)
                _busyText = v
                RaiseTextChanged()
                If needsLayout Then InvalidateLayoutSlotInternal()
                InvalidateVisual()
            End Set
        End Property

        Public ReadOnly Property EffectiveText As String
            Get
                If _isBusy AndAlso Not String.IsNullOrEmpty(_busyText) Then
                    Return _busyText
                End If

                Return _text
            End Get
        End Property

        Public Property Tooltip As String Implements IMASTooltipProvider.Tooltip
            Get
                Return _tooltip
            End Get
            Set(value As String)
                Dim v As String = MASButtonActivationPolicy.NormalizeTooltip(value)
                If String.Equals(_tooltip, v, StringComparison.Ordinal) Then Return

                _tooltip = v
                RaiseEvent TooltipChanged(Me, EventArgs.Empty)
            End Set
        End Property

        ''' <summary>
        ''' Gets or sets the visual personality contract used by the shared
        ''' MAS button theme. Unknown enum values are normalized instead of
        ''' being allowed to leak into renderer policy code.
        ''' </summary>
        Public Property Personality As MASButtonPersonality
            Get
                Return _personality
            End Get
            Set(value As MASButtonPersonality)
                value = NormalizePersonality(value)
                If _personality = value Then Return

                _personality = value
                RaiseEvent PersonalityChanged(Me, EventArgs.Empty)
                InvalidateVisual()
            End Set
        End Property

        ''' <summary>
        ''' Gets or sets the action intent used by the button renderer. Invalid
        ''' enum values are normalized to MASButtonIntent.Default to keep the
        ''' product surface deterministic.
        ''' </summary>
        Public Property Intent As MASButtonIntent
            Get
                Return _intent
            End Get
            Set(value As MASButtonIntent)
                value = NormalizeIntent(value)
                If _intent = value Then Return

                _intent = value
                RaiseEvent IntentChanged(Me, EventArgs.Empty)
                InvalidateVisual()
            End Set
        End Property

        Public Property IsDefaultButton As Boolean
            Get
                Return _isDefaultButton
            End Get
            Set(value As Boolean)
                If _isDefaultButton = value Then Return

                _isDefaultButton = value
                RaiseEvent DefaultButtonChanged(Me, EventArgs.Empty)
                InvalidateVisual()
            End Set
        End Property

        Public Property [Default] As Boolean
            Get
                Return IsDefaultButton
            End Get
            Set(value As Boolean)
                IsDefaultButton = value
            End Set
        End Property

        Public Property IsSelected As Boolean
            Get
                Return _isSelected
            End Get
            Set(value As Boolean)
                If _isSelected = value Then Return

                _isSelected = value
                RaiseEvent SelectedChanged(Me, EventArgs.Empty)
                InvalidateVisual()
            End Set
        End Property

        Public Property IsBusy As Boolean
            Get
                Return _isBusy
            End Get
            Set(value As Boolean)
                If _isBusy = value Then Return

                _isBusy = value
                RaiseEvent BusyChanged(Me, EventArgs.Empty)
                SyncInteractionEnabledState()
                InvalidateLayoutSlotInternal()
                InvalidateVisual()
            End Set
        End Property

        Public Property DisableWhileBusy As Boolean
            Get
                Return _disableWhileBusy
            End Get
            Set(value As Boolean)
                If _disableWhileBusy = value Then Return

                _disableWhileBusy = value
                SyncInteractionEnabledState()
                InvalidateVisual()
            End Set
        End Property

        Public Property IconPlacement As MASButtonIconPlacement
            Get
                Return _iconPlacement
            End Get
            Set(value As MASButtonIconPlacement)
                value = NormalizeIconPlacement(value)
                If _iconPlacement = value Then Return

                _iconPlacement = value
                RaiseEvent IconChanged(Me, EventArgs.Empty)
                InvalidateLayoutSlotInternal()
                InvalidateVisual()
            End Set
        End Property

        Public ReadOnly Property HasIcon As Boolean
            Get
                Return _drawIcon IsNot Nothing OrElse _drawIconEmphasis IsNot Nothing
            End Get
        End Property

        <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Advanced)>
        Friend Property MinWidth As Single
            Get
                Return _minWidth
            End Get
            Set(value As Single)
                Dim v As Single = NormalizeExtent(value)
                If Math.Abs(_minWidth - v) < 0.001F Then Return

                _minWidth = v
                RaiseEvent SizeHintsChanged(Me, EventArgs.Empty)
                InvalidateVisual()
            End Set
        End Property

        <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Advanced)>
        Friend Property MinHeight As Single
            Get
                Return _minHeight
            End Get
            Set(value As Single)
                Dim v As Single = NormalizeExtent(value)
                If Math.Abs(_minHeight - v) < 0.001F Then Return

                _minHeight = v
                RaiseEvent SizeHintsChanged(Me, EventArgs.Empty)
                InvalidateVisual()
            End Set
        End Property

        <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Advanced)>
        Friend Property PreferredWidth As Single
            Get
                Return _preferredWidth
            End Get
            Set(value As Single)
                Dim v As Single = NormalizeExtent(value)
                If Math.Abs(_preferredWidth - v) < 0.001F Then Return

                _preferredWidth = v
                RaiseEvent SizeHintsChanged(Me, EventArgs.Empty)
                InvalidateVisual()
            End Set
        End Property

        <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Advanced)>
        Friend Property PreferredHeight As Single
            Get
                Return _preferredHeight
            End Get
            Set(value As Single)
                Dim v As Single = NormalizeExtent(value)
                If Math.Abs(_preferredHeight - v) < 0.001F Then Return

                _preferredHeight = v
                RaiseEvent SizeHintsChanged(Me, EventArgs.Empty)
                InvalidateVisual()
            End Set
        End Property

        Public Property Command As Action(Of MASButton)
            Get
                Return _command
            End Get
            Set(value As Action(Of MASButton))
                If Object.ReferenceEquals(_command, value) Then Return

                _command = value
                RaiseEvent CommandChanged(Me, EventArgs.Empty)
            End Set
        End Property

#End Region




#Region "Public API"

        Public Sub PerformClick()
            If Not CanActivate() Then Return

            If _command IsNot Nothing Then
                Try
                    _command.Invoke(Me)
                Catch masCaughtException1 As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(masCaughtException1, "MASButton.Command")
                End Try
            End If

            Try
                RaiseEvent Click(Me, EventArgs.Empty)
            Catch masCaughtException2 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(masCaughtException2, "MASButton.Click")
            End Try

            InvalidateVisual()
        End Sub

        Public Function CanActivate() As Boolean
            Return MASButtonActivationPolicy.CanActivate(Enabled, Visible, _isBusy, _disableWhileBusy)
        End Function

        Public Sub SelectButton()
            IsSelected = True
        End Sub

        Public Sub DeselectButton()
            IsSelected = False
        End Sub

        Public Sub SetBusy(Optional value As Boolean = True,
                           Optional busyText As String = Nothing)

            If busyText IsNot Nothing Then
                Me.BusyText = busyText
            End If

            IsBusy = value
        End Sub

        Public Sub ClearBusy()
            IsBusy = False
        End Sub

        Friend Sub SetBounds(bounds As SKRect)
            SetBounds(bounds.Left, bounds.Top, bounds.Width, bounds.Height)
        End Sub

        Friend Sub SetBounds(x As Single,
                             y As Single,
                             width As Single,
                             height As Single)

            Dim safeX As Single = NormalizeCoordinate(x)
            Dim safeY As Single = NormalizeCoordinate(y)
            Dim w As Single = Math.Max(NormalizeExtent(width), _minWidth)
            Dim h As Single = Math.Max(NormalizeExtent(height), _minHeight)

             SetLocalLayoutBoundsInternal(New SKRect(
                safeX,
                safeY,
                safeX + w,
                safeY + h
            ))
        End Sub

        <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Advanced)>
        Friend Function GetPreferredSize(Optional fallbackWidth As Single = 0.0F,
                                         Optional fallbackHeight As Single = 0.0F) As SKSize

            Return MASIntrinsicControlSizeMetrics.ResolveButtonPreferredSize(
                preferredWidth:=_preferredWidth,
                preferredHeight:=_preferredHeight,
                fallbackWidth:=fallbackWidth,
                fallbackHeight:=fallbackHeight,
                minimumWidth:=_minWidth,
                minimumHeight:=_minHeight)
        End Function

        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 desired As SKSize = ResolveIntrinsicDesiredSize(safeContext)
            Dim minimum As SKSize = MASIntrinsicControlSizeMetrics.ResolveButtonMinimumSize(_minWidth, _minHeight)
            Dim hitInset As MASLayoutInset = MASIntrinsicControlSizeMetrics.CreateTouchHitOverflowInset(desired, safeContext.Density)
            Dim contentInset As MASLayoutInset = MASIntrinsicControlSizeMetrics.CreateButtonContentInset()

            Return MASSizeResolver.FromMeasured(
                desiredSize:=desired,
                minSize:=minimum,
                maxSize:=New SKSize(Single.PositiveInfinity, Single.PositiveInfinity),
                intent:=intent,
                context:=safeContext,
                canGrowWidth:=False,
                canGrowHeight:=False,
                contentInset:=contentInset,
                visualOverflowInset:=MASIntrinsicControlSizeMetrics.DefaultVisualOverflowInset,
                hitOverflowInset:=hitInset,
                isFallback:=False)
        End Function

        Friend Function ResolveLayoutSemanticBounds(outerBounds As SKRect,
                                                    sizeResult As MASSizeResult,
                                                    defaultSlot As MASLayoutSlot) As MASLayoutSemanticBounds Implements IMASLayoutSemanticBoundsProvider.ResolveLayoutSemanticBounds
            Return MASLayoutSemanticBoundsPolicy.ForInteractiveControlSurface(defaultSlot)
        End Function

        Public Sub SetIcon(drawIcon As Action(Of SKCanvas, SKRect, Single, SKColor),
                           Optional drawIconEmphasis As Action(Of SKCanvas, SKRect, Single, SKColor) = Nothing)

            _drawIcon = drawIcon
            _drawIconEmphasis = drawIconEmphasis

            RaiseEvent IconChanged(Me, EventArgs.Empty)
            InvalidateLayoutSlotInternal()
            InvalidateVisual()
        End Sub

        Public Sub ClearIcon()
            If _drawIcon Is Nothing AndAlso _drawIconEmphasis Is Nothing Then Return

            _drawIcon = Nothing
            _drawIconEmphasis = Nothing

            RaiseEvent IconChanged(Me, EventArgs.Empty)
            InvalidateLayoutSlotInternal()
            InvalidateVisual()
        End Sub

#End Region




#Region "Fluent API"

        Friend Function WithBounds(bounds As SKRect) As MASButton
            SetBounds(bounds)
            Return Me
        End Function

        Friend Function WithBounds(x As Single,
                                   y As Single,
                                   width As Single,
                                   height As Single) As MASButton

            SetBounds(x, y, width, height)
            Return Me
        End Function

        Public Function WithText(value As String) As MASButton
            Text = value
            Return Me
        End Function

        Public Function WithBusyText(value As String) As MASButton
            BusyText = value
            Return Me
        End Function

        Public Function WithTooltip(value As String) As MASButton
            Tooltip = value
            Return Me
        End Function

        Public Function WithIntent(value As MASButtonIntent) As MASButton
            Intent = value
            Return Me
        End Function

        Public Function WithPersonality(value As MASButtonPersonality) As MASButton
            Personality = value
            Return Me
        End Function

        Public Function WithDefaultButton(Optional value As Boolean = True) As MASButton
            IsDefaultButton = value
            Return Me
        End Function

        Public Function WithSelected(Optional value As Boolean = True) As MASButton
            IsSelected = value
            Return Me
        End Function

        Public Function WithBusy(Optional value As Boolean = True,
                                 Optional busyText As String = Nothing) As MASButton

            SetBusy(value, busyText)
            Return Me
        End Function

        Public Function WithDisableWhileBusy(Optional value As Boolean = True) As MASButton
            DisableWhileBusy = value
            Return Me
        End Function

        Public Function WithCommand(action As Action(Of MASButton)) As MASButton
            Command = action
            Return Me
        End Function

        Public Function WithIcon(drawIcon As Action(Of SKCanvas, SKRect, Single, SKColor),
                                 Optional drawIconEmphasis As Action(Of SKCanvas, SKRect, Single, SKColor) = Nothing) As MASButton

            SetIcon(drawIcon, drawIconEmphasis)
            Return Me
        End Function

        Public Function WithIconPlacement(value As MASButtonIconPlacement) As MASButton
            IconPlacement = value
            Return Me
        End Function

        Public Function WithoutIcon() As MASButton
            ClearIcon()
            Return Me
        End Function

        <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Advanced)>
        Friend Function WithMinSize(width As Single,
                                    height As Single) As MASButton

            MinWidth = width
            MinHeight = height
            Return Me
        End Function

        <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Advanced)>
        Friend Function WithPreferredSize(width As Single,
                                          height As Single) As MASButton

            PreferredWidth = width
            PreferredHeight = height
            Return Me
        End Function

        Friend Function OnClick(action As Action) As MASButton
            If action Is Nothing Then Return Me

            AddHandler Click,
                Sub(sender As Object, e As EventArgs)
                    action.Invoke()
                End Sub

            Return Me
        End Function

        Friend Function OnClick(action As Action(Of MASButton)) As MASButton
            If action Is Nothing Then Return Me

            AddHandler Click,
                Sub(sender As Object, e As EventArgs)
                    action.Invoke(Me)
                End Sub

            Return Me
        End Function

        Public Function AsPrimary() As MASButton
            Intent = MASButtonIntent.Primary
            Return Me
        End Function

        Public Function AsDefault() As MASButton
            Intent = MASButtonIntent.Default
            Return Me
        End Function

        Public Function AsSubtle() As MASButton
            Intent = MASButtonIntent.Subtle
            Return Me
        End Function

        Public Function AsDanger() As MASButton
            Intent = MASButtonIntent.Danger
            Return Me
        End Function

        Public Function AsMAS() As MASButton
            Personality = MASButtonPersonality.MAS
            Return Me
        End Function

        Public Function AsTal() As MASButton
            Personality = MASButtonPersonality.Tal
            Return Me
        End Function

        Public Function AsDefaultAction() As MASButton
            IsDefaultButton = True
            Return Me
        End Function

        Public Function AsSelected() As MASButton
            IsSelected = True
            Return Me
        End Function

#End Region




#Region "Unified MASSize Fluent API"

        ''' <summary>
        ''' Strongly typed entry into the shared MASSize intent API.
        ''' This method keeps fluent chains on the concrete element while delegating all sizing decisions to MASControlBase/MASSize.
        ''' </summary>
        Public Shadows Function WithSize(sizeIntent As Nexamas.UI.Layout.MASSize) As MASButton
            MyBase.SetSize(sizeIntent)
            Return Me
        End Function






#End Region

    End Class

End Namespace
