/*
	Twilight Prophecy 3D/Multimedia SDK
	A multi-platform development system for virtual reality and multimedia.

	Copyright (C) 1997-2001 by Twilight 3D Finland Oy Ltd.
*/
#ifndef PRCORE_ENDIAN_HPP
#define PRCORE_ENDIAN_HPP



namespace prcore
{

	// byte swaps
	
inline void ByteSwap(int8& i)
{
	// satisfy macros defined later
}

inline void ByteSwap(uint8& i)
{
	// satisfy macros defined later
}

inline void ByteSwap(int16& i)
{
	i = (i<<8) | (i>>8);
}

inline void ByteSwap(uint16& i)
{
	i = (i<<8) | (i>>8);
}

inline void ByteSwap(int32& i)
{
	i = (i>>24) |
		(i>> 8) & 0x0000ff00 |
		(i<< 8) & 0x00ff0000 |
		(i<<24);
}

inline void ByteSwap(uint32& i)
{
	i = (i>>24) |
		(i>> 8) & 0x0000ff00 |
		(i<< 8) & 0x00ff0000 |
		(i<<24);
}

inline void ByteSwap(float& v)
{
	uint32& i = (uint32&)v;
	i = (i>>24) |
		(i>> 8) & 0x0000ff00 |
		(i<< 8) & 0x00ff0000 |
		(i<<24);
}

/*
work-around against gcc/linux warning:
"integer constant out of range"

interestingly enough, the gcc allows 64bit datatypes, but not
programming with them(?) like follows. any suggestions how to fix this?
*/
#ifndef PRCORE_PLATFORM_LINUX

inline void ByteSwap(int64& i)
{
	i = (i>>56) |
		(i>>40) & 0x000000000000ff00 |
		(i>>24) & 0x0000000000ff0000 |
		(i>> 8) & 0x00000000ff000000 |
		(i<< 8) & 0x000000ff00000000 |
		(i<<24) & 0x0000ff0000000000 |
		(i<<40) & 0x00ff000000000000 |
		(i<<56);
}

inline void ByteSwap(uint64& i)
{
	i = (i>>56) |
		(i>>40) & 0x000000000000ff00 |
		(i>>24) & 0x0000000000ff0000 |
		(i>> 8) & 0x00000000ff000000 |
		(i<< 8) & 0x000000ff00000000 |
		(i<<24) & 0x0000ff0000000000 |
		(i<<40) & 0x00ff000000000000 |
		(i<<56);
}

inline void ByteSwap(double& v)
{
	uint64& i = (uint64&)v;
	i = (i>>56) |
		(i>>40) & 0x000000000000ff00 |
		(i>>24) & 0x0000000000ff0000 |
		(i>> 8) & 0x00000000ff000000 |
		(i<< 8) & 0x000000ff00000000 |
		(i<<24) & 0x0000ff0000000000 |
		(i<<40) & 0x00ff000000000000 |
		(i<<56);
}

#endif // PRCORE_PLATFORM_LINUX

	// remap templates

template <class T>
inline T LittleEndianRemap(T v)
{
	#ifdef PRCORE_BIG_ENDIAN
	ByteSwap( v );
	#endif
	return v;
}

template <class T>
inline T BigEndianRemap(T v)
{
	#ifdef PRCORE_LITTLE_ENDIAN
	ByteSwap( v );
	#endif
	return v;
}

template <class T, class R>
inline T ReadLittleEndianBuffer(R& buffer)
{
	T v = *(T*)buffer;
	((char*&)buffer) += sizeof(T);
	return LittleEndianRemap( v );
}

template <class T, class R>
inline T ReadBigEndianBuffer(R& buffer)
{
	T v = *(T*)buffer;
	((char*&)buffer) += sizeof(T);
	return BigEndianRemap( v );
}

	// runtime endianess

inline bool IsLittleEndian()
{
	int v = 1;
	return *((char*)&v) != 0;
}

inline bool IsBigEndian()
{
	int v = 1;
	return *((char*)&v) == 0;
}

} // namespace prcore



#endif