/** @file encodercontext.h
*	encoder contexts for native types
*	@author Chris Burns (cburns@ea.com)
*
*	EABaseTypes
*	(c) 2012 Electronic Arts Inc.
*/

#ifndef EABASETYPES_ENCODERCONTEXT_H_
#define EABASETYPES_ENCODERCONTEXT_H_

#include "EABaseTypes/iencodercontext.h"
#include "EABaseTypes/factory.h"
#include "EABaseTypes/string.h"

#include "EASTLICA/vector.h"

#include "EAAssert/EAAssert.h"

namespace EA
{
	namespace Types
	{
		class EndContext : public ChildContext
		{
		public:

			inline EndContext():ChildContext(NULL, NULL) { SetParent(this); }
		};

		class NullContext : public ChildContext
		{
		public:

			inline NullContext(Factory* factory, IEncoderContext*, void* value):ChildContext(NULL, NULL) {}
		};

		class BoolContext : public ChildContext
		{
		public:

			inline BoolContext(Factory* factory,
							   IEncoderContext* parent,
							   void* value):
				ChildContext(parent, value)
			{
			}
	
			virtual IEncoderContext* Reset();
			virtual void BeginArray();
			virtual void BeginObject();
			virtual IEncoderContext* Boolean(bool boolean);
			virtual IEncoderContext* Number(BaseTypeFloat number);
			virtual IEncoderContext* String(const char* str, size_t length);
			virtual IEncoderContext* Function(EA::Types::FunctionIn function);
			virtual IEncoderContext* UserData(EA::Types::UserDataIn userData);
			virtual IEncoderContext* Null();
		};

		class IntContext : public ChildContext
		{
		public:

			inline IntContext(Factory* factory,
							  IEncoderContext* parent,
							  void* value):
				ChildContext(parent, value)
			{
			}
	
			virtual IEncoderContext* Reset();
			virtual void BeginArray();
			virtual void BeginObject();
			virtual IEncoderContext* Boolean(bool boolean);
			virtual IEncoderContext* Number(BaseTypeFloat number);
			virtual IEncoderContext* String(const char* str, size_t length);
			virtual IEncoderContext* Function(EA::Types::FunctionIn function);
			virtual IEncoderContext* UserData(EA::Types::UserDataIn userData);
			virtual IEncoderContext* Null();
		};

		class UnsignedIntContext : public ChildContext
		{
		public:

			inline UnsignedIntContext(Factory* factory,
									  IEncoderContext* parent,
									  void* value):
				ChildContext(parent, value)
			{
			}
	
			virtual IEncoderContext* Reset();
			virtual void BeginArray();
			virtual void BeginObject();
			virtual IEncoderContext* Boolean(bool boolean);
			virtual IEncoderContext* Number(BaseTypeFloat number);
			virtual IEncoderContext* String(const char* str, size_t length);
			virtual IEncoderContext* Function(EA::Types::FunctionIn function);
			virtual IEncoderContext* UserData(EA::Types::UserDataIn userData);
			virtual IEncoderContext* Null();
		};

		class FloatContext : public ChildContext
		{
		public:

			inline FloatContext(Factory* factory,
								IEncoderContext* parent,
								void* value):
				ChildContext(parent, value)
			{
			}
	
			virtual IEncoderContext* Reset();
			virtual void BeginArray();
			virtual void BeginObject();
			virtual IEncoderContext* Boolean(bool boolean);
			virtual IEncoderContext* Number(BaseTypeFloat number);
			virtual IEncoderContext* String(const char* str, size_t length);
			virtual IEncoderContext* Function(EA::Types::FunctionIn function);
			virtual IEncoderContext* UserData(EA::Types::UserDataIn userData);
			virtual IEncoderContext* Null();
		};

		class DoubleContext : public ChildContext
		{
		public:

			inline DoubleContext(Factory* factory,
								 IEncoderContext* parent,
								 void* value):
				ChildContext(parent, value)
			{
			}
	
			virtual IEncoderContext* Reset();
			virtual void BeginArray();
			virtual void BeginObject();
			virtual IEncoderContext* Boolean(bool boolean);
			virtual IEncoderContext* Number(BaseTypeFloat number);
			virtual IEncoderContext* String(const char* str, size_t length);
			virtual IEncoderContext* Function(EA::Types::FunctionIn function);
			virtual IEncoderContext* UserData(EA::Types::UserDataIn userData);
			virtual IEncoderContext* Null();
		};

		class StringContext : public ChildContext
		{
		public:

			inline StringContext(Factory* factory,
								 IEncoderContext* parent,
								 void* value):
				ChildContext(parent, value)
			{
			}
	
			virtual IEncoderContext* Reset();
			virtual void BeginArray();
			virtual void BeginObject();
			virtual IEncoderContext* Boolean(bool boolean);
			virtual IEncoderContext* Number(BaseTypeFloat number);
			virtual IEncoderContext* String(const char* str, size_t length);
			virtual IEncoderContext* Function(EA::Types::FunctionIn function);
			virtual IEncoderContext* UserData(EA::Types::UserDataIn userData);
			virtual IEncoderContext* Null();
		};

		class RawStringContext : public ChildContext
		{
		public:

			inline RawStringContext(Factory* factory,
									IEncoderContext* parent,
									void* value):
				ChildContext(parent, value)
			{
			}
	
			virtual IEncoderContext* Reset();
			virtual void BeginArray();
			virtual void BeginObject();
			virtual IEncoderContext* Boolean(bool boolean);
			virtual IEncoderContext* Number(BaseTypeFloat number);
			virtual IEncoderContext* String(const char* str, size_t length);
			virtual IEncoderContext* Function(EA::Types::FunctionIn function);
			virtual IEncoderContext* UserData(EA::Types::UserDataIn userData);
			virtual IEncoderContext* Null();
		};

		template<typename T>
		class StringArrayContext : public ChildContext
		{
		public:

			inline StringArrayContext(Factory* factory,
									  IEncoderContext* parent,
									  void* value):
				ChildContext(parent, value),
				mStr(factory->GetAllocator()),
				mContext(factory, NULL, &mStr)
			{
				mContext.SetParent(this);
			}

			virtual IEncoderContext* Reset() { static_cast<T*>(mValue)->clear(); return mParent; }
			virtual IEncoderContext* BeginArrayValue(size_t index) { return &mContext; }			
			virtual void EndArrayValue(size_t index) { static_cast<T*>(mValue)->push_back(mStr.c_str()); }
			virtual void BeginObject() { EA_FAIL_MESSAGE("Cannot encode an Object as a string array"); }
			virtual IEncoderContext* Boolean(bool boolean) { EA_FAIL_MESSAGE("Cannot encode a Boolean as a string array"); return mParent; }
			virtual IEncoderContext* Number(BaseTypeFloat number) { EA_FAIL_MESSAGE("Cannot encode a Number as a string array"); return mParent; }
			virtual IEncoderContext* String(const char* str, size_t length) { EA_FAIL_MESSAGE("Cannot encode a String as a string array"); return mParent; }
			virtual IEncoderContext* Function(EA::Types::FunctionIn function) { EA_FAIL_MESSAGE("Cannot encode a Function as a string array"); return mParent; }
			virtual IEncoderContext* UserData(EA::Types::UserDataIn userData) { EA_FAIL_MESSAGE("Cannot encode a UserData as a string array"); return mParent; }
			virtual IEncoderContext* Null() { EA_FAIL_MESSAGE("Cannot encode Null as a string array"); return mParent; }
	
		private:

			EA::RawString		mStr;
			RawStringContext	mContext;
		};

		class BaseTypeContext : public ChildContext
		{
		public:

			inline BaseTypeContext(Factory* factory,
								   IEncoderContext* parent,
								   void* value):
				ChildContext(parent, value),
				mFactory(factory),
				mCurrentKey(factory),
				mStack(factory->GetAllocator())
			{
			}
	
			virtual IEncoderContext* Reset();
			virtual void BeginArray();
			virtual IEncoderContext* EndArray();
			virtual IEncoderContext* BeginArrayValue(size_t index);
			virtual void BeginObject();
			virtual IEncoderContext* EndObject();
			virtual IEncoderContext* BeginObjectValue(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(EA::Types::FunctionIn function);
			virtual IEncoderContext* UserData(EA::Types::UserDataIn userData);
			virtual IEncoderContext* Null();	
	
		private:

			IEncoderContext* Add(Types::BaseType* value);

			Factory*			mFactory;
			EA::String			mCurrentKey;
			typedef EASTLICA::Vector<Types::BaseType*> BaseTypeStack; // could we use some kind of fixed vector here?

			BaseTypeStack		mStack;
		};
	}
}

#endif /* EABASETYPES_ENCODERCONTEXT_H_ */
