///////////////////////////////////////////////////////////////////////////////
//
//  Copyright (2013) Alexander Stukowski
//
//  This file is part of OVITO (Open Visualization Tool).
//
//  OVITO is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  OVITO is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
///////////////////////////////////////////////////////////////////////////////

#ifndef __OVITO_REFTARGET_LISTENER_H
#define __OVITO_REFTARGET_LISTENER_H

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

namespace Ovito { OVITO_BEGIN_INLINE_NAMESPACE(ObjectSystem)

/**
 * \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.
 *
 * This class is designed to be used on the stack or as a member of another
 * class that is not derived from RefMaker but still wants to receive notification events
 * from a RefTarget.
 */
class OVITO_CORE_EXPORT RefTargetListenerBase : public RefMaker
{
public:

	/// \brief The default constructor.
	RefTargetListenerBase() : RefMaker(nullptr) {
		INIT_PROPERTY_FIELD(RefTargetListenerBase::_target);
	}

	/// \brief Destructor.
	virtual ~RefTargetListenerBase() {
		clearAllReferences();
	}

	/// \brief Returns the current target this listener is listening to.
	/// \return The current target object or \c NULL.
	/// \sa setTarget()
	RefTarget* target() const { return _target; }

	/// \brief Sets the current target this listener should listen to.
	/// \param newTarget The new target or \c NULL.
	/// \sa target()
	void setTarget(RefTarget* newTarget) { _target = newTarget; }

Q_SIGNALS:

	/// \brief This Qt signal is emitted by the listener each time it receives a notification
	///        event from the current target.
	/// \param event The notification event.
	void notificationEvent(ReferenceEvent* event);

protected:

	/// \brief This method is called after the reference counter of this object has reached zero
	///        and before the object is being deleted.
	virtual void aboutToBeDeleted() override {
		OVITO_ASSERT_MSG(false, "RefTargetListenerBase::aboutToBeDeleted()", "Invalid use of this class. A RefTargetListener should not be used with reference counting pointers.");
	}

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

private:

	/// The RefTarget which is being monitored by this listener.
	ReferenceField<RefTarget> _target;

	Q_OBJECT
	OVITO_OBJECT

	DECLARE_REFERENCE_FIELD(_target);
};

/**
 * \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.
 *
 * This is the templated version of RefTargetListenerBase class.
 */
template<typename T>
class RefTargetListener : public RefTargetListenerBase
{
public:

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

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

/**
 * \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 class is designed to be used on the stack or as a member of another
 * class that is not derived from RefMaker but still wants to receive notification events
 * from several RefTarget objects.
 */
class OVITO_CORE_EXPORT VectorRefTargetListenerBase : public RefMaker
{
public:

	/// \brief The default constructor.
	VectorRefTargetListenerBase() : RefMaker(nullptr) {
		INIT_PROPERTY_FIELD(VectorRefTargetListenerBase::_targets);
	}

	/// \brief Destructor.
	virtual ~VectorRefTargetListenerBase() {
		clearAllReferences();
	}

	/// \brief Returns the list of targets this listener is listening to.
	/// \return The current list of target objects.
	/// \sa setTargets()
	const QVector<RefTarget*>& targets() const { return _targets; }

	/// \brief Sets the list of targets this listener should listen to.
	/// \param newTargets The new list of targets.
	/// \sa targets()
	void setTargets(const QVector<RefTarget*>& newTargets) { _targets = newTargets; }

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

	/// \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(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(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(index);
	}

	/// \brief Removes an object from the list of targets this listener should listen to.
	void remove(int index) {
		_targets.remove(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, ReferenceEvent* event);

protected:

	/// \brief This method is called after the reference counter of this object has reached zero
	///        and before the object is being deleted.
	virtual void aboutToBeDeleted() override {
		OVITO_ASSERT_MSG(false, "VectorRefTargetListenerBase::aboutToBeDeleted()", "Invalid use of this class. A VectorRefTargetListener should not be used with reference counting pointers.");
	}

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

private:

	/// The list of RefTargets which are being monitored by this listener.
	VectorReferenceField<RefTarget> _targets;

	Q_OBJECT
	OVITO_OBJECT

	DECLARE_VECTOR_REFERENCE_FIELD(_targets);
};

/**
 * \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 VectorRefTargetListenerBase
{
public:

	/// \brief Returns the list of targets this listener is listening to.
	/// \return The current list of target objects.
	/// \sa setTargets()
	const QVector<T*>& targets() const { return reinterpret_cast<const QVector<T*>&>(VectorRefTargetListenerBase::targets()); }

	/// \brief Sets the list of targets this listener should listen to.
	/// \param newTargets The new list of targets.
	/// \sa targets()
	void setTargets(const QVector<T*>& newTargets) { VectorRefTargetListenerBase::setTargets(reinterpret_cast<const QVector<RefTarget*>&>(newTargets)); }

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

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

OVITO_END_INLINE_NAMESPACE
}	// End of namespace

#endif // __OVITO_REFTARGET_LISTENER_H

