/** @file nativeencoder.h
*	native traits for data types
*	@author Chris Burns (cburns@ea.com)
*
*	EABaseTypes
*	(c) 2012 Electronic Arts Inc.
*/

#ifndef EABASETYPES_NATIVETRAITS_H_
#define EABASETYPES_NATIVETRAITS_H_

#include "EABaseTypes/iencoder.h"
#include "EABaseTypes/encodercontext.h"
#include "EABaseTypes/basetypeencoder.h"

#include "eaassert/eaassert.h"

namespace EA
{
	namespace Types
	{
		enum NativeDecoderType
		{
			NATIVE_DECODER_TYPE_BASETYPE = 0,
			NATIVE_DECODER_TYPE_BOOL,
			NATIVE_DECODER_TYPE_INT,
			NATIVE_DECODER_TYPE_UNSIGNED_INT,
			NATIVE_DECODER_TYPE_FLOAT,
			NATIVE_DECODER_TYPE_DOUBLE,
			NATIVE_DECODER_TYPE_CONST_CHAR_PTR,
			NATIVE_DECODER_TYPE_CONST_STRING_REF,
			NATIVE_DECODER_TYPE_CONST_RAWSTRING_REF,
			NATIVE_DECODER_TYPE_CONST_CHAR_PTR_ARRAY,
			NATIVE_DECODER_TYPE_CUSTOM
		};

		typedef void (*NativeDecodeFunction)(Factory* factory, IEncoder* encoder, void* value);

		template<typename T, typename Enable = void>
		struct NativeTraits
		{
			typedef	BaseTypeContext								EncoderContext;

			struct EncodeValue
			{
				EncodeValue(Factory*):mValue() {}
				BaseTypeRef mValue;
			};

			typedef EncodeValue									ReturnEncodeValue;

			struct DecodeValue
			{
				DecodeValue(Factory*):mValue() {}
				T mValue;
				DecodeValue& operator=(const T& rhs)
				{
					mValue = rhs;
					return *this;
				}
			};

			static inline NativeDecoderType GetDecoderType() { return NATIVE_DECODER_TYPE_CUSTOM; }
			static inline T GetValue(const EncodeValue& value) { return BaseTypeTraits1<T>::As(value.mValue); }
			static inline T GetReturnValue(const EncodeValue& value) { return BaseTypeTraits1<T>::As(value.mValue); }
			static inline void* GetEncodePointer(EncodeValue& value) { return value.mValue.GetUnsafeHandle(); }
			static inline void* GetDecodePointer(const T& value) { return const_cast<T*>(&value); }
			static inline void* GetDecodePointer(const DecodeValue& value) { return const_cast<T*>(&(value.mValue)); }
			static inline NativeDecodeFunction GetDecodeFunction() { return &NativeTraits<T>::template Decode<IEncoder>; }

			template<typename Encoder>
			static inline void Decode(Factory* factory, Encoder* encoder, void* value)
			{
				BaseTypeRef baseType = factory->BaseType(*static_cast<const T*>(value));
				BaseTypeDecoder<Encoder>::Decode(encoder, baseType.GetUnsafePointer());
			}
		};

		template<typename T>
		struct NativeTraits<const T&> : NativeTraits<T>
		{
			typedef typename NativeTraits<T>::EncoderContext	EncoderContext;
			typedef typename NativeTraits<T>::EncodeValue		EncodeValue;
			typedef typename NativeTraits<T>::DecodeValue		DecodeValue;
		};

		template<>
		struct NativeTraits<void>
		{
			typedef NullContext									EncoderContext;
			struct ReturnEncodeValue { ReturnEncodeValue(Factory*) {} };

			static inline void GetReturnValue(const ReturnEncodeValue& value) {}
			static inline void* GetEncodePointer(const ReturnEncodeValue& value) { return NULL; }
		};

		template<typename T>
		struct PrimitiveTraits;

		template<typename T>
		struct NativeTraitsPrimitive
		{
			typedef typename PrimitiveTraits<T>::EncoderContext	EncoderContext;

			struct Value
			{
				Value(Factory*):mValue(static_cast<T>(0)) {}
				T mValue;
				
				inline Value& operator=(const T& rhs)
				{
					mValue = rhs;
					return *this;
				}
			};

			typedef	Value										EncodeValue;
			typedef Value										ReturnEncodeValue;
			typedef Value										DecodeValue;

			static inline NativeDecoderType GetDecoderType() { return PrimitiveTraits<T>::DecoderType; }
			static inline T GetValue(const Value& value) { return value.mValue; }
			static inline T GetReturnValue(const Value& value) { return value.mValue; }
			static inline void*	GetEncodePointer(Value& value) { return &value.mValue; }
			static inline void* GetDecodePointer(const Value& value) { return const_cast<T*>(&value.mValue); }
			static inline void*	GetDecodePointer(const T& value) { return const_cast<T*>(&value); }
			static inline NativeDecodeFunction GetDecodeFunction() { return NULL; }

			template<typename Encoder>
			static inline void Decode(Factory* factory, Encoder* encoder, void* value) { PrimitiveTraits<T>::Decode(factory, encoder, value); }
		};

		template<>
		struct PrimitiveTraits<bool>
		{
			typedef BoolContext				EncoderContext;
			static const NativeDecoderType	DecoderType = NATIVE_DECODER_TYPE_BOOL;

			template<typename Encoder>
			static inline void Decode(Factory* factory, Encoder* encoder, void* value) { encoder->Boolean(*static_cast<bool*>(value)); }
		};

		template<>
		struct NativeTraits<bool> : NativeTraitsPrimitive<bool>
		{
			typedef NativeTraitsPrimitive<bool>::EncoderContext	    EncoderContext;
			typedef NativeTraitsPrimitive<bool>::EncodeValue		EncodeValue;
			typedef NativeTraitsPrimitive<bool>::ReturnEncodeValue	ReturnEncodeValue;
			typedef NativeTraitsPrimitive<bool>::DecodeValue		DecodeValue;
		};

		template<>
		struct PrimitiveTraits<int>
		{
			typedef IntContext				EncoderContext;
			static const NativeDecoderType	DecoderType = NATIVE_DECODER_TYPE_INT;

			template<typename Encoder>
			static inline void Decode(Factory* factory, Encoder* encoder, void* value) { encoder->Number(*static_cast<int*>(value)); }
		};

		template<>
		struct NativeTraits<int> : NativeTraitsPrimitive<int>
		{
			typedef NativeTraitsPrimitive<int>::EncoderContext		EncoderContext;
			typedef NativeTraitsPrimitive<int>::EncodeValue		    EncodeValue;
			typedef NativeTraitsPrimitive<int>::ReturnEncodeValue	ReturnEncodeValue;
			typedef NativeTraitsPrimitive<int>::DecodeValue		    DecodeValue;
		};

		template<>
		struct PrimitiveTraits<unsigned int>
		{
			typedef UnsignedIntContext				EncoderContext;
			static const NativeDecoderType			DecoderType = NATIVE_DECODER_TYPE_UNSIGNED_INT;

			template<typename Encoder>
			static inline void Decode(Factory* factory, Encoder* encoder, void* value) { encoder->Number(*static_cast<unsigned int*>(value)); }
		};

		template<>
		struct NativeTraits<unsigned int> : NativeTraitsPrimitive<unsigned int>
		{
			typedef NativeTraitsPrimitive<unsigned int>::EncoderContext	    EncoderContext;
			typedef NativeTraitsPrimitive<unsigned int>::EncodeValue		EncodeValue;
			typedef NativeTraitsPrimitive<unsigned int>::ReturnEncodeValue	ReturnEncodeValue;
			typedef NativeTraitsPrimitive<unsigned int>::DecodeValue		DecodeValue;
		};

		template<>
		struct PrimitiveTraits<float>
		{
			typedef FloatContext			EncoderContext;
			static const NativeDecoderType	DecoderType = NATIVE_DECODER_TYPE_FLOAT;

			template<typename Encoder>
			static inline void Decode(Factory* factory, Encoder* encoder, void* value) { encoder->Number(*static_cast<float*>(value)); }
		};

		template<>
		struct NativeTraits<float> : NativeTraitsPrimitive<float>
		{
			typedef NativeTraitsPrimitive<float>::EncoderContext		EncoderContext;
			typedef NativeTraitsPrimitive<float>::EncodeValue			EncodeValue;
			typedef NativeTraitsPrimitive<float>::ReturnEncodeValue     ReturnEncodeValue;
			typedef NativeTraitsPrimitive<float>::DecodeValue			DecodeValue;
		};

		template<>
		struct PrimitiveTraits<double>
		{
			typedef DoubleContext			EncoderContext;
			static const NativeDecoderType	DecoderType = NATIVE_DECODER_TYPE_DOUBLE;

			template<typename Encoder>
			static inline void Decode(Factory* factory, Encoder* encoder, void* value) { encoder->Number(*static_cast<double*>(value)); }
		};

		template<>
		struct NativeTraits<double> : NativeTraitsPrimitive<double>
		{
			typedef NativeTraitsPrimitive<double>::EncoderContext	    EncoderContext;
			typedef NativeTraitsPrimitive<double>::EncodeValue		    EncodeValue;
			typedef NativeTraitsPrimitive<double>::ReturnEncodeValue	ReturnEncodeValue;
			typedef NativeTraitsPrimitive<double>::DecodeValue			DecodeValue;
		};

		template<>
		struct NativeTraits<const char*>
		{
			typedef StringContext								EncoderContext;
			typedef EA::String									EncodeValue;
			typedef const char*									DecodeValue;

			static inline NativeDecoderType GetDecoderType() { return NATIVE_DECODER_TYPE_CONST_CHAR_PTR; }
			static inline const char* GetValue(const EA::String& value) { return value.c_str(); }
			static inline void*	GetEncodePointer(EA::String& value) { return &value; }
			static inline void*	GetDecodePointer(const char* value) { return const_cast<char*>(value); }
			static inline NativeDecodeFunction GetDecodeFunction() { return NULL; }

			template<typename Encoder>
			static inline void Decode(Factory* factory, Encoder* encoder, void* value) { const char* str = static_cast<const char*>(value); encoder->String(str, strlen(str)); }
		};

		template<>
		struct NativeTraits<EA::String>
		{
			typedef StringContext								EncoderContext;
			typedef EA::String									EncodeValue;
			typedef EA::String									ReturnEncodeValue;
			typedef EA::String									DecodeValue;

			static inline NativeDecoderType GetDecoderType() { return NATIVE_DECODER_TYPE_CONST_STRING_REF; }
			static inline const EA::String& GetValue(const EA::String& value) { return value; }
			static inline const EA::String& GetReturnValue(const EA::String& value) { return value; }
			static inline void*	GetEncodePointer(EA::String& value) { return &value; }
			static inline void*	GetDecodePointer(const EA::String& value) { return const_cast<EA::String*>(&value); }
			static inline NativeDecodeFunction GetDecodeFunction() { return NULL; }

			template<typename Encoder>
			static inline void Decode(Factory* factory, Encoder* encoder, void* value) { const EA::String* str = static_cast<const EA::String*>(value); encoder->String(str->c_str(), str->length()); }
		};

		template<>
		struct NativeTraits<EA::RawString>
		{
			typedef RawStringContext							EncoderContext;

			struct Value
			{
				Value(Factory* factory):mValue(factory->GetAllocator()) {}

				EA::RawString mValue;

				inline Value& operator=(const EA::RawString& rhs)
				{
					mValue = rhs;
					return *this;
				}
			};

			typedef Value										EncodeValue;
			typedef Value										EncodeReturnValue;
			typedef Value										DecodeValue;

			static inline NativeDecoderType GetDecoderType() { return NATIVE_DECODER_TYPE_CONST_RAWSTRING_REF; }
			static inline const EA::RawString& GetValue(const Value& value) { return value.mValue; }
			static inline const EA::RawString& GetReturnValue(const Value& value) { return value.mValue; }
			static inline void*	GetEncodePointer(Value& value) { return &value.mValue; }
			static inline void*	GetDecodePointer(const Value& value) { return const_cast<EA::RawString*>(&value.mValue); }
			static inline void*	GetDecodePointer(const EA::RawString& value) { return const_cast<EA::RawString*>(&value); }
			static inline NativeDecodeFunction GetDecodeFunction() { return NULL; }

			template<typename Encoder>
			static inline void Decode(Factory* factory, Encoder* encoder, void* value) { const EA::RawString* str = static_cast<const EA::RawString*>(value); encoder->String(str->c_str(), str->length()); }
		};

		template<>
		struct NativeTraits<const char**>
		{
			typedef NullContext									EncoderContext;
			struct Value { Value(void*) {} };

			typedef Value										EncodeValue;

			static inline NativeDecoderType GetDecoderType() { return NATIVE_DECODER_TYPE_CONST_CHAR_PTR_ARRAY; }
			static inline const char** GetValue(Value& value) { return NULL; }
			static inline void* GetEncodePointer(Value& value) { return NULL; }
			static inline void* GetDecodePointer(const char** value) { return value; }
			static inline NativeDecodeFunction GetDecodeFunction() { return NULL; }

			template<typename Encoder>
			static inline void Decode(Factory* factory, Encoder* encoder, void* value)
			{
				const char** strArray = static_cast<const char**>(value);

				if(strArray != NULL)
				{
					encoder->BeginArray();

					for(size_t j = 0; strArray[j] != NULL; ++j)
					{
						encoder->BeginArrayValue(j);
						encoder->String(strArray[j], strlen(strArray[j]));
						encoder->EndArrayValue(j);
					}

					encoder->EndArray();
				}
				else
				{
					encoder->Null();
				}
			}
		};
		
		template<typename T, typename Enable = void>
		struct DataTypeOf;

		template<typename T>
		struct DataTypeOf<const T> : DataTypeOf<T> {};

		template<>
		struct DataTypeOf<BaseType> { static inline bool IsType(ConstBaseTypeIn baseType) { return true; } };

		template<>
		struct DataTypeOf<Boolean> { static inline bool IsType(ConstBaseTypeIn baseType) { return baseType->GetType() == BOOLEAN_TYPE; } };

		template<>
		struct DataTypeOf<Number> { static inline bool IsType(ConstBaseTypeIn baseType) { return baseType->GetType() == NUMBER_TYPE; } };

		template<>
		struct DataTypeOf<String> { static inline bool IsType(ConstBaseTypeIn baseType) { return baseType->GetType() == STRING_TYPE; } };

		template<>
		struct DataTypeOf<Array> { static inline bool IsType(ConstBaseTypeIn baseType) { return baseType->GetType() == ARRAY_TYPE; } };

		template<>
		struct DataTypeOf<Object> { static inline bool IsType(ConstBaseTypeIn baseType) { return baseType->GetType() == OBJECT_TYPE; } };

		template<>
		struct DataTypeOf<Function> { static inline bool IsType(ConstBaseTypeIn baseType) { return baseType->GetType() == FUNCTION_TYPE; } };
        
        template<typename T>
        struct DataTypeOf<T, typename EnableIf<IsBaseOf<Function, T> >::type> : DataTypeOf<Function> {};

		template<>
		struct DataTypeOf<UserData> { static inline bool IsType(ConstBaseTypeIn baseType) { return baseType->GetType() == USERDATA_TYPE; } };
        
        template<typename T>
        struct DataTypeOf<T, typename EnableIf<IsBaseOf<UserData, T> >::type> : DataTypeOf<UserData> {};
		
		template<typename T>
		struct NativeTraitsBaseType
		{
			struct Value
			{
				Value(Factory*):mValue() {}
				AutoRef<T> mValue;
				inline Value& operator=(const AutoRef<T>& rhs)
				{
					mValue = rhs;
					return *this;
				}
			};

			typedef	BaseTypeContext								EncoderContext;
			typedef Value										EncodeValue;
			typedef Value										DecodeValue;

			static inline NativeDecoderType GetDecoderType() { return NATIVE_DECODER_TYPE_BASETYPE; }
			static inline const AutoRef<T>& GetValue(const Value& value) { EA_ASSERT(value.mValue == NULL || DataTypeOf<T>::IsType(value.mValue)); return value.mValue; }
			static inline void* GetEncodePointer(Value& value) { return value.mValue.GetUnsafeHandle(); }
			static inline void* GetDecodePointer(Value& value) { return value.mValue.GetUnsafeHandle(); }
			static inline void* GetDecodePointer(AutoRef<T>& value) { return value.GetUnsafeHandle(); }
			static inline void* GetDecodePointer(AutoRefIn<T>& value) { return value.GetUnsafeHandle(); }
			static inline NativeDecodeFunction GetDecodeFunction() { return NULL; }

			template<typename Encoder>
			static inline void Decode(Factory* factory, Encoder *encoder, void* value)
			{
				Types::BaseType* baseType = *static_cast<Types::BaseType**>(value);
				BaseTypeDecoder<Encoder>::Decode(encoder, baseType);
			}
		};
		
		template<typename T>
		struct NativeTraits<AutoRefIn<const T>, typename EnableIf<IsBaseOf<BaseType, T> >::type> : public NativeTraitsBaseType<const T>
		{
			typedef typename NativeTraitsBaseType<const T>::EncoderContext	EncoderContext;
			typedef typename NativeTraitsBaseType<const T>::EncodeValue		EncodeValue;
			typedef typename NativeTraitsBaseType<const T>::DecodeValue	    DecodeValue;
		};
		
		template<typename T>
		struct NativeTraits<AutoRef<const T>, typename EnableIf<IsBaseOf<BaseType, T> >::type> : public NativeTraitsBaseType<const T>
		{
			typedef typename NativeTraitsBaseType<const T>::EncoderContext	EncoderContext;
			typedef typename NativeTraitsBaseType<const T>::EncodeValue		EncodeValue;
			typedef typename NativeTraitsBaseType<const T>::DecodeValue		DecodeValue;

			struct ReturnValue
			{
				ReturnValue(Factory*):mValue(NULL) {}
				const T* mValue;
			};

			typedef ReturnValue											    ReturnEncodeValue;
			
			using NativeTraitsBaseType<const T>::GetEncodePointer;
			static inline void* GetEncodePointer(ReturnValue& value) { return &value.mValue; }
			static inline const T* GetReturnValue(const ReturnValue& value) { return value.mValue; }
		};
		
		template<typename T>
		struct NativeTraits<AutoRefIn<T>, typename EnableIf<IsBaseOf<BaseType, T> >::type> : public NativeTraitsBaseType<T>
		{
			typedef typename NativeTraitsBaseType<T>::EncoderContext	EncoderContext;
			typedef typename NativeTraitsBaseType<T>::EncodeValue		EncodeValue;
			typedef typename NativeTraitsBaseType<T>::DecodeValue		DecodeValue;
		};
		
		template<typename T>
		struct NativeTraits<AutoRef<T>, typename EnableIf<IsBaseOf<BaseType, T> >::type> : public NativeTraitsBaseType<T>
		{
			typedef typename NativeTraitsBaseType<T>::EncoderContext	EncoderContext;
			typedef typename NativeTraitsBaseType<T>::EncodeValue		EncodeValue;
			typedef typename NativeTraitsBaseType<T>::DecodeValue		DecodeValue;
			
			struct ReturnValue
			{
				ReturnValue(Factory*):mValue(NULL) {}
				T* mValue;
			};

			typedef ReturnValue									        ReturnEncodeValue;
			
			using NativeTraitsBaseType<T>::GetEncodePointer;
			static inline void* GetEncodePointer(ReturnValue& value) { return &value.mValue; }
			static inline T* GetReturnValue(const ReturnValue& value) { return value.mValue; }
		};
	}
}

#define EABASETYPES_NATIVETRAITS_STRINGARRAY(TYPE_NAME)\
	namespace EA\
	{\
		namespace Types\
		{\
			template<>\
			struct NativeTraits<TYPE_NAME>\
			{\
				typedef StringArrayContext<TYPE_NAME>				EncoderContext;\
				struct Value\
				{\
					Value(Factory* factory):mValue(factory->GetAllocator()) {}\
					TYPE_NAME mValue;\
					inline Value& operator=(const TYPE_NAME& 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 TYPE_NAME& GetValue(const Value& value) { return value.mValue; }\
				static inline const TYPE_NAME& GetReturnValue(const Value& value) { return value.mValue; }\
				static inline void*	GetEncodePointer(Value& value) { return &value.mValue; }\
				static inline void*	GetDecodePointer(const TYPE_NAME& value) { return const_cast<TYPE_NAME*>(&value); }\
				static inline void*	GetDecodePointer(const Value& value) { return const_cast<TYPE_NAME*>(&value.mValue); }\
				static inline NativeDecodeFunction GetDecodeFunction() { return &NativeTraits<TYPE_NAME>::Decode<IEncoder>; }\
				\
				template<typename Encoder>\
				static inline void Decode(Factory* factory, Encoder* encoder, void* value)\
				{\
					const TYPE_NAME* strArray = static_cast<const TYPE_NAME*>(value);\
					\
					TYPE_NAME::const_iterator it = strArray->begin();\
					TYPE_NAME::const_iterator end = strArray->end();\
					\
					encoder->BeginArray();\
					\
					for(size_t j = 0; it != end; ++it, ++j)\
					{\
						encoder->BeginArrayValue(j);\
						encoder->String((*it).c_str(), (*it).length());\
						encoder->EndArrayValue(j);\
					}\
					\
					encoder->EndArray();\
				}\
			};\
		}\
	}

#endif /* EABASETYPES_NATIVETRAITS_H_ */
