/** @file functioncallwrappers.h
*	Wrappers to pack payloads for BaseType Function Calls
*	@author Nick Beatty (nbeatty@ea.com)
*
*	EABaseTypes
*	(c) 2011 Electronic Arts Inc.
*/

#ifndef EABASETYPES_FUNCTIONCALLWRAPPERS_H
#define EABASETYPES_FUNCTIONCALLWRAPPERS_H

#include "EABaseTypes/functor.h"
#include "EABaseTypes/iencoder.h"
#include "EABaseTypes/nativeencoder.h"

namespace EA
{
	namespace Types
	{
		/* protected inline */ void Function::Call(NativeDecoderParams* decoderParams, NativeEncoder* encoder)
		{
			IEncoderImpl<NativeEncoder> defaultEncoder(encoder);

			Call(Native::CALLER_TYPE,
				 decoderParams,
				 encoder,
				 NativeDecoder<IEncoder>::Decode,
				 &defaultEncoder);
		}

		template<typename R>
		/* inline */ R Function::Call()
		{
			if(GetFunctionType() == EA::Type::TypeOf<Functor<R> >())
			{
				return (*static_cast<Functor<R>*>(this))();
			}
			else
			{
				Factory* factory = mFactory;

				NativeDecoderParams decoderParams(factory, NULL, 0);
				
				typedef NativeTraits<R> RTraits;
				typename RTraits::ReturnEncodeValue result(factory);

				typename RTraits::EncoderContext resultContext(factory, NULL, RTraits::GetEncodePointer(result));
				EndContext endContext;

				NativeEncoder::Argument encoderParams[2] =
					{
						&resultContext,
						&endContext
					};
					
				NativeEncoder encoder(encoderParams);

				Call(&decoderParams, &encoder);

				return RTraits::GetReturnValue(result);
			}
		}

		template<typename R, typename P1>
		/* inline */ R Function::Call(P1 p1)
		{
			if(GetFunctionType() == EA::Type::TypeOf<Functor1<R, P1> >())
			{
				return (*static_cast<Functor1<R, P1>*>(this))(p1);
			}
			else
			{
				typedef NativeTraits<P1> P1Traits;

				NativeDecoderArgument decoderArguments[1] = 
					{ 
							{ P1Traits::GetDecoderType(), P1Traits::GetDecodePointer(p1), P1Traits::GetDecodeFunction() }
					};
				
				Factory* factory = mFactory;

				NativeDecoderParams decoderParams(factory, decoderArguments, 1);

				typedef NativeTraits<R> RTraits;
				typename RTraits::ReturnEncodeValue result(factory);

				typename RTraits::EncoderContext resultContext(factory, NULL, RTraits::GetEncodePointer(result));
				EndContext endContext;

				NativeEncoder::Argument encoderParams[2] =
					{
						&resultContext,
						&endContext
					};
					
				NativeEncoder encoder(encoderParams);

				Call(&decoderParams, &encoder);

				return RTraits::GetReturnValue(result);
			}
		}

		template<typename R, typename P1, typename P2>
		/* inline */ R Function::Call(P1 p1, P2 p2)
		{
			if(GetFunctionType() == EA::Type::TypeOf<Functor2<R, P1, P2> >())
			{
				return (*static_cast<Functor2<R, P1, P2>*>(this))(p1, p2);
			}
			else
			{
				typedef NativeTraits<P1> P1Traits;
				typedef NativeTraits<P2> P2Traits;

				NativeDecoderArgument decoderArguments[2] =
					{
						{ P1Traits::GetDecoderType(), P1Traits::GetDecodePointer(p1), P1Traits::GetDecodeFunction() },
						{ P2Traits::GetDecoderType(), P2Traits::GetDecodePointer(p2), P2Traits::GetDecodeFunction() }
					};

				Factory* factory = mFactory;

				NativeDecoderParams decoderParams(factory, decoderArguments, 2);

				typedef NativeTraits<R> RTraits;
				typename RTraits::ReturnEncodeValue result(factory);

				typename RTraits::EncoderContext resultContext(factory, NULL, RTraits::GetEncodePointer(result));
				EndContext endContext;

				NativeEncoder::Argument encoderParams[2] =
					{
						&resultContext,
						&endContext
					};
					
				NativeEncoder encoder(encoderParams);

				Call(&decoderParams, &encoder);

				return RTraits::GetReturnValue(result);
			}
		}

		template<typename R, typename P1, typename P2, typename P3>
		/* inline */ R Function::Call(P1 p1, P2 p2, P3 p3)
		{
			if(GetFunctionType() == EA::Type::TypeOf<Functor3<R, P1, P2, P3> >())
			{
				return (*static_cast<Functor3<R, P1, P2, P3>*>(this))(p1, p2, p3);
			}
			else
			{
				typedef NativeTraits<P1> P1Traits;
				typedef NativeTraits<P2> P2Traits;
				typedef NativeTraits<P3> P3Traits;

				NativeDecoderArgument decoderArguments[3] =
					{
						{ P1Traits::GetDecoderType(), P1Traits::GetDecodePointer(p1), P1Traits::GetDecodeFunction() },
						{ P2Traits::GetDecoderType(), P2Traits::GetDecodePointer(p2), P2Traits::GetDecodeFunction() },
						{ P3Traits::GetDecoderType(), P3Traits::GetDecodePointer(p3), P3Traits::GetDecodeFunction() }
					};

				Factory* factory = mFactory;

				NativeDecoderParams decoderParams(factory, decoderArguments, 3);

				typedef NativeTraits<R> RTraits;
				typename RTraits::ReturnEncodeValue result(factory);

				typename RTraits::EncoderContext resultContext(factory, NULL, RTraits::GetEncodePointer(result));
				EndContext endContext;

				NativeEncoder::Argument encoderParams[2] =
					{
						&resultContext,
						&endContext
					};
					
				NativeEncoder encoder(encoderParams);

				Call(&decoderParams, &encoder);

				return RTraits::GetReturnValue(result);
			}
		}

		template<typename R, typename P1, typename P2, typename P3, typename P4>
		/* inline */ R Function::Call(P1 p1, P2 p2, P3 p3, P4 p4)
		{
			if(GetFunctionType() == EA::Type::TypeOf<Functor4<R, P1, P2, P3, P4> >())
			{
				return (*static_cast<Functor4<R, P1, P2, P3, P4>*>(this))(p1, p2, p3, p4);
			}
			else
			{
				typedef NativeTraits<P1> P1Traits;
				typedef NativeTraits<P2> P2Traits;
				typedef NativeTraits<P3> P3Traits;
				typedef NativeTraits<P4> P4Traits;

				NativeDecoderArgument decoderArguments[4] =
					{
						{ P1Traits::GetDecoderType(), P1Traits::GetDecodePointer(p1), P1Traits::GetDecodeFunction() },
						{ P2Traits::GetDecoderType(), P2Traits::GetDecodePointer(p2), P2Traits::GetDecodeFunction() },
						{ P3Traits::GetDecoderType(), P3Traits::GetDecodePointer(p3), P3Traits::GetDecodeFunction() },
						{ P4Traits::GetDecoderType(), P4Traits::GetDecodePointer(p4), P4Traits::GetDecodeFunction() }
					};
				
				Factory* factory = mFactory;

				NativeDecoderParams decoderParams(factory, decoderArguments, 4);

				typedef NativeTraits<R> RTraits;
				typename RTraits::ReturnEncodeValue result(factory);

				typename RTraits::EncoderContext resultContext(factory, NULL, RTraits::GetEncodePointer(result));
				EndContext endContext;

				NativeEncoder::Argument encoderParams[2] =
					{
						&resultContext,
						&endContext
					};
					
				NativeEncoder encoder(encoderParams);

				Call(&decoderParams, &encoder);

				return RTraits::GetReturnValue(result);
			}
		}

		template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5>
		/* inline */ R Function::Call(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5)
		{
			if(GetFunctionType() == EA::Type::TypeOf<Functor5<R, P1, P2, P3, P4, P5> >())
			{
				return (*static_cast<Functor5<R, P1, P2, P3, P4, P5>*>(this))(p1, p2, p3, p4, p5);
			}
			else
			{
				typedef NativeTraits<P1> P1Traits;
				typedef NativeTraits<P2> P2Traits;
				typedef NativeTraits<P3> P3Traits;
				typedef NativeTraits<P4> P4Traits;
				typedef NativeTraits<P5> P5Traits;

				NativeDecoderArgument decoderArguments[5] =
					{
						{ P1Traits::GetDecoderType(), P1Traits::GetDecodePointer(p1), P1Traits::GetDecodeFunction() },
						{ P2Traits::GetDecoderType(), P2Traits::GetDecodePointer(p2), P2Traits::GetDecodeFunction() },
						{ P3Traits::GetDecoderType(), P3Traits::GetDecodePointer(p3), P3Traits::GetDecodeFunction() },
						{ P4Traits::GetDecoderType(), P4Traits::GetDecodePointer(p4), P4Traits::GetDecodeFunction() },
						{ P5Traits::GetDecoderType(), P5Traits::GetDecodePointer(p5), P5Traits::GetDecodeFunction() }
					};

				Factory* factory = mFactory;

				NativeDecoderParams decoderParams(factory, decoderArguments, 5);

				typedef NativeTraits<R> RTraits;
				typename RTraits::ReturnEncodeValue result(factory);

				typename RTraits::EncoderContext resultContext(factory, NULL, RTraits::GetEncodePointer(result));
				EndContext endContext;

				NativeEncoder::Argument encoderParams[2] =
					{
						&resultContext,
						&endContext
					};
					
				NativeEncoder encoder(encoderParams);

				Call(&decoderParams, &encoder);

				return RTraits::GetReturnValue(result);
			}
		}

		template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
		/* inline */ R Function::Call(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6)
		{
			if(GetFunctionType() == EA::Type::TypeOf<Functor6<R, P1, P2, P3, P4, P5, P6> >())
			{
				return (*static_cast<Functor6<R, P1, P2, P3, P4, P5, P6>*>(this))(p1, p2, p3, p4, p5, p6);
			}
			else
			{
				typedef NativeTraits<P1> P1Traits;
				typedef NativeTraits<P2> P2Traits;
				typedef NativeTraits<P3> P3Traits;
				typedef NativeTraits<P4> P4Traits;
				typedef NativeTraits<P5> P5Traits;
				typedef NativeTraits<P6> P6Traits;

				NativeDecoderArgument decoderArguments[6] =
					{
						{ P1Traits::GetDecoderType(), P1Traits::GetDecodePointer(p1), P1Traits::GetDecodeFunction() },
						{ P2Traits::GetDecoderType(), P2Traits::GetDecodePointer(p2), P2Traits::GetDecodeFunction() },
						{ P3Traits::GetDecoderType(), P3Traits::GetDecodePointer(p3), P3Traits::GetDecodeFunction() },
						{ P4Traits::GetDecoderType(), P4Traits::GetDecodePointer(p4), P4Traits::GetDecodeFunction() },
						{ P5Traits::GetDecoderType(), P5Traits::GetDecodePointer(p5), P5Traits::GetDecodeFunction() },
						{ P6Traits::GetDecoderType(), P6Traits::GetDecodePointer(p6), P6Traits::GetDecodeFunction() }
					};

				Factory* factory = mFactory;

				NativeDecoderParams decoderParams(factory, decoderArguments, 6);

				typedef NativeTraits<R> RTraits;
				typename RTraits::ReturnEncodeValue result(factory);

				typename RTraits::EncoderContext resultContext(factory, NULL, RTraits::GetEncodePointer(result));
				EndContext endContext;

				NativeEncoder::Argument encoderParams[2] =
					{
						&resultContext,
						&endContext
					};
					
				NativeEncoder encoder(encoderParams);

				Call(&decoderParams, &encoder);

				return RTraits::GetReturnValue(result);
			}
		}

		/*!
		*	Template for passing zero items to a BaseType Function 
		*	@tparam	R	return type
		*	@param[in]	f	the EA::Types::Function instance to use
		*/
		template<typename R>
		inline R CallFunction(Types::FunctionIn f)
		{
			return f->Call<R>();
		}

		/*!
		*	Template for passing 1 item to a BaseType Function 
		*	@tparam	R	return type
		*	@tparam	P1	type of item 1
		*	@param[in]	f	the EA::Types::Function instance to use
		*	@param[in]	p1	the item
		*/
		template<typename R, typename P1>
		inline R CallFunction(Types::FunctionIn f, P1 p1)
		{
			return f->Call<R, P1>(p1);
		}

		/*!
		*	Template for passing 2 item to a BaseType Function 
		*	@tparam	R	return type
		*	@tparam	P1	type of item 1
		*	@tparam	P2	type of item 2
		*	@param[in]	f	the EA::Types::Function instance to use
		*	@param[in]	p1	the first item
		*	@param[in]	p2	the second item
		*/
		template<typename R, typename P1, typename P2>
		inline R CallFunction(Types::FunctionIn f, P1 p1, P2 p2)
		{
			return f->Call<R, P1, P2>(p1, p2);
		}

		/*!
		*	Template for passing 3 item to a BaseType Function  
		*	@tparam	R	return type
		*	@tparam	P1	type of item 1
		*	@tparam	P2	type of item 2
		*	@tparam	P3	type of item 3
		*	@param[in]	f	the EA::Types::Function instance to use
		*	@param[in]	p1	the first item
		*	@param[in]	p2	the second item
		*	@param[in]	p3	the third item
		*/
		template<typename R, typename P1, typename P2, typename P3>
		inline R CallFunction(Types::FunctionIn f, P1 p1, P2 p2, P3 p3)
		{
			return f->Call<R, P1, P2, P3>(p1, p2, p3);
		}

		/*!
		*	Template for passing 4 item to a BaseType Function  
		*	@tparam	R	return type
		*	@tparam	P1	type of item 1
		*	@tparam	P2	type of item 2
		*	@tparam	P3	type of item 3
		*	@tparam	P4	type of item 4
		*	@param[in]	f	the EA::Types::Function instance to use
		*	@param[in]	p1	the first item
		*	@param[in]	p2	the second item
		*	@param[in]	p3	the third item
		*	@param[in]	p4	the fourth item
		*/
		template<typename R, typename P1, typename P2, typename P3, typename P4>
		inline R CallFunction(Types::FunctionIn f, P1 p1, P2 p2, P3 p3, P4 p4)
		{
			return f->Call<R, P1, P2, P3, P4>(p1, p2, p3, p4);
		}

		/*!
		*	Template for passing 5 item to a BaseType Function  
		*	@tparam	R	return type
		*	@tparam	P1	type of item 1
		*	@tparam	P2	type of item 2
		*	@tparam	P3	type of item 3
		*	@tparam	P4	type of item 4
		*	@tparam P5	type of item 5
		*	@param[in]	f	the EA::Types::Function instance to use
		*	@param[in]	p1	the first item
		*	@param[in]	p2	the second item
		*	@param[in]	p3	the third item
		*	@param[in]	p4	the fourth item
		*	@param[in]	p5	the fifth element
		*/
		template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5>
		inline R CallFunction(Types::FunctionIn f, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5)
		{
			return f->Call<R, P1, P2, P3, P4, P5>(p1, p2, p3, p4, p5);
		}

		/*!
		*	Template for passing 6 items to a BaseType Function  
		*	@tparam	R	return type
		*	@tparam	P1	type of item 1
		*	@tparam	P2	type of item 2
		*	@tparam	P3	type of item 3
		*	@tparam	P4	type of item 4
		*	@tparam P5	type of item 5
		*	@tparam P6	type of item 6
		*	@param[in]	f	the EA::Types::Function instance to use
		*	@param[in]	p1	the first item
		*	@param[in]	p2	the second item
		*	@param[in]	p3	the third item
		*	@param[in]	p4	the fourth item
		*	@param[in]	p5	the fifth element
		*	@param[in]	p6	the sixth element
		*/
		template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
		inline R CallFunction(Types::FunctionIn f, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6)
		{
			return f->Call<R, P1, P2, P3, P4, P5, P6>(p1, p2, p3, p4, p5, p6);
		}
	}
}

#endif //EABASETYPES_FUNCTIONCALLWRAPPERS_H
