#include <BALL/CONFIG/config.h>

namespace BALL
{
#define BALL_SWAP_BYTES_16(x)\
	x = ((x >> 8)  & 0x00FFu) | ((x << 8)  & 0xFF00u)

#define BALL_SWAP_BYTES_32(x)\
	x = ((x >> 8)  & 0x00FF00FFu) | ((x << 8)  & 0xFF00FF00u);\
	x = ((x >> 16) & 0x0000FFFFu) | ((x << 16) & 0xFFFF0000u)

#define BALL_SWAP_BYTES_64(x)\
	x = ((x >> 8)  & 0x00FF00FF00FF00FFull) | ((x << 8)  & 0xFF00FF00FF00FF00ull);\
	x = ((x >> 16) & 0x0000FFFF0000FFFFull) | ((x << 16) & 0xFFFF0000FFFF0000ull);\
	x = ((x >> 32) & 0x00000000FFFFFFFFull) | ((x << 32) & 0xFFFFFFFF00000000ull)

	template <>
	BALL_INLINE void swapBytes(unsigned short& t)
	{
#if   BALL_USHORT_SIZE == 2
			BALL_SWAP_BYTES_16(t);
#elif BALL_USHORT_SIZE == 4
			BALL_SWAP_BYTES_32(t);
#elif BALL_USHORT_SIZE == 8
			BALL_SWAP_BYTES_64(t);
#else
	#error "Unknown unsigned short size, refusing to compile."
#endif
	}

	template <>
	BALL_INLINE void swapBytes(short& t)
	{
#if   BALL_SHORT_SIZE == 2
			BALL_SWAP_BYTES_16(t);
#elif BALL_SHORT_SIZE == 4
			BALL_SWAP_BYTES_32(t);
#elif BALL_SHORT_SIZE == 8
			BALL_SWAP_BYTES_64(t);
#else
	#error "Unknown short size, refusing to compile."
#endif
	}

	template <>
	BALL_INLINE void swapBytes(unsigned int& t)
	{
#if   BALL_UINT_SIZE == 2
			BALL_SWAP_BYTES_16(t);
#elif BALL_UINT_SIZE == 4
			BALL_SWAP_BYTES_32(t);
#elif BALL_UINT_SIZE == 8
			BALL_SWAP_BYTES_64(t);
#else
	#error "Unknown unsigned int size, refusing to compile."
#endif
	}

	template <>
	BALL_INLINE void swapBytes(int& t)
	{
#if   BALL_INT_SIZE == 2
			BALL_SWAP_BYTES_16(t);
#elif BALL_INT_SIZE == 4
			BALL_SWAP_BYTES_32(t);
#elif BALL_INT_SIZE == 8
			BALL_SWAP_BYTES_64(t);
#else
	#error "Unknown int size, refusing to compile."
#endif
	}

	template <>
	BALL_INLINE void swapBytes(long& t)
	{
#if   BALL_LONG_SIZE == 2
			BALL_SWAP_BYTES_16(t);
#elif BALL_LONG_SIZE == 4
			BALL_SWAP_BYTES_32(t);
#elif BALL_LONG_SIZE == 8
			BALL_SWAP_BYTES_64(t);
#else
	#error "Unknown long size, refusing to compile."
#endif
	}

	template <>
	BALL_INLINE void swapBytes(unsigned long& t)
	{
#if   BALL_ULONG_SIZE == 2
			BALL_SWAP_BYTES_16(t);
#elif BALL_ULONG_SIZE == 4
			BALL_SWAP_BYTES_32(t);
#elif BALL_ULONG_SIZE == 8
			BALL_SWAP_BYTES_64(t);
#else
	#error "Unknown unsigned long size, refusing to compile."
#endif
	}

	namespace __private
	{
		//We need these unions to provide a safe cast (without violating strict aliasing)
		//from float/double to a bitfield
		union UFloat
		{
			UFloat(float f_) : f(f_) { }

#if   BALL_FLOAT_SIZE == 2
			BALL_UINT16 u;
#elif BALL_FLOAT_SIZE == 4
			BALL_UINT32 u;
#elif BALL_FLOAT_SIZE == 8
			BALL_UINT64 u;
#else
	#error "Unknown double size, refusing to compile."
#endif
			float f;
		};

		union UDouble
		{
			UDouble(double f_) : f(f_) { }

#if   BALL_DOUBLE_SIZE == 2
			BALL_UINT16 u;
#elif BALL_DOUBLE_SIZE == 4
			BALL_UINT32 u;
#elif BALL_DOUBLE_SIZE == 8
			BALL_UINT64 u;
#else
	#error "Unknown double size, refusing to compile."
#endif
			double f;
		};
	}

	template <>
	BALL_INLINE void swapBytes(float& f)
	{
		__private::UFloat u(f);
#if   BALL_FLOAT_SIZE == 2
		BALL_SWAP_BYTES_16(u.u);
#elif BALL_FLOAT_SIZE == 4
		BALL_SWAP_BYTES_32(u.u);
#elif BALL_FLOAT_SIZE == 8
		BALL_SWAP_BYTES_64(u.u);
#else
	#error "Unknown float size, refusing to compile."
#endif
		f = u.f;
	}

	template <>
	BALL_INLINE void swapBytes(double& f)
	{
		__private::UDouble u(f);
#if   BALL_DOUBLE_SIZE == 2
		BALL_SWAP_BYTES_16(u.u);
#elif BALL_DOUBLE_SIZE == 4
		BALL_SWAP_BYTES_32(u.u);
#elif BALL_DOUBLE_SIZE == 8
		BALL_SWAP_BYTES_64(u.u);
#else
	#error "Unknown double size, refusing to compile."
#endif
		f = u.f;
	}
}
