// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#ifndef NLQT_CALLBACK_H
#define NLQT_CALLBACK_H
#include <nel/misc/types_nl.h>

// STL includes

// NeL includes
#include <nel/misc/debug.h>

// Project includes

namespace NLQT {

#define NLQT_CALLBACK_TEMPLATE \
/** \
 * \brief NLQT_CALLBACK_ARGS_CLASS \
 * \date 2009-03-03 18:09GMT \
 * \author Jan Boon (Kaetemi) \
 * Awesome callback template \
 */ \
template<typename TReturn NLQT_CALLBACK_ARGS_TYPENAME> \
class NLQT_CALLBACK_ARGS_CLASS \
{ \
	/* Very simple reference counting callback base */ \
	class CCallbackBase \
	{ \
	public: \
		CCallbackBase() : m_RefCount(0) \
		{ \
			 \
		} \
		 \
		virtual ~CCallbackBase() \
		{ \
			nlassert(!m_RefCount); \
		} \
		 \
		void refAdd() \
		{ \
			++m_RefCount; \
		} \
		 \
		void refRemove() \
		{ \
			--m_RefCount; \
			if (!m_RefCount) \
				delete this; \
		} \
		 \
		virtual TReturn callback(NLQT_CALLBACK_ARGS_DECL) = 0; \
		 \
		virtual bool equals(const CCallbackBase *callbackBase) = 0; \
		 \
		/* disable copy */ \
		CCallbackBase(const CCallbackBase &); \
		CCallbackBase &operator=(const CCallbackBase &); \
		 \
	private: \
		uint m_RefCount; \
	}; \
	 \
	typedef TReturn TCallbackFunction(NLQT_CALLBACK_ARGS_DECL); \
	class CCallbackFunction : public CCallbackBase \
	{ \
	public: \
		CCallbackFunction(TCallbackFunction *callbackFunction) : m_CallbackFunction(callbackFunction) \
		{ \
			nlassert(m_CallbackFunction); \
		} \
		 \
		virtual ~CCallbackFunction() \
		{ \
			m_CallbackFunction = NULL; \
		} \
		 \
		virtual TReturn callback(NLQT_CALLBACK_ARGS_DECL) \
		{ \
			return m_CallbackFunction(NLQT_CALLBACK_ARGS_IMPL); \
		} \
		 \
		virtual bool equals(const CCallbackBase *callbackBase) \
		{ \
			const CCallbackFunction *callbackFunction = \
				dynamic_cast<const CCallbackFunction *>(callbackBase); \
			if (!callbackFunction) return false; \
			return m_CallbackFunction == callbackFunction->m_CallbackFunction; \
		} \
		 \
	private: \
		TCallbackFunction *m_CallbackFunction; \
	}; \
	 \
	template<typename TClass> \
	class CCallbackMethod : public CCallbackBase \
	{ \
		typedef TReturn (TClass::*TCallbackMethod)(NLQT_CALLBACK_ARGS_DECL); \
	public: \
		CCallbackMethod(TClass *callbackObject, TCallbackMethod callbackMethod) : m_CallbackObject(callbackObject), m_CallbackMethod(callbackMethod) \
		{ \
			nlassert(m_CallbackObject); \
			nlassert(m_CallbackMethod); \
		} \
		 \
		virtual ~CCallbackMethod() \
		{ \
			m_CallbackObject = NULL; \
			m_CallbackMethod = NULL; \
		} \
		 \
		virtual TReturn callback(NLQT_CALLBACK_ARGS_DECL) \
		{ \
			return (m_CallbackObject->*m_CallbackMethod)(NLQT_CALLBACK_ARGS_IMPL); \
		} \
		 \
		virtual bool equals(const CCallbackBase *callbackBase) \
		{ \
			const CCallbackMethod *callbackMethod = \
				dynamic_cast<const CCallbackMethod *>(callbackBase); \
			if (!callbackMethod) return false; \
			return m_CallbackObject == callbackMethod->m_CallbackObject \
				&& m_CallbackMethod == callbackMethod->m_CallbackMethod; \
		} \
		 \
	private: \
		TClass *m_CallbackObject; \
		TCallbackMethod m_CallbackMethod; \
	}; \
	 \
public: \
	CCallback() : m_CallbackBase(NULL) \
	{ \
		 \
	} \
	 \
	CCallback(TCallbackFunction *callbackFunction) : m_CallbackBase(new CCallbackFunction(callbackFunction)) \
	{ \
		nlassert(m_CallbackBase); \
		m_CallbackBase->refAdd(); \
	} \
	 \
	template<typename TClass> \
	CCallback(TClass *callbackObject, TReturn (TClass::*callbackMethod)(NLQT_CALLBACK_ARGS_DECL)) : m_CallbackBase(new CCallbackMethod<TClass>(callbackObject, callbackMethod)) \
	{ \
		nlassert(m_CallbackBase); \
		m_CallbackBase->refAdd(); \
	} \
	 \
	CCallback(const CCallback &callback) \
	{ \
		m_CallbackBase = callback.m_CallbackBase; \
		if (m_CallbackBase) \
			m_CallbackBase->refAdd(); \
	} \
	 \
	CCallback &operator=(const CCallback &callback) \
	{ \
		if (m_CallbackBase != callback.m_CallbackBase) \
		{ \
			if (m_CallbackBase) \
				m_CallbackBase->refRemove(); \
			m_CallbackBase = callback.m_CallbackBase; \
			if (m_CallbackBase) \
				m_CallbackBase->refAdd(); \
		} \
		return *this; \
	} \
	 \
	~CCallback() \
	{ \
		if (m_CallbackBase) \
		{ \
			m_CallbackBase->refRemove(); \
			m_CallbackBase = NULL; \
		} \
	} \
	 \
	TReturn callback(NLQT_CALLBACK_ARGS_DECL) \
	{ \
		nlassert(m_CallbackBase); \
		return m_CallbackBase->callback(NLQT_CALLBACK_ARGS_IMPL); \
	} \
	 \
	TReturn operator()(NLQT_CALLBACK_ARGS_DECL) \
	{ \
		nlassert(m_CallbackBase); \
		return m_CallbackBase->callback(NLQT_CALLBACK_ARGS_IMPL); \
	} \
	 \
	bool valid() const \
	{ \
		return m_CallbackBase != NULL; \
	} \
	 \
	operator bool() const \
	{ \
		return m_CallbackBase != NULL; \
	} \
	 \
	bool operator==(const CCallback &callback) \
	{ \
		return m_CallbackBase->equals(callback.m_CallbackBase); \
	} \
	 \
private: \
	CCallbackBase *m_CallbackBase; \
	 \
}; /* class CCallback */ \

template<typename TReturn, typename TArgsA = void, typename TArgsB = void, typename TArgsC = void, typename TArgsD = void, typename TArgsE = void, typename TArgsF = void, typename TArgsG = void, typename TDummy = void>
class CCallback;

#define NLQT_CALLBACK_ARGS_CLASS CCallback<TReturn, void, void, void, void, void, void, void, void>
#define NLQT_CALLBACK_ARGS_TYPENAME 
#define NLQT_CALLBACK_ARGS_DECL 
#define NLQT_CALLBACK_ARGS_IMPL 
NLQT_CALLBACK_TEMPLATE
#undef NLQT_CALLBACK_ARGS_CLASS
#undef NLQT_CALLBACK_ARGS_TYPENAME
#undef NLQT_CALLBACK_ARGS_DECL
#undef NLQT_CALLBACK_ARGS_IMPL

#define NLQT_CALLBACK_ARGS_CLASS CCallback<TReturn, TArgsA, void, void, void, void, void, void, void>
#define NLQT_CALLBACK_ARGS_TYPENAME , typename TArgsA
#define NLQT_CALLBACK_ARGS_DECL TArgsA argsA
#define NLQT_CALLBACK_ARGS_IMPL argsA
NLQT_CALLBACK_TEMPLATE
#undef NLQT_CALLBACK_ARGS_CLASS
#undef NLQT_CALLBACK_ARGS_TYPENAME
#undef NLQT_CALLBACK_ARGS_DECL
#undef NLQT_CALLBACK_ARGS_IMPL

#define NLQT_CALLBACK_ARGS_CLASS CCallback<TReturn, TArgsA, TArgsB, void, void, void, void, void, void>
#define NLQT_CALLBACK_ARGS_TYPENAME , typename TArgsA, typename TArgsB
#define NLQT_CALLBACK_ARGS_DECL TArgsA argsA, TArgsB argsB
#define NLQT_CALLBACK_ARGS_IMPL argsA, argsB
NLQT_CALLBACK_TEMPLATE
#undef NLQT_CALLBACK_ARGS_CLASS
#undef NLQT_CALLBACK_ARGS_TYPENAME
#undef NLQT_CALLBACK_ARGS_DECL
#undef NLQT_CALLBACK_ARGS_IMPL

#define NLQT_CALLBACK_ARGS_CLASS CCallback<TReturn, TArgsA, TArgsB, TArgsC, void, void, void, void, void>
#define NLQT_CALLBACK_ARGS_TYPENAME , typename TArgsA, typename TArgsB, typename TArgsC
#define NLQT_CALLBACK_ARGS_DECL TArgsA argsA, TArgsB argsB, TArgsC argsC
#define NLQT_CALLBACK_ARGS_IMPL argsA, argsB, argsC
NLQT_CALLBACK_TEMPLATE
#undef NLQT_CALLBACK_ARGS_CLASS
#undef NLQT_CALLBACK_ARGS_TYPENAME
#undef NLQT_CALLBACK_ARGS_DECL
#undef NLQT_CALLBACK_ARGS_IMPL

#define NLQT_CALLBACK_ARGS_CLASS CCallback<TReturn, TArgsA, TArgsB, TArgsC, TArgsD, void, void, void, void>
#define NLQT_CALLBACK_ARGS_TYPENAME , typename TArgsA, typename TArgsB, typename TArgsC, typename TArgsD
#define NLQT_CALLBACK_ARGS_DECL TArgsA argsA, TArgsB argsB, TArgsC argsC, TArgsD argsD
#define NLQT_CALLBACK_ARGS_IMPL argsA, argsB, argsC, argsD
NLQT_CALLBACK_TEMPLATE
#undef NLQT_CALLBACK_ARGS_CLASS
#undef NLQT_CALLBACK_ARGS_TYPENAME
#undef NLQT_CALLBACK_ARGS_DECL
#undef NLQT_CALLBACK_ARGS_IMPL

#define NLQT_CALLBACK_ARGS_CLASS CCallback<TReturn, TArgsA, TArgsB, TArgsC, TArgsD, TArgsE, void, void, void>
#define NLQT_CALLBACK_ARGS_TYPENAME , typename TArgsA, typename TArgsB, typename TArgsC, typename TArgsD, typename TArgsE
#define NLQT_CALLBACK_ARGS_DECL TArgsA argsA, TArgsB argsB, TArgsC argsC, TArgsD argsD, TArgsE argsE
#define NLQT_CALLBACK_ARGS_IMPL argsA, argsB, argsC, argsD, argsE
NLQT_CALLBACK_TEMPLATE
#undef NLQT_CALLBACK_ARGS_CLASS
#undef NLQT_CALLBACK_ARGS_TYPENAME
#undef NLQT_CALLBACK_ARGS_DECL
#undef NLQT_CALLBACK_ARGS_IMPL

#define NLQT_CALLBACK_ARGS_CLASS CCallback<TReturn, TArgsA, TArgsB, TArgsC, TArgsD, TArgsE, TArgsF, void, void>
#define NLQT_CALLBACK_ARGS_TYPENAME , typename TArgsA, typename TArgsB, typename TArgsC, typename TArgsD, typename TArgsE, typename TArgsF
#define NLQT_CALLBACK_ARGS_DECL TArgsA argsA, TArgsB argsB, TArgsC argsC, TArgsD argsD, TArgsE argsE, TArgsF argsF
#define NLQT_CALLBACK_ARGS_IMPL argsA, argsB, argsC, argsD, argsE, argsF
NLQT_CALLBACK_TEMPLATE
#undef NLQT_CALLBACK_ARGS_CLASS
#undef NLQT_CALLBACK_ARGS_TYPENAME
#undef NLQT_CALLBACK_ARGS_DECL
#undef NLQT_CALLBACK_ARGS_IMPL

#define NLQT_CALLBACK_ARGS_CLASS CCallback<TReturn, TArgsA, TArgsB, TArgsC, TArgsD, TArgsE, TArgsF, TArgsG, void>
#define NLQT_CALLBACK_ARGS_TYPENAME , typename TArgsA, typename TArgsB, typename TArgsC, typename TArgsD, typename TArgsE, typename TArgsF, typename TArgsG
#define NLQT_CALLBACK_ARGS_DECL TArgsA argsA, TArgsB argsB, TArgsC argsC, TArgsD argsD, TArgsE argsE, TArgsF argsF, TArgsG argsG
#define NLQT_CALLBACK_ARGS_IMPL argsA, argsB, argsC, argsD, argsE, argsF, argsG
NLQT_CALLBACK_TEMPLATE
#undef NLQT_CALLBACK_ARGS_CLASS
#undef NLQT_CALLBACK_ARGS_TYPENAME
#undef NLQT_CALLBACK_ARGS_DECL
#undef NLQT_CALLBACK_ARGS_IMPL
#undef NLQT_CALLBACK_ARGS_CLASSNAME

#undef NLQT_CALLBACK_TEMPLATE

typedef CCallback<void> CEmptyCallback;

} /* namespace NLQT */

#endif /* #ifndef NLQT_CALLBACK_H */

/* end of file */