/** @file structencoder.h
*	struct encoder/decoder templates/macros
*	@author Chris Burns (cburns@ea.com)
*
*	EABaseTypes
*	(c) 2012 Electronic Arts Inc.
*/

#ifndef EABASETYPES_STRUCTENCODER_H_
#define EABASETYPES_STRUCTENCODER_H_

#include "EABaseTypes/nativeencoder.h"
#include "coreallocator/icoreallocatormacros.h"

namespace EA
{
	namespace Types
	{
		class Factory;

		typedef IEncoderContext* (*CreateContextFunction)(Factory* factory, IEncoderContext* parent, void* value);

		template<typename T>
		IEncoderContext* CreateContext(Factory* factory, IEncoderContext* parent, void* value)
		{
			return CORE_NEW(factory->GetAllocator(), "EncoderContext", EA::Allocator::MEM_TEMP) typename NativeTraits<T>::EncoderContext(factory, parent, value);
		}

		struct StructSchema
		{
			const char* 			mKey;
			size_t					mOffset;
			CreateContextFunction	mCreateContext;
			NativeDecodeFunction	mDecode;
		};

		template<typename T>
		const StructSchema* GetSchema();

		class StructContext : public ChildContext
		{
		public:

			StructContext(Factory* factory,
						  IEncoderContext* parent,
						  void* value,
						  const StructSchema* schema);

			virtual void BeginArray();
	
			virtual IEncoderContext* BeginObjectValue(const char* key, size_t length);
	
			virtual void EndObjectValue(const char* key, size_t length);

			virtual IEncoderContext* Boolean(bool boolean);
			virtual IEncoderContext* Number(BaseTypeFloat number);
			virtual IEncoderContext* String(const char* str, size_t length);
			virtual IEncoderContext* Function(FunctionIn function);
			virtual IEncoderContext* UserData(UserDataIn userData);
			virtual IEncoderContext* Null();
	
		private:

			Factory*			mFactory;
			const StructSchema* mSchema;
			IEncoderContext*	mCurrentContext;
			ChildContext		mNullContext;
		};

		void DecodeStruct(Factory* factory, IEncoder* encoder, void* value, const StructSchema* schema);
	}
}

#define EABASETYPES_STRUCT_BEGIN(T) \
namespace EA\
{\
	namespace Types\
	{\
		template<>\
		const StructSchema* GetSchema<T>()\
		{\
				static const StructSchema schema[] =\
				{

#define EABASETYPES_STRUCT_MEMBER(T, MT, MN)\
					{ #MN, offsetof(T, MN), &CreateContext<MT>, &NativeTraits<MT>::Decode<IEncoder> },

#define EABASETYPES_STRUCT_END(T) \
					{ NULL, 0, NULL, NULL }\
				};\
			\
			return schema;\
		}\
		\
		template<>\
		struct NativeTraits<T>\
		{\
			class EncoderContext : public StructContext { public: EncoderContext(Factory* factory, IEncoderContext* parent, void* value):StructContext(factory, parent, value, GetSchema<T>()){} };\
			struct Value\
			{\
				Value(Factory* factory):mValue() {}\
				T mValue;\
				Value& operator=(const T& rhs)\
				{\
					mValue = rhs;\
					return *this;\
				}\
			};\
			typedef Value										EncodeValue;\
			typedef Value										ReturnEncodeValue;\
			typedef Value										DecodeValue;\
			\
			static inline NativeDecoderType GetDecoderType() { return NATIVE_DECODER_TYPE_CUSTOM; }\
			static inline const T& GetValue(const Value& value) { return value.mValue; }\
			static inline const T& GetReturnValue(const Value& value) { return value.mValue; }\
			static inline void*	GetEncodePointer(Value& value) { return &(value.mValue); }\
			static inline void*	GetDecodePointer(const T& value) { return const_cast<T*>(&value); }\
			static inline NativeDecodeFunction GetDecodeFunction() { return &NativeTraits<T>::template Decode<IEncoder>; }\
			\
			template<typename Encoder>\
			static inline void Decode(Factory* factory, Encoder* encoder, void* value) { DecodeStruct(factory, encoder, value, GetSchema<T>()); }\
		};\
	}\
}

#endif /* EABASETYPES_STRUCTENCODER_H_ */
