/*
    Copyright 2004-2005 Electronic Arts Inc.
*/

// --- Include guard
#ifndef EA_GAMETALK_GAMETALKBASE_H
#define EA_GAMETALK_GAMETALKBASE_H


// --- Includes

// --- Macros

// API markup for building shared libraries (DLL)
#if defined(EA_DLL)
	// case: shared libraries
	#if defined(GAMETALK_BUILD)
		// job manager itself is building
		#define GAMETALK_API EA_EXPORT
	#else
		// client code is building
		#define GAMETALK_API EA_IMPORT
	#endif
#else
	// case: static libraries
    #define GAMETALK_API
#endif


#ifdef USER_GAMETALK_WINDOWS_WORKAROUND
    #include <windows.h>
#else
    //  Need to undefine this as Windows defines it and thereby screws up any SendMessage function (i.e. GameTalkManager's, etc)
    #undef SendMessage
#endif



#ifdef USER_GAMETALK_INCLUDE
    #include "gametalkuserinclude.h"
    // If used it MUST provide definitions for the basic integer types:
    //      int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t
    // And platform defines:
    //      EA_PLATFORM_xxxxx
    // Memory: user may optionally provide these defines to control memory allocation (provide both or neither)
    //      USER_GAMETALK_ALLOC(bytes, name)
    //      USER_GAMETALK_FREE(ptr, bytes)
    //      USER_GAMETALK_MEMCPY(dst, src, count)
    //      USER_GAMETALK_MEMSET(dst, value, count)
    // Assertions: user may optionally provide these define for assertions:
    //      USER_GAMETALK_ASSERT(cond, msg)
    // Endian: user may optionally provide there define for endian conversion
    //      USER_GAMETALK_SWAP_ENDIAN32(uint32_t v)
    //      USER_GAMETALK_SWAP_LITTLE_ENDIAN32(uint32_t v)
	//		EA_SYSTEM_LITTLE_ENDIAN
#else
    #include "EABase/eabase.h"
    #include <string.h>
    #include <ctype.h>
    #include <stdio.h>
    #ifdef USER_GAMETALK_DEBUG
        #include <assert.h>
        #include <stdio.h>
    #endif
#endif

// This is included later because some platforms like Marmalade don't compile if eabase.h isn't included first. 
#include <stdlib.h>

#if USER_GAMETALK_MINIMAL_INTERFACE
namespace EA
{
    namespace GameTalk
    {
		// 2 is serialized format because it combines string and GameTalkMessage (binary 01) whereas 3 is binary 11 - which means binary and GameTalkMessage
		enum FormatTypes
		{
			StringFormat=0,
			BinaryFormat=1,
			SerializedFormat=2,
			GameTalkMessageFormat=3,
			Char = 4,
			Int8 = 5,
			UInt8 = 6,
			Int16 = 7,
			UInt16 = 8,
			Int32 = 9,
			UInt32 = 10,
			Int64 = 11,
			UInt64 = 12,
			Float = 13,
			Double = 14,
			Decimal = 15,
		};

		class IMessage { };
		class IConnection{ };
	}
}
#else
#include "IMessage.h"
#include "IConnection.h"
#endif

#define GAMETALK_VERSION_MAJOR   4
#define GAMETALK_VERSION_MINOR   5
#define GAMETALK_VERSION_PATCH   4
#define GAMETALK_VERSION         ((GAMETALK_VERSION_MAJOR << 16) | (GAMETALK_VERSION_MINOR << 8) | GAMETALK_VERSION_PATCH)

// Dictates the amount of data read from the socket per "poll"
#ifndef GAMETALK_RECEIVE_BUFFER_SIZE
    // 16384 = 16 * 1024
    #define GAMETALK_RECEIVE_BUFFER_SIZE 16384
#endif

// The maximum size of a single GameTalkMessage
#ifndef GAMETALK_MAX_BUFFER_SIZE
    // 1048576 = 1024 * 1024
    #define GAMETALK_MAX_BUFFER_SIZE 1048576
#endif

// Dictates the amount of string content data in a call-stack of GameTalkMessages
// You can end up with a call-stack of GameTalkMessages if your GameTalk callback invokes Poll
#ifndef GAMETALK_RECEIVE_STRING_BUFFER_SIZE
    // 16384 = 16 * 1024
    #define GAMETALK_RECEIVE_STRING_BUFFER_SIZE 16384
#endif

// Dictates the number of key/value pairs can occur in a call-stack of GameTalkMessages
// You can end up with a call-stack of GameTalkMessages if your GameTalk callback invokes Poll
#ifndef GAMETALK_MAX_SIMULTANEOUS_MESSAGE_KEYS
    #define GAMETALK_MAX_SIMULTANEOUS_MESSAGE_KEYS 200
#endif

// If the user didn't provide these, create defaults
#ifndef USER_GAMETALK_USE_VAR_TO_AVOID_WARNING
    #define USER_GAMETALK_USE_VAR_TO_AVOID_WARNING(useme) (void) useme
#endif

#ifndef USER_GAMETALK_ASSERT
    #ifdef USER_GAMETALK_DEBUG
		#include <stdio.h>
        #define USER_GAMETALK_ASSERT(cond, msg) {if (!(cond)) {printf(msg);printf("\n");assert(cond);}}
    #else
        #define USER_GAMETALK_ASSERT(cond, msg)
    #endif
#endif

#ifndef USER_GAMETALK_FAIL
	#ifdef USER_GAMETALK_DEBUG
		#include <assert.h>
		#include <stdio.h>
		#define USER_GAMETALK_FAIL(msg) {printf(msg);printf("\n");assert(false);}
	#else
		#define USER_GAMETALK_FAIL(msg)
	#endif
#endif

// If the user didn't provide these, create defaults
#ifndef USER_GAMETALK_ALLOC
	#define USER_GAMETALK_ALLOC(bytes,name) (EA::GameTalk::GameTalkAlloc(bytes, name))
#endif


// If the user didn't provide these, create defaults
#ifndef USER_GAMETALK_FREE
	#define USER_GAMETALK_FREE(ptr,name) EA::GameTalk::GameTalkFree(ptr, name)
#endif

// If the user didn't provide these, create defaults
#ifndef USER_GAMETALK_MEMCPY
    #define USER_GAMETALK_MEMCPY(dst, src, count) memcpy(dst, src, count)
#endif

// If the user didn't provide these, create defaults
#ifndef USER_GAMETALK_MEMSET
    #define USER_GAMETALK_MEMSET(dst, value, count) memset(dst, value, count)
#endif

// If the user didn't provide these, create defaults
#ifndef USER_GAMETALK_SWAP_LITTLE_ENDIAN32
    #define USER_GAMETALK_SWAP_LITTLE_ENDIAN32(v) EA::GameTalk::EndianSwap::SwapLE32(v)
#endif

#ifndef USER_GAMETALK_STRLEN
    // Any compiler that correctly puts these functions in the std namespace (unlike Microsoft) will
    // require users of this code to insert a "using std;" into their code
    #define USER_GAMETALK_STRLEN(stringParam) strlen(stringParam)
#endif

// Since we're going to treat this macro as not guaranteeing addition of a null delimiter - we'll deprecate use of this macro
// Thus all usage of USER_GAMETALK_STRNCPY should be replaced with USER_GAMETALK_MEMCPY (if no null delimiter is needed/wanted) or 
// USER_GAMETALK_STRNCPY_ADD_NULL
#ifndef USER_GAMETALK_STRNCPY
    // Any compiler that correctly puts these functions in the std namespace (unlike Microsoft) will
    // require users of this code to insert a "using std;" into their code
	#define USER_GAMETALK_STRNCPY(strDest, strSource, count) strncpy(strDest, strSource, count)
#endif

#ifndef USER_GAMETALK_STRNCPY_ADD_NULL
    // Any compiler that correctly puts these functions in the std namespace (unlike Microsoft) will
    // require users of this code to insert a "using std;" into their code
#if defined(EA_PLATFORM_WINDOWS) || defined(EA_PLATFORM_XENON)
	// This could also be written as								  strncpy_s(strDest, count, strSource, (count - 1))
	#define USER_GAMETALK_STRNCPY_ADD_NULL(strDest, strSource, count) strncpy_s(strDest, count, strSource, _TRUNCATE)
#else
	#define USER_GAMETALK_STRNCPY_ADD_NULL(strDest, strSource, count)	strncpy(strDest, strSource, count-1); \
																		strDest[count-1] = '\0';
#endif
#endif

#ifndef USER_GAMETALK_TOLOWER
	#include <ctype.h>
	// Any compiler that correctly puts these functions in the std namespace (unlike Microsoft) will
    // require users of this code to insert a "using std;" into their code
    #define USER_GAMETALK_TOLOWER(intParam) tolower(intParam)
#endif

#ifndef USER_GAMETALK_STREQUAL
    #define USER_GAMETALK_STREQUAL(a, b) StrIsEqual(a, b)
#endif

#ifndef USER_GAMETALK_UNSIGNED_VALUE_TO_TEXT
	#include <stdio.h>
	#define USER_GAMETALK_UNSIGNED_VALUE_TO_TEXT(stringToAddText, uintValue) sprintf(stringToAddText, "%u", uintValue)
#endif

#ifndef USER_GAMETALK_TEXT_TO_VALUE
	#include <stdio.h>
	#if defined(EA_PLATFORM_WINDOWS)
		#define USER_GAMETALK_TEXT_TO_VALUE(uintStr, uintValue) sscanf_s(uintStr,"%u",(uint32_t*)&uintValue)
	#else
		#define USER_GAMETALK_TEXT_TO_VALUE(uintStr, uintValue) sscanf(uintStr,"%u",(uint32_t*)&uintValue)
	#endif
#endif

// NOT CURRENTLY IN USE
// This is intended to provide a platform independent way of entering a critical section
#ifndef USER_GAMETALK_THREAD_LOCK
	#include <stdio.h>
    #define USER_GAMETALK_THREAD_LOCK() printf("A thread lock function should be provided for USER_GAMETALK_THREAD_LOCK\n");
#endif

// NOT CURRENTLY IN USE
// This is intended to provide a platform independent way of leaving a critical section
#ifndef USER_GAMETALK_THREAD_UNLOCK
	#include <stdio.h>
    #define USER_GAMETALK_THREAD_UNLOCK() printf("A thread unlock function should be provided for USER_GAMETALK_THREAD_UNLOCK\n");
#endif

// The default behavior of this macro is just to assign to a static GameTalkInstance member of GameTalkManager - users should implement proper TLS support
#ifndef USER_GAMETALK_TLS_SET_VALUE
    #define USER_GAMETALK_TLS_SET_VALUE(value) AssignGameTalkInstanceToStaticMember(value)
#endif

// The default behavior of this macro is to return the static GameTalkInstance member of GameTalkManager - users should implement proper TLS support
#ifndef USER_GAMETALK_TLS_GET_VALUE
    #define USER_GAMETALK_TLS_GET_VALUE() sGameTalkInstance
#endif

#ifndef USER_GAMETALK_MAX_SYNCHRO_HANDLERS
    #define USER_GAMETALK_MAX_SYNCHRO_HANDLERS 10
#endif

#ifndef USER_GAMETALK_ALIGNMENT
    #define USER_GAMETALK_ALIGNMENT 16
#endif

#ifndef USER_GAMETALK_IS_LITTLE_ENDIAN

#if !defined(EA_SYSTEM_LITTLE_ENDIAN) && !defined(EA_SYSTEM_BIG_ENDIAN)
#error If USER_GAMETALK_IS_LITTLE_ENDIAN is not defined - then EA_SYSTEM_BIG_ENDIAN or EA_SYSTEM_LITTLE_ENDIAN must be defined
#endif

	#if defined(EA_SYSTEM_LITTLE_ENDIAN)
		#define USER_GAMETALK_IS_LITTLE_ENDIAN 1
	#else
		#define USER_GAMETALK_IS_LITTLE_ENDIAN 0
	#endif
#endif

#ifndef USER_GAMETALK_MAX_BLOCKING_POLLS
	#define USER_GAMETALK_MAX_BLOCKING_POLLS 200
#endif

#ifndef USER_GAMETALK_MAX_PENDING_SYNCHRO_TRANSACTIONS
	#define USER_GAMETALK_MAX_PENDING_SYNCHRO_TRANSACTIONS 10
#endif

namespace EA
{
    namespace GameTalk
    {

		// GameTalk's allocator macro calls this function - avoids warning of not using name
		void * GameTalkAlloc(size_t bytes, const char * name);

		// GameTalk's deallocator macro calls this function - avoids warning of not using name
		void GameTalkFree(void* ptr, const char* name);

		enum Endian
		{
			LittleEndian,
			BigEndian = 128
		};

// --- GameTalk Memory Management Methods
        GAMETALK_API void* Alloc(size_t bytes, const char* name);
        GAMETALK_API void Free(void* ptr, const char* name);

    // All GameTalk objects use this macro to redirect their memory allocation policy.
#define USE_GAMETALK_ALLOC(name)                                                                \
        void* operator new (size_t bytes) { return(EA::GameTalk::Alloc(bytes, name)); };        \
        void  operator delete (void* ptr) {EA::GameTalk::Free(ptr, name); }                           \
        void* operator new[] (size_t bytes) { return(EA::GameTalk::Alloc(bytes, name)); };      \
        void  operator delete[] (void* ptr) {EA::GameTalk::Free(ptr, name); }                         \
        void* operator new (size_t, void* ptr) { return(ptr); }                                 \
        void  operator delete (void*, void*) {}

// --- Utility function
        bool StrIsEqual (const char* a, const char* b);

// --- Forward declarations
        class GameTalkHandler;
        class IGameTalkProtocol;
        class KeyContent;
        struct KeyContentBuffer;
        class SyncReplyGameTalkMessage;
        class ITimer;
		class GameTalkAllocator;
		class SynchroMsgTable;
        class SynchronousTransaction;

        // NOT CURRENTLY IN USE
        class GtCriticalSection
        {
        public:
            GtCriticalSection()
            {
                USER_GAMETALK_THREAD_LOCK();
            }

            ~GtCriticalSection()
            {
                USER_GAMETALK_THREAD_UNLOCK();
            }
        };

        // --- EndianSwap
        class EndianSwap
        {
        public:
            EndianSwap() {};
            ~EndianSwap() {};

            // Swap in little endian mode
            static uint32_t SwapLE32(uint32_t value);

            // Read a value on a buffer in a little endian mode
            // Increase the corresponding buffer
            static uint32_t ReadLE32(char*& inBuffer);

            // Read a value on a buffer in a little endian mode
            // Increase the corresponding buffer.  Also, the value that is read
            // is set to NULL.
            static uint32_t ReadAndNullLE32(char*& inBuffer);

            static void WriteLE32(char*& outBuffer, uint32_t outValue);

			// Change value to specified endian (possible nothing is required if endian requested is same as current platform)
			static uint32_t SwapEndian(uint32_t value, bool littleEndian);

			// If the current platform's endian is different from the required endian (value of the bool parameter) - this will return true
			static bool NeedSwap(bool littleEndian);
        };

        struct KeyContentBuffer
        {
            // Contains a pointer to the buffer that was allocated by someone else AND
            // that this KeyContentBuffer uses to portion out its component pieces (KeyContents, string buffer, etc)
            char * mBuffer;
            size_t mBufferSize;

            uint32_t mMaxSimultaneousMesgKeys;
            uint32_t mKeyContentsUsed;
            KeyContent ** mKeyContentPtrs;
            KeyContent * mKeyContents;

            char * mStringBuffer;
            // Size of string buffer
            size_t mStringBufferSize;
            size_t mStringBufferUsed;

            KeyContentBuffer(uint32_t maxSimultaneousMesgKeys, size_t strBufferSize);

            ~KeyContentBuffer()
            {
            }

            // Grab a block of KeyContents from the dynamic allocated buffer
            KeyContent ** GetKeyContents(uint32_t numKeyContents);
            // Release the usage of the KeyContents no longer in use
            void ReleaseKeyContents(uint32_t numKeyContents);

            size_t GetRemainingStringBufferSpace()
            {
                return mStringBufferSize - mStringBufferUsed;
            }
            // Grab a block of string data from the allocated buffer
            bool GetStringBuffer(size_t bufferSizeRequested, char *& stringBuffer);
            // Release the block of string data no longer in use from the dynamically allocated buffer
            void ReleaseStringBuffer(uint32_t bufferSize);

            USE_GAMETALK_ALLOC("GameTalk::KeyContentBuffer")
        private:
            KeyContentBuffer();
            KeyContentBuffer(const KeyContentBuffer& keyContentBuffer);
            KeyContentBuffer & operator=(const KeyContentBuffer & keyContentBuffer);
        };
    }
}

#endif  //  EA_GAMETALK_GAMETALKBASE_H

