#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)

	namespace __private {
		union UInt16
		{
			UInt16(BALL_INT16 s_) : s(s_) { }

			BALL_INT64 s;
			BALL_UINT64 u;
		};

		union UInt32
		{
			UInt32(BALL_INT32 s_) : s(s_) { }

			BALL_INT64 s;
			BALL_UINT64 u;
		};

		union UInt64
		{
			UInt64(BALL_INT64 s_) : s(s_) { }

			BALL_INT64 s;
			BALL_UINT64 u;
		};
	}

	template <>
	BALL_INLINE void swapBytes(BALL_INT16& t)
	{
		__private::UInt16 u(t);
		BALL_SWAP_BYTES_16(u.u);
		t = u.s;
	}

	template <>
	BALL_INLINE void swapBytes(BALL_UINT16& t)
	{
		BALL_SWAP_BYTES_16(t);
	}

	template <>
	BALL_INLINE void swapBytes(BALL_INT32& t)
	{
		__private::UInt32 u(t);
		BALL_SWAP_BYTES_32(u.u);
		t = u.s;
	}

	template <>
	BALL_INLINE void swapBytes(BALL_UINT32& t)
	{
		BALL_SWAP_BYTES_32(t);
	}

	template <>
	BALL_INLINE void swapBytes(BALL_INT64& t)
	{
		__private::UInt64 u(t);
		BALL_SWAP_BYTES_64(u.u);
		t = u.s;
	}

	template <>
	BALL_INLINE void swapBytes(BALL_UINT64& t)
	{
		BALL_SWAP_BYTES_64(t);
	}

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