Option Strict On
Option Explicit On

Imports System
Imports Nexamas.UI.CommandActions

Namespace Nexamas.UI.DataBinding

    ''' <summary>
    ''' Internal weak command lease connecting ViewModel command members to CommandActionSystem without keeping the ViewModel alive after disposal.
    ''' </summary>
    Friend NotInheritable Class MASViewModelCommandBindingLease

        Private ReadOnly _registry As WeakReference
        Private ReadOnly _source As WeakReference
        Private _released As Boolean

        Friend Sub New(registry As MASCommandRegistry,
                       source As MASViewModelBindingSource,
                       commandId As String,
                       executeMemberName As String,
                       canExecuteMemberName As String)
            If registry Is Nothing Then Throw New ArgumentNullException(NameOf(registry))
            If source Is Nothing Then Throw New ArgumentNullException(NameOf(source))

            _registry = New WeakReference(registry)
            _source = New WeakReference(source)
            Me.CommandId = MASCommandRegistry.NormalizeCommandId(commandId)
            Me.ExecuteMemberName = Normalize(executeMemberName)
            Me.CanExecuteMemberName = Normalize(canExecuteMemberName)
        End Sub

        Friend ReadOnly Property CommandId As String
        Friend ReadOnly Property ExecuteMemberName As String
        Friend ReadOnly Property CanExecuteMemberName As String

        Friend ReadOnly Property UsesWeakSourceReference As Boolean
            Get
                Return True
            End Get
        End Property

        Friend ReadOnly Property CanRelease As Boolean
            Get
                Return True
            End Get
        End Property

        Friend ReadOnly Property IsReleased As Boolean
            Get
                Return _released
            End Get
        End Property

        Friend ReadOnly Property Status As MASViewModelBindingLifecycleStatus
            Get
                If _released Then Return MASViewModelBindingLifecycleStatus.CommandReleased
                If _source.Target Is Nothing Then Return MASViewModelBindingLifecycleStatus.SourceUnavailable
                Return MASViewModelBindingLifecycleStatus.CommandRegistered
            End Get
        End Property

        Friend Function CanExecute(context As MASCommandExecutionContext) As Boolean
            If _released Then Return False

            Dim bindingSource As MASViewModelBindingSource = TryCast(_source.Target, MASViewModelBindingSource)
            If bindingSource Is Nothing Then Return False

            Return bindingSource.EvaluateCanExecute(CanExecuteMemberName, context)
        End Function

        Friend Sub Execute(context As MASCommandExecutionContext)
            If _released Then Return

            Dim bindingSource As MASViewModelBindingSource = TryCast(_source.Target, MASViewModelBindingSource)
            If bindingSource Is Nothing Then Throw New ObjectDisposedException(NameOf(MASViewModelBinder))

            Dim failure As String = String.Empty
            If Not bindingSource.TryInvokeCommandAction(ExecuteMemberName, context, failure) Then
                Throw New InvalidOperationException(failure)
            End If
        End Sub

        Friend Function Release() As MASViewModelBindingLifecycleStatus
            If _released Then Return MASViewModelBindingLifecycleStatus.CommandReleased

            Dim registry As MASCommandRegistry = TryCast(_registry.Target, MASCommandRegistry)
            If registry IsNot Nothing Then
                registry.ReleaseRegisteredCommand(CommandId)
            End If

            _released = True
            Return MASViewModelBindingLifecycleStatus.CommandReleased
        End Function

        Friend Function ToRecord() As MASViewModelBindingLifecycleRecord
            Return New MASViewModelBindingLifecycleRecord(
                Status,
                If(_released, "Command binding lease has been released.", "Command binding lease uses a weak ViewModel source route."),
                CommandId,
                usesWeakSourceReference:=UsesWeakSourceReference,
                canRelease:=CanRelease)
        End Function

        Private Shared Function Normalize(value As String) As String
            Return If(value, String.Empty).Trim()
        End Function

    End Class

End Namespace
