/******************************************************************************/
/* VARIOUS UTILITIES - INLINE CODE ********************************************/
/******************************************************************************/

#ifndef _H_LW_LIB_INLINE_UTILITIES
#define _H_LW_LIB_INLINE_UTILITIES
#include <lw/lib/Utilities.hh>
namespace lw {


template< typename T >
inline constexpr bool IsPowerOf2( T value )
{
	static_assert( std::is_integral< T >( ) , "integer type only" );
	return value > 0 && ( value & ( value - 1 ) ) == 0;
}


/*= ENDIAN DETECTION =========================================================*/

inline static constexpr bool IsBigEndian( )
{
	return E_Endian::NATIVE == E_Endian::BIG;
}

inline static constexpr bool IsLittleEndian( )
{
	return E_Endian::NATIVE == E_Endian::LITTLE;
}

static_assert( IsBigEndian( ) || IsLittleEndian( ) ,
	      "unsupported endianness" );


/*= ENDIAN CONVERSION ========================================================*/

template<
	typename T ,
	uint32_t S = sizeof( T ) ,
	typename = std::enable_if_t<
		std::is_integral< T >::value
		|| std::is_floating_point< T >::value
		>
	>
struct T_ByteSwapper_ { };


/*----------------------------------------------------------------------------*/

template< typename T >
struct T_ByteSwapper_< T , 1 >
{
	static constexpr T swap( T value )
	{
		return value;
	}
};


/*----------------------------------------------------------------------------*/

template< typename T >
struct T_ByteSwapper_< T , 2 >
{
	static T swap( T value )
	{
		union { T v;
			uint16_t i;
		} u = { value };
		u.i = __builtin_bswap16( u.i );
		return u.v;
	}
};


/*----------------------------------------------------------------------------*/

template< typename T >
struct T_ByteSwapper_< T , 4 >
{
	static T swap( T value )
	{
		union { T v;
			uint32_t i;
		} u = { value };
		u.i = __builtin_bswap32( u.i );
		return u.v;
	}
};


/*----------------------------------------------------------------------------*/

template< typename T >
struct T_ByteSwapper_< T , 8 >
{
	static T swap( T value );
};


template< typename T >
inline T T_ByteSwapper_< T , 8 >::swap( T value )
{
	union { T v;
		uint64_t i;
	} u = { value };
	u.i = __builtin_bswap64( u.i );
	return u.v;
}

/*----------------------------------------------------------------------------*/

template< typename T >
struct T_LittleEndian< T , E_Endian::LITTLE >
{
	static T convert( T value )
	{
		return value;
	}
};


template< typename T >
struct T_LittleEndian< T , E_Endian::BIG >
{
	static T convert( T value )
	{
		return T_ByteSwapper_< T >::swap( value );
	}
};


/*----------------------------------------------------------------------------*/

template< typename T >
struct T_BigEndian< T , E_Endian::LITTLE >
{
	static T convert( T value )
	{
		return T_ByteSwapper_< T >::swap( value );
	}
};


template< typename T >
struct T_BigEndian< T , E_Endian::BIG >
{
	static T convert( T value )
	{
		return value;
	}
};


/*= COMPARATORS AND SORTING ==================================================*/

template< typename T >
inline int T_Comparator< T >::compare( T const& a , T const& b )
{
	if ( a < b ) return -1;
	if ( a > b ) return 1;
	return 0;
}

/*----------------------------------------------------------------------------*/

// T_Sorter_ - "Plumbing" for Sort
struct T_Sorter_
{
	typedef std::function< void ( uint8_t* a , uint8_t* b ) > F_Swap;
	typedef std::function< int ( uint8_t const* a , uint8_t const* b ) > F_Cmp;
	static void sort( uint8_t* data , uint32_t itemSize , uint32_t items , F_Swap swap , F_Cmp cmp );
};


template< typename T >
void Sort( T* array , uint32_t items , F_Comparator< T > comparator )
{
	T_Sorter_::sort( reinterpret_cast< uint8_t* >( array ) ,
			sizeof( T ) , items ,
			[]( uint8_t* a , uint8_t* b ) {
			using std::swap;
			swap( *reinterpret_cast< T* >( a ) ,
			     *reinterpret_cast< T* >( b ) );
		} ,
			[comparator]( uint8_t const* a , uint8_t const* b ) {
			return comparator(
					  *reinterpret_cast< T const* >( a ) ,
					  *reinterpret_cast< T const* >( b ) );
		} );
}


/*= HASHING ==================================================================*/

template< typename T , int S >
inline uint32_t T_HashFunction< T , S >::hash( T const& item )
{
	return HashData( reinterpret_cast< uint8_t const* >( &item ) , sizeof( T ) );
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline uint32_t ComputeHash( T const& item )
{
	return T_HashFunction< T >::hash( item );
}

/*----------------------------------------------------------------------------*/
// Hash function specializations for data sizes 1, 2, 4 and 8.

template< typename T >
struct T_HashFunction< T , 1 >
{
	static uint32_t hash( T const& item )
	{
		return *reinterpret_cast< uint8_t const* >( &item );
	}
};


template< typename T >
struct T_HashFunction< T , 2 >
{
	static uint32_t hash( T const& item )
	{
		return *reinterpret_cast< uint16_t const* >( &item );
	}
};


template< typename T >
struct T_HashFunction< T , 4 >
{
	static uint32_t hash( T const& item )
	{
		return *reinterpret_cast< uint32_t const* >( &item );
	}
};


template< typename T >
struct T_HashFunction< T , 8 >
{
	static uint32_t hash( T const& item )
	{
		uint64_t v = *reinterpret_cast< uint64_t const* >( &item );
		return ( v >> 32 ) ^ v;
	}
};


/*= PRIVATE IMPLEMENTATION BASE ==============================================*/

inline A_PrivateImplementation::A_PrivateImplementation( void* p , T_Destructor_ destructor )
	: p_( p ) , piDestructor_( destructor )
{ }

template< typename T >
inline A_PrivateImplementation::A_PrivateImplementation( T* p )
	: A_PrivateImplementation( p , []( void* ptr ) {
		reinterpret_cast< T* >( ptr )->~T( );
	} )
{ }

inline A_PrivateImplementation::A_PrivateImplementation( A_PrivateImplementation&& other ) noexcept
	: p_( other.p_ ) , piDestructor_( other.piDestructor_ )
{
	other.p_ = nullptr;
}

inline A_PrivateImplementation& A_PrivateImplementation::operator=( A_PrivateImplementation&& other ) noexcept
{
	callPrivateDestructor( );
	p_ = other.p_;
	piDestructor_ = other.piDestructor_;
	other.p_ = nullptr;
	return *this;
}

inline A_PrivateImplementation::~A_PrivateImplementation( )
{
	callPrivateDestructor( );
}

template< typename T >
inline T& A_PrivateImplementation::p( ) const
{
	return *reinterpret_cast< T* >( p_ );
}

inline void A_PrivateImplementation::callPrivateDestructor( ) noexcept
{
	if ( p_ && piDestructor_ ) {
		piDestructor_( p_ );
		::operator delete( p_ );
	}
}


}
#endif // _H_LW_LIB_INLINE_UTILITIES