/** @file proxyencoder.h
*	proxy encoder/decoder templates
*	@author Chris Burns (cburns@ea.com)
*
*	Core Technology UI
*	(c) 2012 Electronic Arts Inc.
*/

#ifndef EABASETYPESMESSAGE_PROXYENCODER_H_
#define EABASETYPESMESSAGE_PROXYENCODER_H_

#include "EABaseTypesPlus/bytestreamencoder.h"

namespace EA
{
	namespace Types
	{
		class MessageProxy;

		EA::Types::FunctionReturn ProxyFunction(MessageProxy* localProxy, MessageProxy* remoteProxy, EA::Types::FunctionIn function);

		/*! @class	ProxyEncoder proxyencoder.h	"EABaseTypesMessage/proxyencoder.h"
		*	@brief	Encodes data from a Decoder as proxied via the given message proxy
		*/
		template<typename Encoder>
		class ProxyEncoder
		{
		public:

			/*!
			*	ProxyEncoder constructor
			*	@param	localProxy	The local proxy
			*	@param	remoteProxy	The remote proxy
			*	@param	data		The array to populate with data
			*	@param	metadata	The array to populate with metadata
			*/
			ProxyEncoder(MessageProxy* localProxy, MessageProxy* remoteProxy, Encoder* encoder):
				mLocalProxy(localProxy),
				mRemoteProxy(remoteProxy),
				mEncoder(encoder) {}

			// Encoder template interface
			void Reset() { mEncoder->Reset(); }

			void BeginArray() { mEncoder->BeginArray(); }
			void EndArray() { mEncoder->EndArray(); }

			void BeginArrayValue(size_t index) { mEncoder->BeginArrayValue(index); }
			void EndArrayValue(size_t index) { mEncoder->EndArrayValue(index); }

			void BeginObject() { mEncoder->BeginObject(); }
			void EndObject() { mEncoder->EndObject(); }

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

			void Boolean(bool boolValue) { mEncoder->Boolean(boolValue); }
			void Number(BaseTypeFloat number) { mEncoder->Number(number); }
			void String(const char* str, size_t length) { mEncoder->String(str, length); }
			void Function(FunctionIn function) { mEncoder->Function(ProxyFunction(mLocalProxy, mRemoteProxy, function)); }
			void UserData(UserDataIn userData) { mEncoder->UserData(userData->Clone()->AsUserData()); }
			void Null() { mEncoder->Null(); }

		private:

			MessageProxy*		mLocalProxy;
			MessageProxy*		mRemoteProxy;
			Encoder*			mEncoder;
		};
        
        EA::Types::FunctionReturn UnproxyFunction(MessageProxy* localProxy, EA::Types::FunctionIn function);
        
        struct ProxyDecoderParams
        {
        public:
        
            ProxyDecoderParams(MessageProxy* localProxy, const ByteStream* in):
                mLocalProxy(localProxy), mIn(in)
            {
            }
            
            MessageProxy*       mLocalProxy;
            const ByteStream*   mIn;
        };
        
        template<typename Encoder>
        class UnproxyEncoder
        {
        public:

            UnproxyEncoder(MessageProxy* localProxy, Encoder* encoder):
                mLocalProxy(localProxy),
                mEncoder(encoder) {}

            // Encoder template interface
            void Reset() { mEncoder->Reset(); }

            void BeginArray() { mEncoder->BeginArray(); }
            void EndArray() { mEncoder->EndArray(); }

            void BeginArrayValue(size_t index) { mEncoder->BeginArrayValue(index); }
            void EndArrayValue(size_t index) { mEncoder->EndArrayValue(index); }

            void BeginObject() { mEncoder->BeginObject(); }
            void EndObject() { mEncoder->EndObject(); }

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

            void Boolean(bool boolValue) { mEncoder->Boolean(boolValue); }
            void Number(BaseTypeFloat number) { mEncoder->Number(number); }
            void String(const char* str, size_t length) { mEncoder->String(str, length); }
            void Function(FunctionIn function) { mEncoder->Function(UnproxyFunction(mLocalProxy, function)); }
            void UserData(UserDataIn userData) { mEncoder->UserData(userData); }
            void Null() { mEncoder->Null(); }

        private:

            MessageProxy*	mLocalProxy;
            Encoder*		mEncoder;
        };

		template<typename Encoder>
		class ProxyDecoder
        {
		public:
            
            // Decoder template interface
			static void Decode(void* e, void *p)
			{
				Encoder* encoder = static_cast<Encoder*>(e);
				ProxyDecoderParams* params = static_cast<ProxyDecoderParams*>(p);
                
                UnproxyEncoder<Encoder> unproxyEncoder(params->mLocalProxy, encoder);
                
                ByteStreamDecoder<UnproxyEncoder<Encoder> >::Decode(&unproxyEncoder, const_cast<ByteStream*>(params->mIn));
            }
        };
	}
}

#endif /* EABASETYPESMESSAGE_PROXYENCODER_H_ */
