#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <memory>

class Event
{
public:
    typedef std::shared_ptr<Event> Pointer;
    int id;
    Event() : id(0) {}

    virtual std::shared_ptr<Event> getSharedPointer()
    {
        return std::shared_ptr<Event>(this);
    }
};

class ParameterChangedEvent : public Event
{
public:
    typedef std::shared_ptr<ParameterChangedEvent> Pointer;
    int id;
    std::string device;
    ParameterChangedEvent() : id(1), device("M1") {}

    virtual std::shared_ptr<Event> getSharedPointer()
    {
        return std::make_shared<ParameterChangedEvent>(*this);
    }
};

class YetAnotherEvent : public Event
{
public:
    typedef std::shared_ptr<YetAnotherEvent> Pointer;
    int id;
    YetAnotherEvent() : id(3) {}

    virtual std::shared_ptr<Event> getSharedPointer()
    {
        return std::make_shared<YetAnotherEvent>(*this);
    }
};

typedef std::function<void (Event::Pointer)> EventListener;
std::queue<Event::Pointer> events;
std::vector<EventListener> listeners;

void enqueue(Event & event)
{
    events.push(event.getSharedPointer());
}

void producer1()
{
    ParameterChangedEvent event;
    enqueue(event);
}

void producer2()
{
    YetAnotherEvent event;
    enqueue(event);
}

void dispatch()
{
    while (!events.empty()) {
        auto event = events.front();
        for (auto listener: listeners) {
            listener(event);
        }
        events.pop();
    }
}

void listener1(Event::Pointer event)
{
    ParameterChangedEvent::Pointer event2 = std::dynamic_pointer_cast<ParameterChangedEvent>(event);
    if (event2 == nullptr) {
        return;
    }
    std::cout << "Listener 1 [Event.id:" << event2->id << "], device: " << event2->device << std::endl;
}

void listener2(Event::Pointer event)
{
    YetAnotherEvent::Pointer event2 = std::dynamic_pointer_cast<YetAnotherEvent>(event);
    if (event2 == nullptr) {
        return;
    }
    std::cout << "Listener 2 [Event.id:" << event2->id << "], id: " << event2->id << std::endl;
}

int main()
{
    listeners.push_back(EventListener(listener1));
    listeners.push_back(EventListener(listener2));

    producer1();
    producer1();
    producer2();
    producer1();
    producer2();
    producer2();

    dispatch();

    return 0;
}
