/** @file basetypeencoder.cpp
 *  Encoder implementation for basetypes
 * 	@headerfile basetypeencoder.h "EABaseTypes/basetypeencoder.h"
 *	@author Chris Burns (cburns@ea.com)
 *
 *	EABaseTypes
 *	(c) 2011 Electronic Arts Inc. 
 */

#include "EABaseTypes/basetypeencoder.h"
#include "EABaseTypes/factory.h"
#include "EABaseTypes/typechecking.h"

#include "eaassert/eaassert.h"
#include "coreallocator/icoreallocatormacros.h"

namespace EA
{
	namespace Types
	{
		BaseTypeEncoder::BaseTypeEncoder(EA::Types::Factory* factory):
			mCurrentKey(factory),
			mStack(factory->GetAllocator()),
			mFactory(factory),
			mRoot(NULL)
		{
		}

		BaseTypeEncoder::~BaseTypeEncoder()
		{
		}
		
		EA::Types::BaseType* BaseTypeEncoder::AcquireRoot()
		{
			EA::Types::BaseType* root = mRoot;
			mRoot = NULL;
			return root;
		}

		void BaseTypeEncoder::Reset()
		{
			mStack.clear();

			if(mRoot != NULL)
			{
				mRoot->Release();
				mRoot = NULL;
			}
		}

		void BaseTypeEncoder::BeginArray()
		{
			Types::Array* value = new(mFactory) Types::Array(mFactory);

			Add(value);

			mStack.push_back(value);
		}

		void BaseTypeEncoder::EndArray()
		{
			mStack.pop_back();
		}

		void BaseTypeEncoder::BeginObject()
		{
			Types::Object* value = new(mFactory) Types::Object(mFactory);

			Add(value);

			mStack.push_back(value);
		}

		void BaseTypeEncoder::EndObject()
		{
			mStack.pop_back();
		}

		void BaseTypeEncoder::BeginObjectValue(const char* key, size_t length)
		{
			mCurrentKey.assign(key, static_cast<eastl_size_t>(length));
		}

		void BaseTypeEncoder::EndObjectValue(const char* key, size_t length)
		{
			// not really necessary
			//mCurrentKey = "";
		}

		void BaseTypeEncoder::Boolean(bool boolean)
		{
			Types::Boolean *b = new(mFactory) Types::Boolean(mFactory, boolean);

			Add(b);
		}

		void BaseTypeEncoder::Number(BaseTypeFloat number)
		{
			Types::Number *f = new(mFactory) Types::Number(mFactory, number);

			Add(f);
		}

		void BaseTypeEncoder::String(const char* str, size_t length)
		{
			Types::String *s = new(mFactory) Types::String(mFactory, str, static_cast<eastl_size_t>(length));

			Add(s);
		}

		void BaseTypeEncoder::Function(EA::Types::FunctionIn function)
		{
			// AddRef the function as we are taking ownership of it
			function->AddRef();

			Add(function.GetUnsafePointer());
		}

		void BaseTypeEncoder::UserData(EA::Types::UserDataIn userData)
		{
			// AddRef the UserData as we are taking ownership of it
			userData->AddRef();

			Add(userData.GetUnsafePointer());
		}

		void BaseTypeEncoder::Null()
		{
			Add(NULL);
		}

		/* private */ void BaseTypeEncoder::Add(Types::BaseType* value)
		{
			if(mStack.empty())
			{
				// if the stack is empty, the first value we create becomes the root (so it can be retrieved later)

				if(mRoot != NULL)
				{
					mRoot->Release();
				}

				mRoot = value;
			}
			else
			{
				// otherwise, add the data to the current object or array on the top of the stack

				Types::BaseType* target = mStack.back();

				switch(target->GetType())
				{
					case Types::OBJECT_TYPE:
						static_cast<Types::Object*>(target)->insert(mCurrentKey).first->second = value;
						break;
					case Types::ARRAY_TYPE:
						static_cast<Types::Array*>(target)->push_back() = value;
						break;
					default:
						EA_FAIL_MESSAGE("Invalid type for BaseType on stack");
						break;
				}
			}
		}

		BaseTypeArrayEncoder::BaseTypeArrayEncoder(Types::Array* root):
			mRoot(root),
			mEncoder(root->GetFactory()),
			mLevel(0)
		{
			EA_ASSERT(root->size() == 0);
		}

		BaseTypeArrayEncoder::~BaseTypeArrayEncoder()
		{
		}

		void BaseTypeArrayEncoder::Reset(Types::Array* root)
		{
			EA_ASSERT(root->size() == 0);

			mRoot = root;
			mLevel = 0;

			mEncoder.Reset();
		}

		void BaseTypeArrayEncoder::Reset()
		{
			mLevel = 0;

			mRoot->clear();

			mEncoder.Reset();
		}

		void BaseTypeArrayEncoder::BeginArray()
		{
			mLevel++;

			mEncoder.BeginArray();
		}

		void BaseTypeArrayEncoder::EndArray()
		{
			mEncoder.EndArray();

			mLevel--;

			EndValue();
		}

		void BaseTypeArrayEncoder::BeginArrayValue(size_t index)
		{
			mEncoder.BeginArrayValue(index);
		}

		void BaseTypeArrayEncoder::EndArrayValue(size_t index)
		{
			mEncoder.EndArrayValue(index);
		}

		void BaseTypeArrayEncoder::BeginObject()
		{
			mLevel++;

			mEncoder.BeginObject();
		}

		void BaseTypeArrayEncoder::EndObject()
		{
			mEncoder.EndObject();

			mLevel--;

			EndValue();
		}

		void BaseTypeArrayEncoder::BeginObjectValue(const char* key, size_t length)
		{
			mEncoder.BeginObjectValue(key, length);
		}

		void BaseTypeArrayEncoder::EndObjectValue(const char* key, size_t length)
		{
			mEncoder.EndObjectValue(key, length);
		}

		void BaseTypeArrayEncoder::Boolean(bool boolean)
		{
			mEncoder.Boolean(boolean);

			EndValue();
		}
	
		void BaseTypeArrayEncoder::Number(BaseTypeFloat number)
		{
			mEncoder.Number(number);

			EndValue();
		}
	
		void BaseTypeArrayEncoder::String(const char* str, size_t length)
		{
			mEncoder.String(str, length);

			EndValue();
		}
	
		void BaseTypeArrayEncoder::Function(EA::Types::FunctionIn function)
		{
			mEncoder.Function(function);

			EndValue();
		}

		void BaseTypeArrayEncoder::UserData(EA::Types::UserDataIn userData)
		{
			mEncoder.UserData(userData);

			EndValue();
		}
	
		void BaseTypeArrayEncoder::Null()
		{
			mEncoder.Null();

			EndValue();
		}

		/* private */ void BaseTypeArrayEncoder::EndValue()
		{
			if(mLevel <= 0)
			{
				// if we're at the top level, push the encoded value into the Array and then reset the encoder
				// so we can use it for the next value
				mRoot->push_back() = mEncoder.AcquireRoot(); // this transfers ownership of the root
				mEncoder.Reset();
			}
		}
	}
	
}
