/******************************************************************************/
/* STRINGS AND RELATED UTILITIES - INLINE CODE ********************************/
/******************************************************************************/

#ifndef _H_EBCL_INLINE_STRINGS
#define _H_EBCL_INLINE_STRINGS
#include <ebcl/Strings.hh>
namespace ebcl {


/*= T_Character ==============================================================*/

inline constexpr T_Character::T_Character( ) noexcept
	: codepoint( 0 )
{ }

inline constexpr T_Character::T_Character(
		const T_Character& other ) noexcept
	: codepoint( other.codepoint )
{ }

template< typename T , typename >
inline constexpr T_Character::T_Character(
		T codepoint ) noexcept
	: codepoint( uint32_t( codepoint ) )
{
	assert( codepoint >= 0 );
}

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

inline constexpr bool T_Character::isValid( ) const
{
	return codepoint < 0x110000;
}

inline constexpr bool T_Character::isAscii( ) const
{
	return codepoint < 128;
}

inline constexpr bool T_Character::isControl( ) const
{
	return codepoint < 32;
}

inline constexpr bool T_Character::isUppercase( ) const
{
	return codepoint >= 'A' && codepoint <= 'Z';
}

inline constexpr bool T_Character::isLowercase( ) const
{
	return codepoint >= 'a' && codepoint <= 'z';
}

inline constexpr bool T_Character::isAlpha( ) const
{
	return isUppercase( ) || isLowercase( );
}

inline constexpr bool T_Character::isNumeric( ) const
{
	return codepoint >= '0' && codepoint <= '9';
}

inline constexpr bool T_Character::isAlphanumeric( ) const
{
	return isAlpha( ) || isNumeric( );
}

inline constexpr bool T_Character::isWhitespace( ) const
{
	return codepoint == 32 || codepoint == '\t'
	       || codepoint == '\n' || codepoint == '\r';
}

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

inline constexpr bool T_Character::operator== ( T_Character const& other ) const
{
	return codepoint == other.codepoint;
}

inline constexpr bool T_Character::operator!= ( T_Character const& other ) const
{
	return codepoint != other.codepoint;
}

inline constexpr bool T_Character::operator< ( T_Character const& other ) const
{
	return codepoint < other.codepoint;
}

inline constexpr bool T_Character::operator> ( T_Character const& other ) const
{
	return codepoint > other.codepoint;
}

inline constexpr bool T_Character::operator<= ( T_Character const& other ) const
{
	return codepoint <= other.codepoint;
}

inline constexpr bool T_Character::operator>= ( T_Character const& other ) const
{
	return codepoint >= other.codepoint;
}

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

template< typename T , typename >
inline constexpr bool T_Character::operator== ( T other ) const
{
	return codepoint == uint32_t( other );
}

template< typename T , typename >
inline constexpr bool T_Character::operator!= ( T other ) const
{
	return codepoint != uint32_t( other );
}

template< typename T , typename >
inline constexpr bool T_Character::operator< ( T other ) const
{
	return codepoint < uint32_t( other );
}

template< typename T , typename >
inline constexpr bool T_Character::operator<= ( T other ) const
{
	return codepoint <= uint32_t( other );
}

template< typename T , typename >
inline constexpr bool T_Character::operator> ( T other ) const
{
	return codepoint > uint32_t( other );
}

template< typename T , typename >
inline constexpr bool T_Character::operator>= ( T other ) const
{
	return codepoint >= uint32_t( other );
}

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

inline constexpr T_Character::operator uint32_t ( ) const
{
	return codepoint;
}

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

inline uint32_t T_Character::writeTo( char* output , uint32_t avail ) const
{
	return UTF8PutCodepoint( output , avail , codepoint );
}

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

inline constexpr T_Character T_Character::toUpper( ) const
{
	if ( isLowercase( ) ) {
		return *this & (~0x20);
	} else {
		return *this;
	}
}

inline constexpr T_Character T_Character::toLower( ) const
{
	if ( isUppercase( ) ) {
		return *this | 0x20;
	} else {
		return *this;
	}
}


/*= A_StringData =============================================================*/

inline bool A_StringData::valid( ) const
{
	return valid_;
}

inline char const* A_StringData::data( ) const
{
	return data_;
}

inline uint32_t A_StringData::length( ) const
{
	return length_;
}

inline uint32_t A_StringData::size( ) const
{
	return size_;
}


/*= T_String =================================================================*/

inline T_String T_String::Pooled( char const* string )
{
	return Pooled( string , strlen( string ) );
}

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

inline bool T_String::valid( ) const
{
	return data_->valid( );
}

inline uint32_t T_String::size( ) const
{
	return data_->size( );
}

inline uint32_t T_String::length( ) const
{
	return data_->length( );
}

inline char const* T_String::data( ) const
{
	return data_->data( );
}

inline T_String::operator bool ( ) const
{
	return size( ) != 0;
}

inline bool T_String::operator! ( ) const
{
	return size( ) == 0;
}

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

inline T_Character T_String::operator[] ( uint32_t index ) const
{
	assert( index < length( ) );
	char const* const d( data( ) );
	return UTF8GetCodepoint( d + UTF8GetMemoryOffset( d , index ) );
}

inline T_String T_String::range( uint32_t start , uint32_t end ) const
{
	if ( start > end ) {
		return T_String( );
	}
	return substr( start , end == UINT32_MAX ? UINT32_MAX : ( end - start + 1 ) );
}

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

inline bool T_String::equals( T_String const& other ) const
{
	return data_ == other.data_ || ( other.size( ) == size( )
					&& ( size( ) == 0 || !memcmp( other.data( ) , data( ) , size( ) ) ) );
}

inline bool T_String::equals( char const* string ) const
{
	const auto l( strlen( string ) );
	return size( ) == l && ( l == 0 || !memcmp( string , data( ) , l ) );
}

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

inline bool T_String::operator== ( T_String const& other ) const
{
	return equals( other );
}

inline bool T_String::operator!= ( T_String const& other ) const
{
	return !equals( other );
}

inline bool T_String::operator< ( T_String const& other ) const
{
	return compare( other ) < 0;
}

inline bool T_String::operator> ( T_String const& other ) const
{
	return compare( other ) > 0;
}

inline bool T_String::operator>= ( T_String const& other ) const
{
	return compare( other ) >= 0;
}

inline bool T_String::operator<= ( T_String const& other ) const
{
	return compare( other ) <= 0;
}

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

inline bool T_String::operator== ( char const* string ) const
{
	return equals( string );
}

inline bool T_String::operator!= ( char const* string ) const
{
	return !equals( string );
}

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

inline int64_t T_String::toInteger( bool * ok , int base , bool useSep ,
		T_Character separator ) const
{
	return UTF8ToInteger( data( ) , size( ) , ok , base , useSep , separator );
}

inline uint64_t T_String::toUnsignedInteger( bool * ok , int base , bool useSep ,
		T_Character separator ) const
{
	return UTF8ToUnsignedInteger( data( ) , size( ) , ok , base , useSep , separator );
}

inline double T_String::toDouble( bool * ok , T_Character decimalPoint ,
		bool useSep , T_Character separator ) const
{
	return UTF8ToDouble( data( ) , size( ) , ok , decimalPoint , useSep , separator );
}

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

inline T_StringIterator T_String::getIterator( uint32_t offset ) const
{
	if ( offset >= data_->size( ) ) {
		return T_StringIterator( nullptr , 0 );
	}
	return T_StringIterator( data_ , offset );
}

inline T_String::operator T_StringIterator( ) const
{
	return getIterator( 0 );
}


/*= T_String helpers =========================================================*/

inline M_DEFINE_HASH( T_String )
{
	return HashData(
			reinterpret_cast< uint8_t const* >( item.data( ) ) ,
			item.size( ) );
}

inline M_DEFINE_COMPARATOR( T_String )
{
	return a.compare( b );
}


/*= T_StringIterator =========================================================*/

inline T_StringIterator::T_StringIterator( T_StringIterator&& other ) noexcept
	: data_( other.data_ ) , pos_( other.pos_ ) ,
	index_( other.index_ ) ,
	codepoint_( other.codepoint_ ) ,
	bytes_( other.bytes_ )
{
	other.data_ = nullptr;
}

inline T_StringIterator& T_StringIterator::operator= ( T_StringIterator&& other ) noexcept
{
	swap( *this , other );
	return *this;
}

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

inline uint32_t T_StringIterator::index( ) const
{
	return index_;
}

inline bool T_StringIterator::atEnd( ) const
{
	return data_ == nullptr || pos_ == data_->size( );
}

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

inline T_Character T_StringIterator::character( ) const
{
	return codepoint_;
}

inline T_StringIterator::operator T_Character ( ) const
{
	return codepoint_;
}


/*= T_StringBuilder ==========================================================*/

inline T_StringBuilder::T_StringBuilder( ) noexcept
	: data_( nullptr ) , capacity_( 0 ) , size_( 0 ) , length_( 0 )
{ }

inline T_StringBuilder::T_StringBuilder( char const* string )
	: T_StringBuilder( string , strlen( string ) )
{ }

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

inline char const* T_StringBuilder::data( ) const
{
	return data_;
}

inline uint32_t T_StringBuilder::capacity( ) const
{
	return capacity_;
}

inline uint32_t T_StringBuilder::size( ) const
{
	return size_;
}

inline uint32_t T_StringBuilder::length( ) const
{
	return length_;
}

inline T_StringBuilder::operator bool ( ) const
{
	return size_ != 0;
}

inline bool T_StringBuilder::operator! ( ) const
{
	return size_ == 0;
}

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

inline T_StringBuilder& T_StringBuilder::clear( )
{
	size_ = length_ = 0;
	return *this;
}

inline T_StringBuilder& T_StringBuilder::truncate(
		const uint32_t maxLength ) noexcept
{
	if ( maxLength < length_ ) {
		size_ = UTF8GetMemoryOffset( data_ , maxLength );
		length_ = maxLength;
	}
	return *this;
}

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

inline bool T_StringBuilder::operator== ( T_StringBuilder const& other ) const
{
	return other.size_ == size_ && ( size_ == 0 || !memcmp( other.data_ , data_ , size_ ) );
}

inline bool T_StringBuilder::operator!= ( T_StringBuilder const& other ) const
{
	return other.size_ != size_ || ( size_ != 0 && memcmp( other.data_ , data_ , size_ ) );
}

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

inline bool T_StringBuilder::operator== ( T_String const& string ) const
{
	return string.size( ) == size_ && ( size_ == 0 || !memcmp( string.data( ) , data_ , size_ ) );
}

inline bool T_StringBuilder::operator!= ( T_String const& string ) const
{
	return string.size( ) != size_ || ( size_ != 0 && memcmp( string.data( ) , data_ , size_ ) );
}

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

inline bool T_StringBuilder::operator== ( char const* string ) const
{
	return strlen( string ) == size_ && ( size_ == 0 || !memcmp( string , data_ , size_ ) );
}

inline bool T_StringBuilder::operator!= ( char const* string ) const
{
	return strlen( string ) != size_ || ( size_ != 0 && memcmp( string , data_ , size_ ) );
}

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

inline int64_t T_StringBuilder::toInteger( bool * ok , int base , bool useSep ,
		T_Character separator ) const
{
	return UTF8ToInteger( data_ , size_ , ok , base , useSep , separator );
}

inline uint64_t T_StringBuilder::toUnsignedInteger( bool * ok , int base , bool useSep ,
		T_Character separator ) const
{
	return UTF8ToUnsignedInteger( data_ , size_ , ok , base , useSep , separator );
}

inline double T_StringBuilder::toDouble( bool * ok , T_Character decimalPoint ,
		bool useSep , T_Character separator ) const
{
	return UTF8ToDouble( data( ) , size( ) , ok , decimalPoint , useSep , separator );
}

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

inline T_StringBuilder& operator<< ( T_StringBuilder& sb , T_StringBuilder const& value )
{
	return sb.append( value );
}

inline T_StringBuilder& operator<< ( T_StringBuilder& sb , T_StringBuilder&& value )
{
	return sb.append( std::move( value ) );
}

inline T_StringBuilder& operator<< ( T_StringBuilder& sb , T_String const& value )
{
	return sb.append( value );
}

inline T_StringBuilder& operator<< ( T_StringBuilder& sb , char const* value )
{
	return sb.append( value , strlen( value ) );
}

inline T_StringBuilder& operator<< ( T_StringBuilder& sb , char value )
{
	return sb.append( value );
}

inline T_StringBuilder& operator<< ( T_StringBuilder& sb , T_Character value )
{
	return sb.append( value );
}

inline T_StringBuilder& operator<< ( T_StringBuilder& sb , int16_t value )
{
	return sb.appendNumeric( int64_t( value ) );
}

inline T_StringBuilder& operator<< ( T_StringBuilder& sb , int32_t value )
{
	return sb.appendNumeric( int64_t( value ) );
}

inline T_StringBuilder& operator<< ( T_StringBuilder& sb , int64_t value )
{
	return sb.appendNumeric( value );
}

inline T_StringBuilder& operator<< ( T_StringBuilder& sb , uint16_t value )
{
	return sb.appendNumeric( uint64_t( value ) );
}

inline T_StringBuilder& operator<< ( T_StringBuilder& sb , uint32_t value )
{
	return sb.appendNumeric( uint64_t( value ) );
}

inline T_StringBuilder& operator<< ( T_StringBuilder& sb , uint64_t value )
{
	return sb.appendNumeric( value );
}

inline T_StringBuilder& operator<< ( T_StringBuilder& sb , float value )
{
	return sb.appendDouble( double( value ) );
}

inline T_StringBuilder& operator<< ( T_StringBuilder& sb , double value )
{
	return sb.appendDouble( value );
}


} // namespace
#endif // _H_EBCL_INLINE_STRINGS