/** @file jsonencoder.h
*	JSON encoder/decoder templates
*	@author Chris Burns (cburns@ea.com)
*
*	UX
*	(c) 2012 Electronic Arts Inc.
*/

#ifndef EABASETYPESJSON_JSONENCODER_H_
#define EABASETYPESJSON_JSONENCODER_H_

#include "EABaseTypes/factory.h"
#include "EABaseTypes/datatypes.h"

#include "EABaseTypes/string.h"
#include "EASTLICA/vector.h"
#include "EABaseTypesJson/jsoncallbackreader.h"

#include "eaassert/eaassert.h"

namespace EA
{
	namespace Json
	{
		class JsonWriter;
	}
}

namespace EA
{
	namespace Types
	{
		/*! @class JsonEncoder jsonencoder.h	"EABaseTypesJson/jsonencoder.h"
		*	@brief Encodes data from a Decoder as JSON
		*/
		class JsonEncoder
		{
		public:
		
			/*!
			*	JsonEncoder Constructor
			*	@param[in]	writer	The writer to write the JSON to
			*/
			JsonEncoder(EA::Json::JsonWriter *writer);
			~JsonEncoder();

			// Encoder template interface
			void Reset();

			void BeginArray();
			void EndArray();

			void BeginArrayValue(size_t index) {}
			void EndArrayValue(size_t index) {}

			void BeginObject();
			void EndObject();

			void BeginObjectValue(const char* key, size_t length);
			void EndObjectValue(const char* key, size_t length) {}

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

		private:

			EA::Json::JsonWriter *mWriter;
		};

		/*! @class JsonDecoder jsonencoder.h	"EABaseTypesJson/jsonencoder.h"
		*	@brief Decoder template for JSON
		*/
		struct JsonDecoderParams
		{
			JsonDecoderParams(Types::Factory* factory, JsonCallbackReader* callbackReader):
				mFactory(factory), mCallbackReader(callbackReader) 
			{
			}

			Types::Factory*		mFactory;
			JsonCallbackReader*	mCallbackReader;
		};

		/*! @class JsonDecoder jsonencoder.h	"EABaseTypesJson/jsonencoder.h"
		*	@brief Decoder template for JSON
		*	@tparam Encoder	The encoder type
		*/
		template<typename Encoder>
		class JsonDecoder
		{
		public:

			// Decoder template interface
			static void Decode(void* e, void* p)
			{
				Encoder* encoder = static_cast<Encoder*>(e);
				JsonDecoderParams* params = static_cast<JsonDecoderParams*>(p);
			
				ReaderCallback callback(params->mFactory, encoder);

				params->mCallbackReader->Parse(&callback);

				if(params->mCallbackReader->GetResult() != EA::Json::kSuccess)
				{
					encoder->Reset();
					encoder->Null();

					char error[256];

					EA::Json::FormatJsonResultString(params->mCallbackReader, error, sizeof(error) - 1);

					encoder->String(error, strlen(error));
				}
				else
				{
					encoder->String("", 0);
				}
			}

		private:

			/*! @class ReaderCallback jsonencoder.h	"EABaseTypesJson/jsonencoder.h"
			*	@brief IJsonReaderCallback implementation that writes into an encoder
			*/
			class ReaderCallback : public EA::Json::IJsonReaderCallback
			{
			public:

				ReaderCallback(Factory* factory, Encoder* encoder)
					: mFactory(factory)
					, mIndexStack(factory->GetAllocator())
					, mKeyStack(factory->GetAllocator())
					, mEncoder(encoder)
				{
				}

				virtual bool BeginDocument()
				{ 
					return true;
				}

				virtual bool EndDocument()
				{ 
					return true;
				}

				virtual bool BeginObject()
				{
					BeginValue();

					mEncoder->BeginObject();

					mIndexStack.push_back(-1);

					return true;
				}

				virtual bool BeginObjectValue(const char* pName, size_t nNameLength)
				{
					mEncoder->BeginObjectValue(pName, nNameLength);

					EA::String key(mFactory);
					key.assign(pName, static_cast<eastl_size_t>(nNameLength));
					mKeyStack.push_back(key);

					return true;
				}

				//virtual bool EndObjectValue(const char* pName, size_t nNameLength)
				//{
				//	mEncoder->EndObjectValue(pName, nNameLength);

				//	return true;
				//}

				virtual bool EndObject()
				{ 
					mIndexStack.pop_back();

					mEncoder->EndObject();

					EndValue();

					return true;
				}

				virtual bool BeginArray()
				{ 
					BeginValue();

					mEncoder->BeginArray();

					mIndexStack.push_back(0);

					return true;
				}

				virtual bool EndArray()
				{ 
					mIndexStack.pop_back();

					mEncoder->EndArray();

					EndValue();

					return true;
				}

				virtual bool Integer(int64_t value, const char*, size_t)
				{
					BeginValue();

					mEncoder->Number(static_cast<EA::BaseTypeFloat>(value));

					EndValue();

					return true;
				}

				virtual bool Double(double value, const char*, size_t)
				{
					BeginValue();

					mEncoder->Number(static_cast<EA::BaseTypeFloat>(value));

					EndValue();

					return true;
				}

				virtual bool Bool(bool value, const char*, size_t)
				{ 
					BeginValue();

					mEncoder->Boolean(value);

					EndValue();

					return true;
				}

				virtual bool String(const char* pValue, size_t nValueLength, const char*, size_t)
				{
					BeginValue();

					mEncoder->String(pValue, nValueLength);

					EndValue();

					return true;
				}
		   
				virtual bool Null()
				{
					BeginValue();

					mEncoder->Null();

					EndValue();

					return true;
				}

			private:

				void BeginValue()
				{
					IndexStack::reverse_iterator it = mIndexStack.rbegin();

					if(it != mIndexStack.rend() && (*it) >= 0)
					{
						size_t val = static_cast<size_t>((*it));
						mEncoder->BeginArrayValue(val);
					}
				}

				void EndValue()
				{
					IndexStack::reverse_iterator it = mIndexStack.rbegin();

					if(it != mIndexStack.rend())
					{
						if((*it) >= 0)
						{
							size_t val = static_cast<size_t>((*it));
							mEncoder->EndArrayValue(val);

							(*it)++;
						}
						else
						{
							const EA::String& key = mKeyStack.back();

							mEncoder->EndObjectValue(key.c_str(), key.length());

							mKeyStack.pop_back();
						}
					}
				}

				typedef EASTLICA::Vector<int> IndexStack;
				typedef EASTLICA::Vector<EA::String> KeyStack;

				Factory*	mFactory;
				IndexStack	mIndexStack;
				KeyStack	mKeyStack;

				Encoder*	mEncoder;
			};
		};
	}
}

#endif /* EABASETYPESJSON_JSONENCODER_H_ */
