Imports System.Reflection

''' <summary>
''' A Simple EventBus for .NET
''' https://stackoverflow.com/questions/368265/a-simple-event-bus-for-net
''' </summary>
Public Class EventBus
    Private Shared _Instance As EventBus
    Private _Listeners As List(Of EventListenerWrapper) = New List(Of EventListenerWrapper)()

    Public Shared ReadOnly Property Instance As EventBus
        Get
            If IsNothing(_Instance) Then
                _Instance = New EventBus()
            End If
            Return _Instance
        End Get
    End Property

    ''' <summary>
    ''' Register a form as an event listener.
    ''' </summary>
    ''' <param name="listener">The event listener, usually `Me`</param>
    Public Sub Register(ByVal listener As Object)
        If Not _Listeners.Any(Function(l) l.Listener.Equals(listener)) Then
            _Listeners.Add(New EventListenerWrapper(listener))
        End If
    End Sub

    Public Sub Unregister(ByVal listener As Object)
        Try
            _Listeners.RemoveAll(Function(l) l.Listener = listener)
        Catch ex As Exception

        End Try

    End Sub

    Public Sub PostEvent(ByVal e As Object)
        _Listeners.
            Where(Function(l) l.EventType = e.[GetType]()).
            ToList().
            ForEach(Sub(l) l.PostEvent(e))
    End Sub

    Private Class EventListenerWrapper
        Public Property Listener As Object
        Public Property EventType As Type
        Private _method As MethodBase

        Public Sub New(ByVal listener As Object)
            Me.Listener = listener
            Dim oType As Type = listener.[GetType]()
            _method = oType.GetMethod("OnEvent")
            If _method Is Nothing Then
                Throw New ArgumentException("Class " & oType.Name & " does not containt method OnEvent")
            End If
            Dim oParameters As ParameterInfo() = _method.GetParameters()

            If oParameters.Length <> 1 Then
                Throw New ArgumentException("Method OnEvent of class " & oType.Name & " have invalid number of parameters (should be one)")
            End If
            EventType = oParameters(0).ParameterType
        End Sub

        Public Sub PostEvent(ByVal e As Object)
            _method.Invoke(Listener, {e})
        End Sub
    End Class
End Class
