//
//  Copyright (C) 2009-2012 Artyom Beilis (Tonkikh)
//
//  Distributed under the Boost Software License, Version 1.0. (See
//  accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOSTER_FUNCTION_H
#define BOOSTER_FUNCTION_H

#include <booster/backtrace.h>
#include <booster/clone_ptr.h>

namespace booster {
	template<typename Type>
	class function;

	///
	/// \brief This exception is thrown in case of an attempt to call to
	/// unassigned \ref booster::function
	///
	class bad_function_call : public booster::runtime_error {
	public:
		bad_function_call() : 
			booster::runtime_error("bad_function_call")
		{
		}
	};


	#ifdef BOOSTER_DOXYGEN_DOCS
	///
	/// \brief This is Booster's implementation of std::tr1::function/booster::function.
	///
	/// This function is created from generic object that can be "called" i.e. 
	/// a class with operator() or function pointer that has same signature as
	/// the function.
	///
	/// See: http://www.boost.org/doc/html/function.html
	///
	/// Notes:
	///
	/// - this code is not taken from Boost and has slightly different interface.
	/// - as most of compilers do not support Variadic templates yet, this class
	///   is explicitly specialized for Params of size 0 to 8. So maximal amout
	///   of parameters that can be used is 8.
	///
	///
	template<typename Result,typename ...Params>			
	class function<Result(Params...)>				
	{									
	public:									
		///
		/// Type of result, for use with boost::bind
		///
		typedef Result result_type;		
		///
		/// Default constructor, creates an empty functions
		///			
		function();
		///
		/// Creates a function from a functional \a func of type F. func
		/// should be copyable. It is copied and stored inside function object.
		///
		template<typename F>						
		function(F func);
		
		///
		/// Copy function, Copies underlying functional object.
		///
		function(function const &other);

		///
		/// Assign a functional \a func of type F. func
		/// should be copyable. It is copied and stored inside function object.
		///
		template<typename F>						
		function const &operator=(F func);

		///
		/// Assignment operator. Copies underlying functional object.
		/// 
		function const &operator=(function const &other);

		///
		/// Calls underling functional object. If the function is empty, throws bad_function_call.
		///
		result_type operator()(Params... params) const;
		///
		/// Return true if the function is empty
		///
		bool empty() const;
		///
		/// Returns true if the function is not empty
		///
		operator bool() const;

		///
		/// Swaps two functional object. Does not throw.
		///
		void swap(function &other);
	};			

	#else

	#define BOOSTER_FUNCTION						\
	template<typename Result BOOSTER_TEMPLATE_PARAMS >			\
	class function<Result(BOOSTER_TEMPLATE_TYPE_PARAMS)>			\
	{									\
	public:									\
		typedef Result result_type;					\
		struct callable {						\
			virtual Result call(BOOSTER_TYPE_PARAMS) =0;		\
			virtual callable *clone() const = 0;			\
			virtual ~callable(){}					\
		};								\
										\
		template<typename R,typename F>					\
		struct callable_impl : public callable {			\
			F func;							\
			callable_impl(F f) : func(f){}				\
			virtual R call(BOOSTER_TYPE_PARAMS) 			\
			{  return func(BOOSTER_CALL_PARAMS); }			\
			virtual callable *clone() const				\
			{ return new callable_impl<R,F>(func); }		\
		};								\
		template<typename F>						\
		struct callable_impl<void,F> : public callable {		\
			F func;							\
			callable_impl(F f) : func(f){}				\
			virtual void call(BOOSTER_TYPE_PARAMS) 			\
			{  func(BOOSTER_CALL_PARAMS); }				\
			virtual callable *clone() const				\
			{ return new callable_impl<void,F>(func); }		\
		};								\
		function(){}							\
		template<typename F>						\
		function(F func) : call_ptr(new callable_impl<Result,F>(func)) 	\
		{}								\
		function(function const &other) : call_ptr(other.call_ptr) {}	\
		template<typename F>						\
		function const &operator=(F func)				\
		{ 								\
			call_ptr.reset(new callable_impl<Result,F>(func));	\
			return *this;						\
		}								\
		function const &operator=(function const &other)		\
		{ 								\
			if(this != &other) { call_ptr=other.call_ptr; } 	\
			return *this;						\
		}								\
		Result operator()(BOOSTER_TYPE_PARAMS) const			\
		{ 								\
			if(!call_ptr.get()) throw bad_function_call();		\
			return call_ptr->call(BOOSTER_CALL_PARAMS); 		\
		}								\
		bool empty() const { return call_ptr.get()==0; }		\
		operator bool() const { return !empty(); }			\
		void swap(function &other) { call_ptr.swap(other.call_ptr); }	\
	private:								\
		clone_ptr<callable> call_ptr;					\
	};									\

	#define BOOSTER_TEMPLATE_PARAMS
	#define BOOSTER_TEMPLATE_TYPE_PARAMS
	#define BOOSTER_TYPE_PARAMS
	#define BOOSTER_CALL_PARAMS
	BOOSTER_FUNCTION
	#undef BOOSTER_TEMPLATE_PARAMS
	#undef BOOSTER_TEMPLATE_TYPE_PARAMS
	#undef BOOSTER_TYPE_PARAMS
	#undef BOOSTER_CALL_PARAMS

	#define BOOSTER_TEMPLATE_PARAMS ,typename P1
	#define BOOSTER_TEMPLATE_TYPE_PARAMS  P1
	#define BOOSTER_TYPE_PARAMS  P1 a1
	#define BOOSTER_CALL_PARAMS a1
	BOOSTER_FUNCTION
	#undef BOOSTER_TEMPLATE_PARAMS
	#undef BOOSTER_TEMPLATE_TYPE_PARAMS
	#undef BOOSTER_TYPE_PARAMS
	#undef BOOSTER_CALL_PARAMS

	#define BOOSTER_TEMPLATE_PARAMS ,typename P1,typename P2
	#define BOOSTER_TEMPLATE_TYPE_PARAMS  P1, P2
	#define BOOSTER_TYPE_PARAMS  P1 a1,P2 a2
	#define BOOSTER_CALL_PARAMS a1,a2
	BOOSTER_FUNCTION
	#undef BOOSTER_TEMPLATE_PARAMS
	#undef BOOSTER_TEMPLATE_TYPE_PARAMS
	#undef BOOSTER_TYPE_PARAMS
	#undef BOOSTER_CALL_PARAMS
			
	#define BOOSTER_TEMPLATE_PARAMS ,typename P1,typename P2,typename P3
	#define BOOSTER_TEMPLATE_TYPE_PARAMS  P1, P2, P3
	#define BOOSTER_TYPE_PARAMS  P1 a1,P2 a2,P3 a3
	#define BOOSTER_CALL_PARAMS a1,a2,a3
	BOOSTER_FUNCTION
	#undef BOOSTER_TEMPLATE_PARAMS
	#undef BOOSTER_TEMPLATE_TYPE_PARAMS
	#undef BOOSTER_TYPE_PARAMS
	#undef BOOSTER_CALL_PARAMS
	
	#define BOOSTER_TEMPLATE_PARAMS ,typename P1,typename P2,typename P3,typename P4
	#define BOOSTER_TEMPLATE_TYPE_PARAMS  P1, P2, P3, P4
	#define BOOSTER_TYPE_PARAMS  P1 a1,P2 a2,P3 a3,P4 a4
	#define BOOSTER_CALL_PARAMS a1,a2,a3,a4
	BOOSTER_FUNCTION
	#undef BOOSTER_TEMPLATE_PARAMS
	#undef BOOSTER_TEMPLATE_TYPE_PARAMS
	#undef BOOSTER_TYPE_PARAMS
	#undef BOOSTER_CALL_PARAMS

	#define BOOSTER_TEMPLATE_PARAMS ,typename P1,typename P2,typename P3,typename P4,typename P5
	#define BOOSTER_TEMPLATE_TYPE_PARAMS  P1, P2, P3, P4, P5
	#define BOOSTER_TYPE_PARAMS  P1 a1,P2 a2,P3 a3,P4 a4,P5 a5
	#define BOOSTER_CALL_PARAMS a1,a2,a3,a4,a5
	BOOSTER_FUNCTION
	#undef BOOSTER_TEMPLATE_PARAMS
	#undef BOOSTER_TEMPLATE_TYPE_PARAMS
	#undef BOOSTER_TYPE_PARAMS
	#undef BOOSTER_CALL_PARAMS

	#define BOOSTER_TEMPLATE_PARAMS ,typename P1,typename P2,typename P3,typename P4,typename P5,typename P6
	#define BOOSTER_TEMPLATE_TYPE_PARAMS  P1, P2, P3, P4, P5, P6
	#define BOOSTER_TYPE_PARAMS  P1 a1,P2 a2,P3 a3,P4 a4,P5 a5,P6 a6
	#define BOOSTER_CALL_PARAMS a1,a2,a3,a4,a5,a6
	BOOSTER_FUNCTION
	#undef BOOSTER_TEMPLATE_PARAMS
	#undef BOOSTER_TEMPLATE_TYPE_PARAMS
	#undef BOOSTER_TYPE_PARAMS
	#undef BOOSTER_CALL_PARAMS
	
	#define BOOSTER_TEMPLATE_PARAMS ,typename P1,typename P2,typename P3,typename P4,typename P5,typename P6,typename P7
	#define BOOSTER_TEMPLATE_TYPE_PARAMS  P1, P2, P3, P4, P5, P6, P7
	#define BOOSTER_TYPE_PARAMS  P1 a1,P2 a2,P3 a3,P4 a4,P5 a5,P6 a6,P7 a7
	#define BOOSTER_CALL_PARAMS a1,a2,a3,a4,a5,a6,a7
	BOOSTER_FUNCTION
	#undef BOOSTER_TEMPLATE_PARAMS
	#undef BOOSTER_TEMPLATE_TYPE_PARAMS
	#undef BOOSTER_TYPE_PARAMS
	#undef BOOSTER_CALL_PARAMS
	
	#define BOOSTER_TEMPLATE_PARAMS ,typename P1,typename P2,typename P3,typename P4,typename P5,typename P6,typename P7,typename P8
	#define BOOSTER_TEMPLATE_TYPE_PARAMS  P1, P2, P3, P4, P5, P6, P7, P8
	#define BOOSTER_TYPE_PARAMS  P1 a1,P2 a2,P3 a3,P4 a4,P5 a5,P6 a6,P7 a7,P8 a8
	#define BOOSTER_CALL_PARAMS a1,a2,a3,a4,a5,a6,a7,a8
	BOOSTER_FUNCTION
	#undef BOOSTER_TEMPLATE_PARAMS
	#undef BOOSTER_TEMPLATE_TYPE_PARAMS
	#undef BOOSTER_TYPE_PARAMS
	#undef BOOSTER_CALL_PARAMS

	#undef BOOSTER_FUNCTION

	#endif // DOC

} // booster


#endif
