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

// --- Includes
#include "GameTalk/GameTalkBase.h"
#include "GameTalk/IGameTalkProtocol.h"

// --- Using

using namespace EA::GameTalk;

// --- Utility functions
bool EA::GameTalk::StrIsEqual (const char* a, const char* b)
{
    while (*a && *b)
    {
        if (USER_GAMETALK_TOLOWER(*a) != USER_GAMETALK_TOLOWER(*b))
            return(false);

        a++;
        b++;
    }
    return(*a == *b);
}


// ------------------------------------------------------------------------------
/*! Interface :: SwapLE32
If the platform is little endian do nothing
If the platform is big endian, change the endianess value
*/
// ------------------------------------------------------------------------------
#if defined(EA_PLATFORM_PS3)
#include "sdk_version.h"
#endif

// Because there are intel versions of OS X (one uses the Intel chipset and the other doesn't) - we can't use the following to differentiate (and thus use the native intrinsic)
/*#if defined(EA_PLATFORM_OSX) && !defined( EA_ASM_STYLE_INTEL )
#include <ppc_intrinsics.h>
#define __loadwordbytereverse(offset, ptr) __lwbrx(ptr, offset)
*/
#if defined (EA_PLATFORM_XENON)
#include <ppcintrinsics.h>
#elif defined(EA_PLATFORM_PS3) && (CELL_SDK_VERSION >= 0x190000)
#include <ppu_intrinsics.h>
#elif  defined (EA_PLATFORM_GAMECUBE) || defined(EA_PLATFORM_REVOLUTION) || defined(EA_PLATFORM_PS3)
// We may need to add more #elif conditions for any other platforms that natively define the __loadwordbytereverse function
static inline uint32_t __loadwordbytereverse (uint32_t offset, void* ptr)
{
      register uint32_t result;
      asm ( "lwbrx %0,%1,%2"
            : "=r" (result)                           // outputs
            : "b" (ptr), "r" (offset)                 // inputs
            ) ;                                       // clobbers
      return result;
} ;
#else
// the uint32_t is the offset from the start of the pointer - I'm going to assume it is 0.  On windows at least - we should use the intrinsic available instead of this crude function below
static inline uint32_t __loadwordbytereverse (uint32_t offset, void* ptr)
{
	USER_GAMETALK_ASSERT(offset == 0, "this function is too stupid to use offsets");
	USER_GAMETALK_USE_VAR_TO_AVOID_WARNING(offset);
	char *originalPtr = (char*)ptr;
	uint32_t copyOfValue = *((uint32_t*)ptr);
	char *copyPtr = (char*)&copyOfValue;
	originalPtr[0] = copyPtr[3];
	originalPtr[1] = copyPtr[2];
	originalPtr[2] = copyPtr[1];
	originalPtr[3] = copyPtr[0];
	return *((uint32_t*)originalPtr);
}
#endif

uint32_t EndianSwap::SwapLE32(uint32_t value)
{
#if defined(EA_PLATFORM_PS3) && (CELL_SDK_VERSION >= 0x190000)
    return(__lwbrx(&value));
#elif (USER_GAMETALK_IS_LITTLE_ENDIAN)
      //  Already in little endian, do nothing...
      return  (value);
#else
      //  In big endian, must change the order
      return(__loadwordbytereverse(0, &value));
#endif
}

// For Windows - would like to use ntohl - but it is defined in Winsock2.h - don't think we want the PC version of GameTalk to be strongly dependent on that
uint32_t EndianSwap::SwapEndian(uint32_t value, bool littleEndian)
{
// If the user defined USER_GAMETALK_SWAP_ENDIAN32 - then check if USER_GAMETALK_IS_LITTLE_ENDIAN is 1 - if it is then determine if we need to swap based on whether the user wants their value in little endian
#if defined(USER_GAMETALK_SWAP_ENDIAN32)
	#if USER_GAMETALK_IS_LITTLE_ENDIAN
		bool needToSwap = !littleEndian;
	#else
		bool needToSwap = littleEndian;
	#endif

	if (!needToSwap)
		return (value);
	else
		return USER_GAMETALK_SWAP_ENDIAN32(value);
#elif defined(EA_PLATFORM_PS3) && (CELL_SDK_VERSION >= 0x190000)
	if (littleEndian) //  Our platform is in big endian - must change the order
		return(__lwbrx(&value));
	else
		return (value);
#elif (USER_GAMETALK_IS_LITTLE_ENDIAN)
	if (littleEndian) //  Already in little endian, do nothing...
		return  (value);
	else
		return(__loadwordbytereverse(0, &value));
#else
	if (littleEndian) //  Our platform is in big endian - must change the order
		  return(__loadwordbytereverse(0, &value));
	else
		return (value);
#endif
}

bool EndianSwap::NeedSwap(bool littleEndian)
{
#if (USER_GAMETALK_IS_LITTLE_ENDIAN)
	if (littleEndian) //  If already in little endian, do nothing (i.e. no swap necessary)
		return  (false);
	else
		return (true);
#else
	if (littleEndian) //  Our platform is in big endian - must change the order
		  return (true);
	else
		return (false);
#endif
}
// ------------------------------------------------------------------------------
// ReadLE32
// ------------------------------------------------------------------------------
uint32_t EndianSwap::ReadLE32(char*& inBuffer)
{
    uint32_t v = 0;
    USER_GAMETALK_MEMCPY(&v, inBuffer, 4);
    inBuffer += 4;
    v = SwapLE32(v);
    return v;
}


uint32_t EndianSwap::ReadAndNullLE32(char*& inBuffer)
{
    uint32_t v = 0;
    USER_GAMETALK_MEMCPY(&v, inBuffer, 4);
    // Make the byte null now that we've read the value - we do this for
    // null delimination of strings that occur earlier in the byte array
    *inBuffer = 0;
    inBuffer += 4;
    v = SwapLE32(v);
    return v;
}

// ------------------------------------------------------------------------------
// WriteLE32
// ------------------------------------------------------------------------------
void EndianSwap::WriteLE32(char*& outBuffer, uint32_t outValue)
{
    uint32_t v = SwapLE32(outValue);
    USER_GAMETALK_MEMCPY(outBuffer, &v, 4);
    outBuffer += 4;
}


namespace EA
{
    namespace GameTalk
    {
        // ------------------------------------------------------------------------------
        // Default Alloc function for GameTalk  - avoids warning of not using name
        // ------------------------------------------------------------------------------
		void * GameTalkAlloc(size_t bytes, const char * name)
		{
			USER_GAMETALK_USE_VAR_TO_AVOID_WARNING(name);
			void *ptr = (void*)(new char[bytes]);
			return ptr;
		}

        // ------------------------------------------------------------------------------
        // Default Dealloc function for GameTalk  - avoids warning of not using name
        // ------------------------------------------------------------------------------
        void GameTalkFree(void* ptr, const char* name)
        {
			USER_GAMETALK_USE_VAR_TO_AVOID_WARNING(name);

            if (ptr)
                delete[] (char*)(ptr);
        }

        // ------------------------------------------------------------------------------
        // Alloc
        // ------------------------------------------------------------------------------
        void* Alloc(size_t bytes, const char* name)
        {
            if (bytes > 0)
                return USER_GAMETALK_ALLOC(bytes, name);
            else
                return NULL;
        }

        // ------------------------------------------------------------------------------
        // Free
        // ------------------------------------------------------------------------------
        void Free(void* ptr, const char* name)
        {
            if (ptr != NULL)
                USER_GAMETALK_FREE(ptr, name);
        }
	}
}

