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

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

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

namespace EA
{
	namespace Types
	{
		/* virtual */ IEncoderContext* BoolContext::Reset() { *static_cast<bool*>(mValue) = false; return mParent; }
	
		/* virtual */ void BoolContext::BeginArray() { EA_FAIL_MESSAGE("Cannot encode an Array as a bool"); }
		/* virtual */ void BoolContext::BeginObject() { EA_FAIL_MESSAGE("Cannot encode an Object as a bool"); }
		/* virtual */ IEncoderContext* BoolContext::Boolean(bool boolean) { *static_cast<bool*>(mValue) = boolean; return mParent; }
		/* virtual */ IEncoderContext* BoolContext::Number(BaseTypeFloat number) { EA_FAIL_MESSAGE("Cannot encode a Number as a bool"); return mParent; }
		/* virtual */ IEncoderContext* BoolContext::String(const char* str, size_t length) { EA_FAIL_MESSAGE("Cannot encode a String as a bool"); return mParent; }
		/* virtual */ IEncoderContext* BoolContext::Function(FunctionIn function) { EA_FAIL_MESSAGE("Cannot encode a Function as a bool"); return mParent; }
		/* virtual */ IEncoderContext* BoolContext::UserData(UserDataIn userData) { EA_FAIL_MESSAGE("Cannot encode a UserData as a bool"); return mParent; }
		/* virtual */ IEncoderContext* BoolContext::Null() { EA_FAIL_MESSAGE("Cannot encode Null as a bool"); return mParent; }

		/* virtual */ IEncoderContext* IntContext::Reset() { *static_cast<int*>(mValue) = 0; return mParent; }
	
		/* virtual */ void IntContext::BeginArray() { EA_FAIL_MESSAGE("Cannot encode an Array as an int"); }
		/* virtual */ void IntContext::BeginObject() { EA_FAIL_MESSAGE("Cannot encode an Object as an int"); }
		/* virtual */ IEncoderContext* IntContext::Boolean(bool boolean) { EA_FAIL_MESSAGE("Cannot encode a Boolean as an int"); return mParent; }

		/* virtual */ IEncoderContext* IntContext::Number(BaseTypeFloat number) 
		{ 
			int value = static_cast<int>(number);
			EA_ASSERT_MESSAGE(static_cast<BaseTypeFloat>(value) == number, "Number is not an integer");
			*static_cast<int*>(mValue) = value;
			return mParent; 
		}

		/* virtual */ IEncoderContext* IntContext::String(const char* str, size_t length) { EA_FAIL_MESSAGE("Cannot encode a String as an int"); return mParent; }
		/* virtual */ IEncoderContext* IntContext::Function(FunctionIn function) { EA_FAIL_MESSAGE("Cannot encode a Function as an int"); return mParent; }
		/* virtual */ IEncoderContext* IntContext::UserData(UserDataIn userData) { EA_FAIL_MESSAGE("Cannot encode a UserData as an int"); return mParent; }
		/* virtual */ IEncoderContext* IntContext::Null() { EA_FAIL_MESSAGE("Cannot encode Null as an int"); return mParent; }

		/* virtual */ IEncoderContext* UnsignedIntContext::Reset() { *static_cast<unsigned int*>(mValue) = 0; return mParent; }
		/* virtual */ void UnsignedIntContext::BeginArray() { EA_FAIL_MESSAGE("Cannot encode an Array as an unsigned int"); }
		/* virtual */ void UnsignedIntContext::BeginObject() { EA_FAIL_MESSAGE("Cannot encode an Object as an unsigned int"); }
		/* virtual */ IEncoderContext* UnsignedIntContext::Boolean(bool boolean) { EA_FAIL_MESSAGE("Cannot encode a Boolean as an unsigned int"); return mParent; }

		/* virtual */ IEncoderContext* UnsignedIntContext::Number(BaseTypeFloat number) 
		{ 
			unsigned int value = static_cast<unsigned int>(number);
			EA_ASSERT_MESSAGE(static_cast<BaseTypeFloat>(value) == number, "Number is not an unsigned integer");
			*static_cast<unsigned int*>(mValue) = value;
			return mParent; 
		}

		/* virtual */ IEncoderContext* UnsignedIntContext::String(const char* str, size_t length) { EA_FAIL_MESSAGE("Cannot encode a String as an unsigned int"); return mParent; }
		/* virtual */ IEncoderContext* UnsignedIntContext::Function(FunctionIn function) { EA_FAIL_MESSAGE("Cannot encode a Function as an unsigned int"); return mParent; }
		/* virtual */ IEncoderContext* UnsignedIntContext::UserData(UserDataIn userData) { EA_FAIL_MESSAGE("Cannot encode a UserData as an unsigned int"); return mParent; }
		/* virtual */ IEncoderContext* UnsignedIntContext::Null() { EA_FAIL_MESSAGE("Cannot encode Null as an unsigned int"); return mParent; }

		/* virtual */ IEncoderContext* FloatContext::Reset() { *static_cast<float*>(mValue) = 0; return mParent; }
		/* virtual */ void FloatContext::BeginArray() { EA_FAIL_MESSAGE("Cannot encode an Array as a float"); }
		/* virtual */ void FloatContext::BeginObject() { EA_FAIL_MESSAGE("Cannot encode an Object as a float"); }
		/* virtual */ IEncoderContext* FloatContext::Boolean(bool boolean) { EA_FAIL_MESSAGE("Cannot encode a Boolean as a float"); return mParent; }

		/* virtual */ IEncoderContext* FloatContext::Number(BaseTypeFloat number) 
		{ 
			*static_cast<float*>(mValue) = static_cast<float>(number);
			return mParent; 
		}

		/* virtual */ IEncoderContext* FloatContext::String(const char* str, size_t length) { EA_FAIL_MESSAGE("Cannot encode a String as a float"); return mParent; }
		/* virtual */ IEncoderContext* FloatContext::Function(FunctionIn function) { EA_FAIL_MESSAGE("Cannot encode a Function as a float"); return mParent; }
		/* virtual */ IEncoderContext* FloatContext::UserData(UserDataIn userData) { EA_FAIL_MESSAGE("Cannot encode a UserData a float"); return mParent; }
		/* virtual */ IEncoderContext* FloatContext::Null() { EA_FAIL_MESSAGE("Cannot encode Null as a float"); return mParent; }

		/* virtual */ IEncoderContext* DoubleContext::Reset() { *static_cast<double*>(mValue) = 0; return mParent; }
		/* virtual */ void DoubleContext::BeginArray() { EA_FAIL_MESSAGE("Cannot encode an Array as a double"); }
		/* virtual */ void DoubleContext::BeginObject() { EA_FAIL_MESSAGE("Cannot encode an Object as a double"); }
		/* virtual */ IEncoderContext* DoubleContext::Boolean(bool boolean) { EA_FAIL_MESSAGE("Cannot encode a Boolean as a double"); return mParent; }

		/* virtual */ IEncoderContext* DoubleContext::Number(BaseTypeFloat number) 
		{ 
			*static_cast<double*>(mValue) = static_cast<double>(number);
			return mParent; 
		}

		/* virtual */ IEncoderContext* DoubleContext::String(const char* str, size_t length) { EA_FAIL_MESSAGE("Cannot encode a String as a double"); return mParent; }
		/* virtual */ IEncoderContext* DoubleContext::Function(FunctionIn function) { EA_FAIL_MESSAGE("Cannot encode a Function as a double"); return mParent; }
		/* virtual */ IEncoderContext* DoubleContext::UserData(UserDataIn userData) { EA_FAIL_MESSAGE("Cannot encode a UserData a double"); return mParent; }
		/* virtual */ IEncoderContext* DoubleContext::Null() { EA_FAIL_MESSAGE("Cannot encode Null as a double"); return mParent; }

		/* virtual */ IEncoderContext* StringContext::Reset() { static_cast<EA::String*>(mValue)->assign(""); return mParent; }
		/* virtual */ void StringContext::BeginArray() { EA_FAIL_MESSAGE("Cannot encode an Array as a string"); }
		/* virtual */ void StringContext::BeginObject() { EA_FAIL_MESSAGE("Cannot encode an Object as a string"); }
		/* virtual */ IEncoderContext* StringContext::Boolean(bool boolean) { EA_FAIL_MESSAGE("Cannot encode a Boolean as a string"); return mParent; }
		/* virtual */ IEncoderContext* StringContext::Number(BaseTypeFloat number) { EA_FAIL_MESSAGE("Cannot encode a Number as a string"); return mParent; }
		/* virtual */ IEncoderContext* StringContext::String(const char* str, size_t length) { static_cast<EA::String*>(mValue)->assign(str, static_cast<eastl_size_t>(length)); return mParent; }
		/* virtual */ IEncoderContext* StringContext::Function(FunctionIn function) { EA_FAIL_MESSAGE("Cannot encode a Function as a string"); return mParent; }
		/* virtual */ IEncoderContext* StringContext::UserData(UserDataIn userData) { EA_FAIL_MESSAGE("Cannot encode a UserData a string"); return mParent; }
		
        // if someone passes NULL and we want a string, we'll make an empty string
        /* virtual */ IEncoderContext* StringContext::Null() { return mParent; }

		/* virtual */ IEncoderContext* RawStringContext::Reset() { static_cast<EA::RawString*>(mValue)->clear(); return mParent; }
		/* virtual */ void RawStringContext::BeginArray() { EA_FAIL_MESSAGE("Cannot encode an Array as a string"); }
		/* virtual */ void RawStringContext::BeginObject() { EA_FAIL_MESSAGE("Cannot encode an Object as a string"); }
		/* virtual */ IEncoderContext* RawStringContext::Boolean(bool boolean) { EA_FAIL_MESSAGE("Cannot encode a Boolean as a string"); return mParent; }
		/* virtual */ IEncoderContext* RawStringContext::Number(BaseTypeFloat number) { EA_FAIL_MESSAGE("Cannot encode a Number as a string"); return mParent; }
		/* virtual */ IEncoderContext* RawStringContext::String(const char* str, size_t length) { static_cast<EA::RawString*>(mValue)->assign(str, static_cast<eastl_size_t>(length)); return mParent; }
		/* virtual */ IEncoderContext* RawStringContext::Function(FunctionIn function) { EA_FAIL_MESSAGE("Cannot encode a Function as a string"); return mParent; }
		/* virtual */ IEncoderContext* RawStringContext::UserData(UserDataIn userData) { EA_FAIL_MESSAGE("Cannot encode a UserData a string"); return mParent; }
		
        // if someone passes NULL and we want a string, we'll make an empty string
        /* virtual */ IEncoderContext* RawStringContext::Null() { return mParent; }

		/* virtual */ IEncoderContext* BaseTypeContext::Reset()
		{
			mStack.clear();

			BaseType** root = static_cast<BaseType**>(mValue);

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

			return mParent;
		}

		/* virtual */ void BaseTypeContext::BeginArray()
		{
			Types::Array* value = new(mFactory) Types::Array(mFactory);

			Add(value);

			mStack.push_back(value);
		}

		//lint -efunc(904, EA::Types::BaseTypeContext::EndArray)
		/* virtual */ IEncoderContext* BaseTypeContext::EndArray()
		{
			mStack.pop_back();

			if(!mStack.empty())
			{
				return this;
			}
			else
			{
				return mParent;
			}
		}

		/* virtual */ IEncoderContext* BaseTypeContext::BeginArrayValue(size_t index)
		{
			return this;
		}

		/* virtual */ void BaseTypeContext::BeginObject()
		{
			Types::Object* value = new(mFactory) Types::Object(mFactory);

			Add(value);

			mStack.push_back(value);
		}

		//lint -efunc(904, EA::Types::BaseTypeContext::EndObject)
		/* virtual */ IEncoderContext* BaseTypeContext::EndObject()
		{
			mStack.pop_back();

			if(!mStack.empty())
			{
				return this;
			}
			else
			{
				return mParent;
			}
		}

		/* virtual */ IEncoderContext* BaseTypeContext::BeginObjectValue(const char* key, size_t length)
		{
			mCurrentKey.assign(key, static_cast<eastl_size_t>(length));

			return this;
		}

		/* virtual */ IEncoderContext* BaseTypeContext::Boolean(bool boolean)
		{
			Types::Boolean *b = new(mFactory) Types::Boolean(mFactory, boolean);

			return Add(b);
		}

		/* virtual */ IEncoderContext* BaseTypeContext::Number(BaseTypeFloat number)
		{
			Types::Number *f = new(mFactory) Types::Number(mFactory, number);

			return Add(f);
		}

		/* virtual */ IEncoderContext* BaseTypeContext::String(const char* str, size_t length)
		{
			Types::String *s = new(mFactory) Types::String(mFactory, str, length);

			return Add(s);
		}

		/* virtual */ IEncoderContext* BaseTypeContext::Function(FunctionIn function)
		{
			// AddRef the function as we are taking ownership of it
			function->AddRef();

			return Add(function.GetUnsafePointer());
		}

		/* virtual */ IEncoderContext* BaseTypeContext::UserData(UserDataIn userData)
		{
			// AddRef the UserData as we are taking ownership of it
			userData->AddRef();

			return Add(userData.GetUnsafePointer());
		}

		/* virtual */ IEncoderContext* BaseTypeContext::Null()
		{
			return Add(NULL);
		}

		//lint -efunc(904, EA::Types::BaseTypeContext::Add)
		/* private */ IEncoderContext* BaseTypeContext::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)

				BaseType** root = static_cast<BaseType**>(mValue);

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

				*root = value;

				return mParent;
			}
			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;
				}

				return this;
			}
		}
	}
}
