// See the file "COPYING" in the main distribution directory for copyright.

#include "zeek/Notifier.h"

#include <set>

#include "zeek/DebugLogger.h"

zeek::notifier::detail::Registry zeek::notifier::detail::registry;

namespace zeek::notifier::detail {

Receiver::Receiver() { DBG_LOG(DBG_NOTIFIERS, "creating receiver %p", this); }

Receiver::~Receiver() { DBG_LOG(DBG_NOTIFIERS, "deleting receiver %p", this); }

Registry::~Registry() {
    while ( registrations.begin() != registrations.end() )
        Unregister(registrations.begin()->first);
}

void Registry::Register(Modifiable* m, Receiver* r) {
    DBG_LOG(DBG_NOTIFIERS, "registering object %p for receiver %p", m, r);

    registrations.insert({m, r});
    ++m->num_receivers;
}

void Registry::Unregister(Modifiable* m, Receiver* r) {
    DBG_LOG(DBG_NOTIFIERS, "unregistering object %p from receiver %p", m, r);

    auto x = registrations.equal_range(m);
    for ( auto i = x.first; i != x.second; i++ ) {
        if ( i->second == r ) {
            --i->first->num_receivers;
            registrations.erase(i);
            break;
        }
    }
}

void Registry::Unregister(Modifiable* m) {
    DBG_LOG(DBG_NOTIFIERS, "unregistering object %p from all notifiers", m);

    auto x = registrations.equal_range(m);
    for ( auto i = x.first; i != x.second; i++ )
        --i->first->num_receivers;

    registrations.erase(x.first, x.second);
}

void Registry::Modified(Modifiable* m) {
    DBG_LOG(DBG_NOTIFIERS, "object %p has been modified", m);

    auto x = registrations.equal_range(m);
    for ( auto i = x.first; i != x.second; i++ )
        i->second->Modified(m);
}

void Registry::Terminate() {
    while ( ! registrations.empty() ) {
        const auto& it = registrations.begin();
        it->second->Terminate();
    }
}

Modifiable::~Modifiable() {
    if ( num_receivers )
        registry.Unregister(this);
}

} // namespace zeek::notifier::detail
