//------------------------------------------------------------------------------
//	Copyright (C) 2002, 2004, 2007 Humboldt-Universitaet zu Berlin
//
//	This library is free software; you can redistribute it and/or
//	modify it under the terms of the GNU Lesser General Public
//	License as published by the Free Software Foundation; either
//	version 2.1 of the License, or (at your option) any later version.
//
//	This library 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
//	Lesser General Public License for more details.
//
//	You should have received a copy of the GNU Lesser General Public
//	License along with this library; if not, write to the Free Software
//	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/**
 * @file Observable.h
 * @author Ralf Gerstenberger
 * @date created at 2002/02/11
 * @brief Declaration and implementation of odemx::data::Observable
 * @since 1.0
 */

#if defined(_MSC_VER)
#pragma warning(disable : 4250 4786)
#endif

#ifndef ODEMX_OBSERVABLE_INCLUDED
#define ODEMX_OBSERVABLE_INCLUDED

#include <odemx/setup.h>

#include <algorithm>
#include <list>

namespace odemx {
namespace data {

/**
 * @brief Observable provides management of observers.
 * @ingroup data
 * @author Ralf Gerstenberger
 *
 * Observation is meant as an association scheme between two
 * objects, one observed, one observing, that share an individual (observation)
 * interface. The observed object reports events and attribute changes
 * of some meaning through the interface to the observer, which implements this
 * xxxObserver interface. The xxxObserver interface is defined along with the
 * class of the observed object, where xxx is replaced with the class name.
 * An Observer is a client to its observed object and managed by the class
 * template Observable. A class that supports the observation scheme as a type
 * for observed objects should use Observable as a base class.
 *
 * @note If ODEMX_USE_OBSERVATION is not defined in odemx/setup.h,
 * this class is compiled as empty (base) class.
 *
 * @since 1.0
 */
template< typename ObserverT >
class Observable
{

// this is essentially an empty base class if observation is disabled
#ifdef ODEMX_USE_OBSERVATION

public:
	/// Construction
	Observable( ObserverT* obs = 0 )
	{
		if( obs != 0 )
		{
			observers.push_back( obs );
		}
	}

	/// Get observer list
	const std::list<ObserverT*>& getObservers()
	{
		return observers;
	}

	/// Add an observer to the list
	void addObserver( ObserverT* newObserver )
	{
		typename std::list< ObserverT* >::iterator found
			= std::find( observers.begin(), observers.end(), newObserver );

		if( found == observers.end() )
		{
			observers.push_back( newObserver );
		}
	}

	/// Remove an observer from the list
	void removeObserver( ObserverT* ob ) {observers.remove(ob);}

private:
	/// The list of registered observers
	std::list< ObserverT* > observers;

#else /* ODEMX_USE_OBSERVATION not defined */

public:
	// for compatibility with derived classes, this empty constructor is needed
	Observable( ObserverT* obs = 0 ) {}

#endif /* ODEMX_USE_OBSERVATION */

};

#ifdef ODEMX_USE_OBSERVATION

/**
	\brief Broadcast event

	\ingroup data

	\param ObserverType
		Type of observer (xxxObserver)

	\param Event
		Event handler function without on ( Create(this)->onCreate(this) )
*/
#define ODEMX_OBS(ObserverType, Event) \
{\
	for(std::list<ObserverType* >::const_iterator \
		i = data::Observable<ObserverType >::getObservers().begin(); \
		i != data::Observable<ObserverType >::getObservers().end(); ++i) \
		{ \
			(*i)->on##Event;\
		} \
}

/**
	\brief Broadcast attribute change

	\ingroup data

	\param ObserverType
		Type of observer (xxxObserver)

	\param Attribute
		Changed attribute name

	\param oldValue
		Old value of attribute

	\param newValue
		New value of attribute
*/
#define ODEMX_OBS_ATTR(ObserverType, Attribute, oldValue, newValue) \
{\
	for(std::list<ObserverType* >::const_iterator \
		i = data::Observable<ObserverType >::getObservers().begin(); \
		i != data::Observable<ObserverType >::getObservers().end(); ++i) \
		{ \
			(*i)->onChange##Attribute(this, oldValue, newValue); \
		} \
}


/**
	\brief Broadcast event with observer interface template

	\ingroup data

	\param ObserverType
		Type of observer (xxxObserver)

	\param Event
		Event handler function without on ( Create(this)->onCreate(this) )
*/
#define ODEMX_OBS_T(ObserverType, Event) \
{\
	for( typename std::list<ObserverType* >::const_iterator \
		i = data::Observable<ObserverType >::getObservers().begin(); \
		i != data::Observable<ObserverType >::getObservers().end(); ++i) \
		{ \
			(*i)->on##Event;\
		} \
}

/**
	\brief Broadcast attribute change with observer interface template

	\ingroup data

	\param ObserverType
		Type of observer (xxxObserver)

	\param Attribute
		Changed attribute name

	\param oldValue
		Old value of attribute

	\param newValue
		New value of attribute
*/
#define ODEMX_OBS_ATTR_T(ObserverType, Attribute, oldValue, newValue) \
{\
	for( typename std::list<ObserverType* >::const_iterator \
		i = data::Observable<ObserverType >::getObservers().begin(); \
		i != data::Observable<ObserverType >::getObservers().end(); ++i) \
		{ \
			(*i)->onChange##Attribute(this, oldValue, newValue); \
		} \
}

#else /* ODEMX_USE_OBSERVATION not defined */

// disable all observation macros
#define ODEMX_OBS(ObserverType, Event)
#define ODEMX_OBS_ATTR(ObserverType, Attribute, oldValue, newValue)
#define ODEMX_OBS_T(ObserverType, Event)
#define ODEMX_OBS_ATTR_T(ObserverType, Attribute, oldValue, newValue)

#endif /* ODEMX_USE_OBSERVATION */

} } // namespace odemx::data

#endif /* ODEMX_OBSERVABLE_INCLUDED */
