/*****************************************************************************
 * $LastChangedDate: 2009-12-18 16:29:35 -0500 (Fri, 18 Dec 2009) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Basic types.
 *//*
 * LEGAL:   COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#ifndef BASE_TYPES_HH
#define BASE_TYPES_HH 1

// Basic types must be fast, prevent them having virtual functions.
#define virtual VIRTUAL_METHODS_IN_BASIC_TYPES_IS_TOO_SLOW

////////////////////////////////////////////////////////////////////////////////
// Basic types.
//
#if ! BASE_OMIT_BOOL
typedef unsigned char      Bool;  ///< byte-sized bool
#endif
typedef unsigned char      uchar;
#ifdef OS_WINDOWS
#undef uint
#define uint unsigned int
#else
typedef unsigned int       uint;
#endif
#undef ulong
typedef unsigned long      ulong;
typedef float              fp;
#if SPEED
typedef fp                 fpx;  // extended-precision float
#else
typedef double             fpx;
#endif

#define UINT_CAST( U ) (static_cast<unsigned int>((U)))
#define ULONG_CAST( U ) (static_cast<unsigned long>((U)))

//------------------------------------------------------------------------------

namespace base {

////////////////////////////////////////////////////////////////////////////////
// Exact-size types (limited use).
// int64() cast or "LL" suffix is critical to compile 64-bit shifts correctly.
#if ! NO_STDINT
typedef int8_t             int8;
typedef uint8_t            uint8;
typedef int16_t            int16;
typedef uint16_t           uint16;
typedef int32_t            int32;
typedef uint32_t           uint32;
typedef int64_t            int64;   // C99
typedef uint64_t           uint64;  // C99
#else
typedef char               int8;
typedef unsigned char      uint8;
typedef short              int16;
typedef unsigned short     uint16;
typedef int                int32;
typedef unsigned int       uint32;
typedef long long          int64;   // C99
typedef unsigned long long uint64;  // C99
#endif

////////////////////////////////////////////////////////////////////////////////
// Limited use: may result in unportable code.
// FYI, on Linux x86-64, int is 32-bits, long is 64-bits.
//
const uint INT_BITS     = sizeof(int)   * 8;
const uint LONG_BITS    = sizeof(long)  * 8;
const uint POINTER_BITS = sizeof(void*) * 8;

/// @class base::Void
/// @brief Void that can be instantiated (useful with templates).
class Void { };

////////////////////////////////////////////////////////////////////////////////
/// @brief Macro to turn a fundamental type into a distinct class.
/// @verbatim
/// For example:
/// DECLARE_DISTINCT_TYPE( Radian, fp )
/// DECLARE_DISTINCT_TYPE( Degree, fp )
/// BTW, this can't done using a template class and typedefs
/// as the typedefs would still be the same type eg:
/// typedef Distinct<fp> Radian;
/// typedef Distinct<fp> Degree;
/// @endverbatim
///
/// ----------------------------------------------------------------------------
/// NOTE: DECLARE_DISTINCT_TYPE() MIGHT CAUSE BUGS IF NOT CAREFUL!
/// !!!!! DECLARE_DISTINCT_TYPE() allows implied conversion to fundamental type!
///       Instead use DECLARE_DISTINCT_TYPE_STRICT() to force explicit conversion.
/// ----------------------------------------------------------------------------
#define DECLARE_DISTINCT_TYPE( CLASS, T, CONV_FUNC )                                        \
class CLASS                                                                                 \
{                                                                                           \
public:                                                                                     \
    typedef T value_type;                                                                   \
                CLASS( void ) : mVal(0) { }  /* clients depends on zero */                  \
                CLASS( T val ) : mVal(val) { }                                              \
    T           CONV_FUNC( void ) const { return mVal; }  /* explicit conversion */         \
                operator T() const { return mVal; }                                         \
    CLASS&      operator=( const T& val ) { mVal = val; return *this; }                     \
    friend bool operator==( const CLASS a, const CLASS b ) { return a.mVal == b.mVal; }     \
    friend bool operator!=( const CLASS a, const CLASS b ) { return a.mVal != b.mVal; }     \
    /* Arithmetic operators are omitted because of ambiguity. */                            \
    /* Rather, cast to fundamental type then do the math. */                                \
private:                                                                                    \
    T   mVal;                                                                               \
};

// Strict variant requires explicit conversion.
// This one is tedious but it can prevent conversion bugs.
// Example:
// DECLARE_DISTINCT_TYPE_STRICT( Meter, fp, FP )
// void Func( Meter meter )
// {
//   fp distance = ...;
//   fp sum = distance + meter.FP();  // explicitly convert to fp
// }
#define DECLARE_DISTINCT_TYPE_STRICT( CLASS, T, CONV_FUNC )                                 \
class CLASS                                                                                 \
{                                                                                           \
public:                                                                                     \
    typedef T value_type;                                                                   \
                 CLASS( void ) : mVal(0) { }  /* clients depends on zero */                 \
    explicit     CLASS( T val ) : mVal(val) { }  /* explicit conversion */                  \
              /* operator T() const { return mVal; } [wrong] */                             \
    T            CONV_FUNC( void ) const { return mVal; }  /* explicit conversion */        \
  /*CLASS&       operator=( const T& val ) { mVal = val; return *this; } [circumvents] */   \
    CLASS&       operator=(  const CLASS src ) { mVal =  src.mVal; return *this; }          \
    CLASS&       operator+=( const CLASS src ) { mVal += src.mVal; return *this; }          \
    CLASS&       operator-=( const CLASS src ) { mVal -= src.mVal; return *this; }          \
    CLASS&       operator*=( const CLASS src ) { mVal *= src.mVal; return *this; }          \
    CLASS&       operator/=( const CLASS src ) { mVal /= src.mVal; return *this; }          \
    friend CLASS operator+( CLASS a, CLASS b ) { return CLASS(a.mVal + b.mVal); }           \
    friend CLASS operator-( CLASS a, CLASS b ) { return CLASS(a.mVal - b.mVal); }           \
    friend CLASS operator*( CLASS a, CLASS b ) { return CLASS(a.mVal * b.mVal); }           \
    friend CLASS operator/( CLASS a, CLASS b ) { return CLASS(a.mVal / b.mVal); }           \
    friend CLASS operator-( CLASS a )          { return CLASS(-a.mVal); }                   \
    friend bool  operator==( const CLASS a, const CLASS b ) { return a.mVal == b.mVal; }    \
    friend bool  operator!=( const CLASS a, const CLASS b ) { return a.mVal != b.mVal; }    \
    friend bool  operator>( CLASS a, CLASS b ) { return a.mVal > b.mVal; }                  \
    friend bool  operator<( CLASS a, CLASS b ) { return a.mVal < b.mVal; }                  \
    friend bool  operator>=( CLASS a, CLASS b ) { return a.mVal >= b.mVal; }                \
    friend bool  operator<=( CLASS a, CLASS b ) { return a.mVal <= b.mVal; }                \
private:                                                                                    \
    T   mVal;                                                                               \
};

/// @class base::bool0
/// @brief Fundamental type auto-initialized as zero.
/// @class base::int0
/// @brief Fundamental type auto-initialized as zero.
/// @class base::uint0
/// @brief Fundamental type auto-initialized as zero.
DECLARE_DISTINCT_TYPE( bool0, bool, CONV_FUNC_UNUSED )
DECLARE_DISTINCT_TYPE( int0,  int , CONV_FUNC_UNUSED )
DECLARE_DISTINCT_TYPE( uint0, uint, CONV_FUNC_UNUSED )

#undef virtual

} // namespace base

#endif // BASE_TYPES_HH
