#ifndef __cxxtest__ValueTraits_h__
#define __cxxtest__ValueTraits_h__

//
// ValueTraits are used by CxxTest to convert arbitrary
// values used in TS_ASSERT_EQUALS() to a string representation.
//
// This header file contains value traits for builtin integral types.
// To declare value traits for new types you should instantiate the class
// ValueTraits<YourClass>.
//

#include <cxxtest/Flags.h>

#ifdef _CXXTEST_OLD_TEMPLATE_SYNTAX
#define CXXTEST_TEMPLATE_INSTANTIATION
#else  // !_CXXTEST_OLD_TEMPLATE_SYNTAX
#define CXXTEST_TEMPLATE_INSTANTIATION template <>
#endif  // _CXXTEST_OLD_TEMPLATE_SYNTAX

namespace CxxTest {
//
// This is how we use the value traits
//
#define TS_AS_STRING( x ) CxxTest::traits( x ).asString()

  //
  // Char representation of a digit
  //
  char digitToChar( unsigned digit );

  //
  // Convert byte value to hex digits
  // Returns pointer to internal buffer
  //
  const char* byteToHex( unsigned char byte );

  //
  // Convert byte values to string
  // Returns one past the copied data
  //
  char* bytesToString( const unsigned char* bytes,
                       unsigned numBytes,
                       unsigned maxBytes,
                       char* s );

  //
  // Copy a string.
  // Returns one past the end of the destination string
  // Remember -- we can't use the standard library!
  //
  char* copyString( char* dst, const char* src );

  //
  // Compare two strings.
  // Remember -- we can't use the standard library!
  //
  bool stringsEqual( const char* s1, const char* s2 );

  //
  // Represent a character value as a string
  // Returns one past the end of the string
  // This will be the actual char if printable or '\xXXXX' otherwise
  //
  char* charToString( unsigned long c, char* s );

  //
  // Prevent problems with negative (signed char)s
  //
  char* charToString( char c, char* s );

  //
  // The default ValueTraits class dumps up to 8 bytes as hex values
  //
  template <class T>
  class ValueTraits {
    enum { MAX_BYTES = 8 };
    char _asString[sizeof( "{ " ) + sizeof( "XX " ) * MAX_BYTES +
                   sizeof( "... }" )];

    public:
    ValueTraits( const T& t ) {
      bytesToString(
          (const unsigned char*)&t, sizeof( T ), MAX_BYTES, _asString );
    }
    const char* asString( void ) const { return _asString; }
  };

  //
  // traits( T t )
  // Creates an object of type ValueTraits<T>
  //
  template <class T>
  inline ValueTraits<T> traits( T t ) {
    return ValueTraits<T>( t );
  }

//
// You can duplicate the implementation of an existing ValueTraits
//
#define CXXTEST_COPY_TRAITS( CXXTEST_NEW_CLASS, CXXTEST_OLD_CLASS )     \
  CXXTEST_TEMPLATE_INSTANTIATION class ValueTraits<CXXTEST_NEW_CLASS> { \
    ValueTraits<CXXTEST_OLD_CLASS> _old;                                \
                                                                        \
    public:                                                             \
    ValueTraits( CXXTEST_NEW_CLASS n )                                  \
        : _old( (CXXTEST_OLD_CLASS)n ) {}                               \
    const char* asString( void ) const { return _old.asString(); }      \
  }

//
// Certain compilers need separate declarations for T and const T
//
#ifdef _CXXTEST_NO_COPY_CONST
#define CXXTEST_COPY_CONST_TRAITS( CXXTEST_CLASS )
#else  // !_CXXTEST_NO_COPY_CONST
#define CXXTEST_COPY_CONST_TRAITS( CXXTEST_CLASS ) \
  CXXTEST_COPY_TRAITS( CXXTEST_CLASS, const CXXTEST_CLASS )
#endif  // _CXXTEST_NO_COPY_CONST

  //
  // Avoid compiler warnings about unsigned types always >= 0
  //
  template <class N>
  inline bool negative( N n ) {
    return n < 0;
  }
  template <class N>
  inline N abs( N n ) {
    return negative( n ) ? -n : n;
  }

#define CXXTEST_NON_NEGATIVE( Type )                                   \
  CXXTEST_TEMPLATE_INSTANTIATION inline bool negative<Type>( Type ) {  \
    return false;                                                      \
  }                                                                    \
  CXXTEST_TEMPLATE_INSTANTIATION inline Type abs<Type>( Type value ) { \
    return value;                                                      \
  }

  CXXTEST_NON_NEGATIVE( bool )
  CXXTEST_NON_NEGATIVE( unsigned char )
  CXXTEST_NON_NEGATIVE( unsigned short int )
  CXXTEST_NON_NEGATIVE( unsigned int )
  CXXTEST_NON_NEGATIVE( unsigned long int )
#ifdef _CXXTEST_LONGLONG
  CXXTEST_NON_NEGATIVE( unsigned _CXXTEST_LONGLONG )
#endif  // _CXXTEST_LONGLONG

  //
  // Represent (integral) number as a string
  // Returns one past the end of the string
  // Remember -- we can't use the standard library!
  //
  template <class N>
  char* numberToString( N n,
                        char* s,
                        N base = 10,
                        unsigned skipDigits = 0,
                        unsigned maxDigits = (unsigned)-1 ) {
    if ( negative( n ) ) {
      *s++ = '-';
      n = abs( n );
    }

    N digit = 1;

    while ( digit <= ( n / base ) )
      digit *= base;

    N digitValue;

    for ( ; digit >= 1 && skipDigits;
          n -= digit * digitValue, digit /= base, --skipDigits )
      digitValue = (unsigned)( n / digit );

    for ( ; digit >= 1 && maxDigits;
          n -= digit * digitValue, digit /= base, --maxDigits )
      *s++ = digitToChar( (unsigned)( digitValue = (unsigned)( n / digit ) ) );

    *s = '\0';
    return s;
  }

//
// All the specific ValueTraits follow.
// You can #define CXXTEST_USER_VALUE_TRAITS if you don't want them
//

#ifndef CXXTEST_USER_VALUE_TRAITS
  //
  // ValueTraits: const char * const &
  // This is used for printing strings, as in TS_FAIL( "Message" )
  //
  CXXTEST_TEMPLATE_INSTANTIATION
  class ValueTraits<const char* const&> {
    ValueTraits& operator=( const ValueTraits& );
    const char* _asString;

    public:
    ValueTraits( const char* const& value )
        : _asString( value ) {}
    ValueTraits( const ValueTraits& other )
        : _asString( other._asString ) {}
    const char* asString( void ) const { return _asString; }
  };

  CXXTEST_COPY_TRAITS( const char*, const char* const& );
  CXXTEST_COPY_TRAITS( char*, const char* const& );

  //
  // ValueTraits: bool
  //
  CXXTEST_TEMPLATE_INSTANTIATION
  class ValueTraits<const bool> {
    bool _value;

    public:
    ValueTraits( const bool value )
        : _value( value ) {}
    const char* asString( void ) const { return _value ? "true" : "false"; }
  };

  CXXTEST_COPY_CONST_TRAITS( bool );

#ifdef _CXXTEST_LONGLONG
  //
  // ValueTraits: signed long long
  //
  CXXTEST_TEMPLATE_INSTANTIATION
  class ValueTraits<const signed _CXXTEST_LONGLONG> {
    typedef _CXXTEST_LONGLONG T;
    char _asString[2 + 3 * sizeof( T )];

    public:
    ValueTraits( T t ) { numberToString<T>( t, _asString ); }
    const char* asString( void ) const { return _asString; }
  };

  CXXTEST_COPY_CONST_TRAITS( signed _CXXTEST_LONGLONG );

  //
  // ValueTraits: unsigned long long
  //
  CXXTEST_TEMPLATE_INSTANTIATION
  class ValueTraits<const unsigned _CXXTEST_LONGLONG> {
    typedef unsigned _CXXTEST_LONGLONG T;
    char _asString[1 + 3 * sizeof( T )];

    public:
    ValueTraits( T t ) { numberToString<T>( t, _asString ); }
    const char* asString( void ) const { return _asString; }
  };

  CXXTEST_COPY_CONST_TRAITS( unsigned _CXXTEST_LONGLONG );
#endif  // _CXXTEST_LONGLONG

  //
  // ValueTraits: signed long
  //
  CXXTEST_TEMPLATE_INSTANTIATION
  class ValueTraits<const signed long int> {
    typedef signed long int T;
    char _asString[2 + 3 * sizeof( T )];

    public:
    ValueTraits( T t ) { numberToString<T>( t, _asString ); }
    const char* asString( void ) const { return _asString; }
  };

  CXXTEST_COPY_CONST_TRAITS( signed long int );

  //
  // ValueTraits: unsigned long
  //
  CXXTEST_TEMPLATE_INSTANTIATION
  class ValueTraits<const unsigned long int> {
    typedef unsigned long int T;
    char _asString[1 + 3 * sizeof( T )];

    public:
    ValueTraits( T t ) { numberToString<T>( t, _asString ); }
    const char* asString( void ) const { return _asString; }
  };

  CXXTEST_COPY_CONST_TRAITS( unsigned long int );

  //
  // All decimals are the same as the long version
  //

  CXXTEST_COPY_TRAITS( const signed int, const signed long int );
  CXXTEST_COPY_TRAITS( const unsigned int, const unsigned long int );
  CXXTEST_COPY_TRAITS( const signed short int, const signed long int );
  CXXTEST_COPY_TRAITS( const unsigned short int, const unsigned long int );
  CXXTEST_COPY_TRAITS( const unsigned char, const unsigned long int );

  CXXTEST_COPY_CONST_TRAITS( signed int );
  CXXTEST_COPY_CONST_TRAITS( unsigned int );
  CXXTEST_COPY_CONST_TRAITS( signed short int );
  CXXTEST_COPY_CONST_TRAITS( unsigned short int );
  CXXTEST_COPY_CONST_TRAITS( unsigned char );

  //
  // ValueTraits: char
  // Returns 'x' for printable chars, '\x??' for others
  //
  CXXTEST_TEMPLATE_INSTANTIATION
  class ValueTraits<const char> {
    char _asString[sizeof( "'\\xXX'" )];

    public:
    ValueTraits( char c ) {
      copyString( charToString( c, copyString( _asString, "'" ) ), "'" );
    }
    const char* asString( void ) const { return _asString; }
  };

  CXXTEST_COPY_CONST_TRAITS( char );

  //
  // ValueTraits: signed char
  // Same as char, some compilers need it
  //
  CXXTEST_COPY_TRAITS( const signed char, const char );
  CXXTEST_COPY_CONST_TRAITS( signed char );

  //
  // ValueTraits: double
  //
  CXXTEST_TEMPLATE_INSTANTIATION
  class ValueTraits<const double> {
    public:
    ValueTraits( double t ) {
      ( requiredDigitsOnLeft( t ) > MAX_DIGITS_ON_LEFT ) ? hugeNumber( t )
                                                         : normalNumber( t );
    }

    const char* asString( void ) const { return _asString; }

    private:
    enum { MAX_DIGITS_ON_LEFT = 24, DIGITS_ON_RIGHT = 4, BASE = 10 };
    char _asString[1 + MAX_DIGITS_ON_LEFT + 1 + DIGITS_ON_RIGHT + 1];

    static unsigned requiredDigitsOnLeft( double t );
    char* doNegative( double& t );
    void hugeNumber( double t );
    void normalNumber( double t );
    char* doubleToString( double t,
                          char* s,
                          unsigned skip = 0,
                          unsigned max = (unsigned)-1 );
  };

  CXXTEST_COPY_CONST_TRAITS( double );

  //
  // ValueTraits: float
  //
  CXXTEST_COPY_TRAITS( const float, const double );
  CXXTEST_COPY_CONST_TRAITS( float );
#endif  // !CXXTEST_USER_VALUE_TRAITS
}

#ifdef _CXXTEST_HAVE_STD
#include <cxxtest/StdValueTraits.h>
#endif  // _CXXTEST_HAVE_STD

//
// CXXTEST_ENUM_TRAITS
//
#define CXXTEST_ENUM_TRAITS( TYPE, VALUES )                         \
  namespace CxxTest {                                               \
    CXXTEST_TEMPLATE_INSTANTIATION class ValueTraits<TYPE> {        \
      TYPE _value;                                                  \
      char _fallback[sizeof( "(" #TYPE ")" ) + 3 * sizeof( TYPE )]; \
                                                                    \
      public:                                                       \
      ValueTraits( TYPE value ) {                                   \
        _value = value;                                             \
        numberToString<unsigned long int>(                          \
            _value, copyString( _fallback, "(" #TYPE ")" ) );       \
      }                                                             \
      const char* asString( void ) const {                          \
        switch ( _value ) { VALUES default : return _fallback; }    \
      }                                                             \
    };                                                              \
  }

#define CXXTEST_ENUM_MEMBER( MEMBER ) \
  case MEMBER:                        \
    return #MEMBER;

#endif  // __cxxtest__ValueTraits_h__
