////////////////////////////////////////////////////////////////////////////////////////
//
//  Copyright 2026 OVITO GmbH, Germany
//
//  This file is part of OVITO (Open Visualization Tool).
//
//  OVITO is free software; you can redistribute it and/or modify it either under the
//  terms of the GNU General Public License version 3 as published by the Free Software
//  Foundation (the "GPL") or, at your option, under the terms of the MIT License.
//  If you do not alter this notice, a recipient may use your version of this
//  file under either the GPL or the MIT License.
//
//  You should have received a copy of the GPL along with this program in a
//  file LICENSE.GPL.txt.  You should have received a copy of the MIT License along
//  with this program in a file LICENSE.MIT.txt
//
//  This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
//  either express or implied. See the GPL or the MIT License for the specific language
//  governing rights and limitations.
//
////////////////////////////////////////////////////////////////////////////////////////

#pragma once


#include <ovito/core/Core.h>
#include "RefTarget.h"

namespace Ovito {

/**
 * \brief A QObject-derived class that can be used to monitor notification events
 *        generated by a RefTarget object without the need to derive a new class from RefMaker.
 */
class OVITO_CORE_EXPORT RefTargetListenerImpl : public QObject, public RefMaker
{
    OVITO_CLASS(RefTargetListenerImpl)
    Q_OBJECT

Q_SIGNALS:

    /// \brief This Qt signal is emitted by the listener each time it receives a notification
    ///        event from the current target.
    /// \param source The object that sent the notification event.
    /// \param event The notification event.
    void notificationEvent(RefTarget* source, const ReferenceEvent& event);

protected:

    /// \brief Is called when the RefTarget referenced by this listener has generated an event.
    virtual bool referenceEvent(RefTarget* source, const ReferenceEvent& event) override;

private:

    /// The RefTarget which is being monitored by this listener.
    DECLARE_MODIFIABLE_REFERENCE_FIELD(OORef<RefTarget>, target, setTarget);
};

/**
 * \brief A helper class that can be used to monitor notification events
 *        generated by a RefTarget object without the need to derive a new class from RefMaker.
 */
template<typename T>
class RefTargetListener
{
public:

    /// \brief Connects a member-function slot to receive notification events from the observed RefTarget.
    template<typename Func>
    QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func>::Object* receiver, Func slot, Qt::ConnectionType type = Qt::AutoConnection) {
        return QObject::connect(_impl.get(), &RefTargetListenerImpl::notificationEvent, receiver, slot, type);
    }

    /// \brief Connects a functor slot to receive notification events from the observed RefTarget.
    template<typename Func>
    typename std::enable_if<QtPrivate::FunctionPointer<Func>::ArgumentCount == -1, QMetaObject::Connection>::type connect(const QObject* context, Func slot, Qt::ConnectionType type = Qt::AutoConnection) {
        return QObject::connect(_impl.get(), &RefTargetListenerImpl::notificationEvent, context, std::move(slot), type);
    }

    /// \brief Returns the current target this listener is listening to.
    /// \return The current target object or \c nullptr.
    T* target() const { return static_object_cast<T>(_impl->target()); }

    /// \brief Sets the current target this listener should listen to.
    /// \param newTarget The new target or \c nullptr.
    void setTarget(T* newTarget) { _impl->setTarget(newTarget); }

private:

    OORef<RefTargetListenerImpl> _impl = OORef<RefTargetListenerImpl>::create();
};

/**
 * \brief A helper class that can be used to monitor notification events
 *        generated by multiple RefTarget objects without the need to derive a new class from RefMaker.
 */
class OVITO_CORE_EXPORT VectorRefTargetListenerImpl : public QObject, public RefMaker
{
    OVITO_CLASS(VectorRefTargetListenerImpl)
    Q_OBJECT

public:

    /// \brief Clears the list of targets.
    void clear() { _targets.clear(this, PROPERTY_FIELD(targets));  }

    /// \brief Adds a new object to the list of targets this listener should listen to.
    void push_back(RefTarget* target) { OVITO_CHECK_OBJECT_POINTER(target); _targets.push_back(this, PROPERTY_FIELD(targets), target); }

    /// \brief Inserts a new object into the list of targets this listener should listen to.
    void insert(int index, RefTarget* target) { OVITO_CHECK_OBJECT_POINTER(target); _targets.insert(this, PROPERTY_FIELD(targets), index, target); }

    /// \brief Replaces an object in the list of targets this listener should listen to.
    void set(int index, RefTarget* target) { OVITO_CHECK_OBJECT_POINTER(target); _targets.set(this, PROPERTY_FIELD(targets), index, target); }

    /// \brief Removes an object from the list of targets this listener should listen to.
    void remove(RefTarget* target) {
        OVITO_CHECK_OBJECT_POINTER(target);
        int index = _targets.indexOf(target);
        if(index >= 0)
            _targets.remove(this, PROPERTY_FIELD(targets), index);
    }

    /// \brief Removes an object from the list of targets this listener should listen to.
    void remove(int index) {
        _targets.remove(this, PROPERTY_FIELD(targets), index);
    }

Q_SIGNALS:

    /// \brief This Qt signal is emitted by the listener each time it receives a notification
    ///        event from a target.
    /// \param source The object that sent the notification event.
    /// \param event The notification event.
    void notificationEvent(RefTarget* source, const ReferenceEvent& event);

protected:

    /// \brief Is called when a RefTarget referenced by this listener has generated an event.
    virtual bool referenceEvent(RefTarget* source, const ReferenceEvent& event) override;

private:

    /// The list of RefTargets which are being monitored by this listener.
    DECLARE_MODIFIABLE_VECTOR_REFERENCE_FIELD(OORef<RefTarget>, targets, setTargets);
};

/**
 * \brief A helper class that can be used to monitor notification events
 *        generated by multiple RefTarget objects without the need to derive a new class from RefMaker.
 *
 * This is the templated version of VectorRefTargetListenerBase.
 */
template<typename T>
class VectorRefTargetListener
{
public:

   /// \brief Connects a member-function slot to receive notification events from the observed RefTarget2.
    template<typename Func>
    QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func>::Object* receiver, Func slot, Qt::ConnectionType type = Qt::AutoConnection) {
        return QObject::connect(_impl.get(), &VectorRefTargetListenerImpl::notificationEvent, receiver, slot, type);
    }

    /// \brief Connects a functor slot to receive notification events from the observed RefTarget2.
    template<typename Func>
    typename std::enable_if<QtPrivate::FunctionPointer<Func>::ArgumentCount == -1, QMetaObject::Connection>::type connect(const QObject* context, Func slot, Qt::ConnectionType type = Qt::AutoConnection) {
        return QObject::connect(_impl.get(), &VectorRefTargetListenerImpl::notificationEvent, context, std::move(slot), type);
    }

    /// \brief Returns the list of targets this listener is listening to.
    /// \return The current list of target objects.
    /// \sa setTargets()
    const auto& targets() const {
        static_assert(std::is_same_v<decltype(_impl->targets()), const QVector<OORef<RefTarget>>&>);
        return reinterpret_cast<const QVector<OORef<T>>&>(_impl->targets());
    }

    /// \brief Sets the list of targets this listener should listen to.
    /// \param newTargets The new list of targets.
    /// \sa targets()
    template<typename U>
    void setTargets(U&& newTargets) {
        _impl->setTargets(std::forward<U>(newTargets));
    }

    /// \brief Sets the list of targets this listener should listen to.
    /// \param newTargets The new list of targets.
    /// \sa targets()
    void setTargets(std::initializer_list<T*> newTargets) {
        _impl->setTargets(std::move(newTargets));
    }

    /// \brief Adds a new object to the list of targets this listener should listen to.
    void push_back(T* target) { _impl->push_back(target); }

    /// \brief Inserts a new object into the list of targets this listener should listen to.
    void insert(int index, T* target) { _impl->insert(index, target); }

    /// \brief Replaces an object in the list of targets this listener should listen to.
    void set(int index, T* target) { _impl->set(index, target); }

    /// \brief Clears the list of targets.
    void clear() { _impl->clear(); }

    /// \brief Removes an object from the list of targets this listener should listen to.
    void remove(T* target) { _impl->remove(target); }

    /// \brief Removes an object from the list of targets this listener should listen to.
    void remove(int index) { _impl->remove(index); }

private:

    OORef<VectorRefTargetListenerImpl> _impl = OORef<VectorRefTargetListenerImpl>::create();
};

}   // End of namespace
