/** @file nativeencoder.h
*	native encoder/decoder templates
*	@author Chris Burns (cburns@ea.com)
*
*	EABaseTypes
*	(c) 2011 Electronic Arts Inc.
*/

#ifndef EABASETYPES_NATIVEENCODER_H_
#define EABASETYPES_NATIVEENCODER_H_

#include "EABaseTypes/nativetraits.h"

namespace EA
{
	namespace Types
	{
		class NativeEncoder
		{
		public:
		
			typedef ChildContext* Argument;

			NativeEncoder(ChildContext** contexts):
				mContexts(contexts),
				mCurrent(contexts[0]),
				mNext(1)
			{
			}
	
			// IEncoder interface
			inline void Reset() 
			{
				unsigned int i = 1;
				IEncoderContext* current = NULL;
				IEncoderContext* next = mContexts[0];
				do
				{
					current = next;
					next = current->Reset();
					if(next == NULL) { next = mContexts[i++]; }
				}
				while(next != current);

				mCurrent = mContexts[0];
				mNext = 1;
			}

			inline void BeginArray() { mCurrent->BeginArray(); }
			inline void EndArray() { mCurrent = mCurrent->EndArray(); CheckNext(); }

			inline void BeginArrayValue(size_t index) { mCurrent = mCurrent->BeginArrayValue(index); CheckNext(); }
			inline void EndArrayValue(size_t index) { mCurrent->EndArrayValue(index); }

			inline void BeginObject() { mCurrent->BeginObject(); }
			inline void EndObject() { mCurrent = mCurrent->EndObject(); CheckNext(); }

			inline void BeginObjectValue(const char* key, size_t length) { mCurrent = mCurrent->BeginObjectValue(key, length); CheckNext(); }
			inline void EndObjectValue(const char* key, size_t length) { mCurrent->EndObjectValue(key, length); }

			inline void Boolean(bool boolean) { mCurrent = mCurrent->Boolean(boolean); CheckNext(); }
			inline void Number(BaseTypeFloat number) { mCurrent = mCurrent->Number(number); CheckNext(); }
			inline void String(const char* str, size_t length) { mCurrent = mCurrent->String(str, length); CheckNext(); }
			inline void Function(FunctionIn function) { mCurrent = mCurrent->Function(function); CheckNext(); }
			inline void UserData(UserDataIn userData) { mCurrent = mCurrent->UserData(userData); CheckNext(); }
			inline void Null() { mCurrent = mCurrent->Null(); CheckNext(); }
	
		private:

			inline void CheckNext() { if(mCurrent == NULL) { mCurrent = mContexts[mNext++]; } }

			ChildContext**		mContexts;
			IEncoderContext*	mCurrent;
			unsigned int		mNext;
		};

		/*!
		*	@struct NativeDecoderArgument
		*	@brief structure containing the type to decode and a pointer to the value
		*/
		struct NativeDecoderArgument
		{
			NativeDecoderType		mType;
			void*					mValue;
			NativeDecodeFunction	mDecode;
		};
		
		class Factory;

		/*!
		*	@struct NativeDecoderParams
		*	@brief structure containing data on what to decode
		*/
		struct NativeDecoderParams
		{
			/*!
			*	NativeDecoderParams Constructor
			*	@param	arguments		A pointer to an array of arguments
			*	@param	numArguments	The number of arguments
			*/
			NativeDecoderParams(EA::Types::Factory* factory, NativeDecoderArgument* arguments, size_t numArguments):
				mFactory(factory),
				mArguments(arguments),
				mNumArguments(numArguments)
			{
			}

			Factory*				mFactory;
			NativeDecoderArgument*	mArguments;
			size_t					mNumArguments;
		};

		/*! @class NativeDecoder nativeencoder.h	"EABaseTypes/nativeencoder.h"
		*	@brief template class for a NativeDecoder
		*	@tparam	Encoder	The encoder type
		*/
		template<typename Encoder>
		class NativeDecoder
		{
		public:

			// Decoder template interface
			static void Decode(void* e, void* p)
			{
				Encoder* encoder = static_cast<Encoder*>(e);
				NativeDecoderParams* params = static_cast<NativeDecoderParams*>(p);
				Factory* factory = params->mFactory;
				
				size_t numArguments = params->mNumArguments;
				NativeDecoderArgument* arguments = params->mArguments;
			
				for(size_t i = 0; i < numArguments; ++i)
				{
					NativeDecoderType type = arguments[i].mType;
					void* value = arguments[i].mValue;

					switch(type)
					{
						case NATIVE_DECODER_TYPE_BOOL:
							NativeTraits<bool>::Decode(factory, encoder, value);
							break;
						case NATIVE_DECODER_TYPE_INT:
							NativeTraits<int>::Decode(factory, encoder, value);
							break;
						case NATIVE_DECODER_TYPE_UNSIGNED_INT:
							NativeTraits<unsigned int>::Decode(factory, encoder, value);
							break;
						case NATIVE_DECODER_TYPE_FLOAT:
							NativeTraits<float>::Decode(factory, encoder, value);
							break;
						case NATIVE_DECODER_TYPE_DOUBLE:
							NativeTraits<double>::Decode(factory, encoder, value);
							break;
						case NATIVE_DECODER_TYPE_CONST_CHAR_PTR:
							NativeTraits<const char*>::Decode(factory, encoder, value);
							break;
						case NATIVE_DECODER_TYPE_CONST_STRING_REF:
							NativeTraits<const EA::String&>::Decode(factory, encoder, value);
							break;
						case NATIVE_DECODER_TYPE_CONST_RAWSTRING_REF:
							NativeTraits<const EA::RawString&>::Decode(factory, encoder, value);
							break;
						case NATIVE_DECODER_TYPE_BASETYPE:
							NativeTraitsBaseType<BaseType>::Decode(factory, encoder, value);
							break;
						case NATIVE_DECODER_TYPE_CONST_CHAR_PTR_ARRAY:
							NativeTraits<const char**>::Decode(factory, encoder, value);
							break;
						case NATIVE_DECODER_TYPE_CUSTOM:
							{
								IEncoderImpl<Encoder> encoderImpl(encoder);

								arguments[i].mDecode(factory, &encoderImpl, value);
							}
							break;
					}
				}
			}
		};
	}
}

#endif /* EABASETYPES_NATIVEENCODER_H_ */
