// File: crn_traits.h
// See Copyright Notice and license at the end of inc/crnlib.h
#pragma once

namespace crnlib {
template <typename T>
struct int_traits {
  enum { cMin = crnlib::cINT32_MIN,
         cMax = crnlib::cINT32_MAX,
         cSigned = true };
};

template <>
struct int_traits<int8> {
  enum { cMin = crnlib::cINT8_MIN,
         cMax = crnlib::cINT8_MAX,
         cSigned = true };
};
template <>
struct int_traits<int16> {
  enum { cMin = crnlib::cINT16_MIN,
         cMax = crnlib::cINT16_MAX,
         cSigned = true };
};
template <>
struct int_traits<int32> {
  enum { cMin = crnlib::cINT32_MIN,
         cMax = crnlib::cINT32_MAX,
         cSigned = true };
};

template <>
struct int_traits<uint8> {
  enum { cMin = 0,
         cMax = crnlib::cUINT8_MAX,
         cSigned = false };
};
template <>
struct int_traits<uint16> {
  enum { cMin = 0,
         cMax = crnlib::cUINT16_MAX,
         cSigned = false };
};
template <>
struct int_traits<uint32> {
  enum { cMin = 0,
         cMax = crnlib::cUINT32_MAX,
         cSigned = false };
};
template <typename T>
struct scalar_type {
  enum { cFlag = false };
  static inline void construct(T* p) { helpers::construct(p); }
  static inline void construct(T* p, const T& init) { helpers::construct(p, init); }
  static inline void construct_array(T* p, uint n) { helpers::construct_array(p, n); }
  static inline void destruct(T* p) { helpers::destruct(p); }
  static inline void destruct_array(T* p, uint n) { helpers::destruct_array(p, n); }
};

template <typename T>
struct scalar_type<T*> {
  enum { cFlag = true };
  static inline void construct(T** p) { memset(p, 0, sizeof(T*)); }
  static inline void construct(T** p, T* init) { *p = init; }
  static inline void construct_array(T** p, uint n) { memset(p, 0, sizeof(T*) * n); }
  static inline void destruct(T**) {}
  static inline void destruct_array(T**, uint) {}
};

#define CRNLIB_DEFINE_BUILT_IN_TYPE(X)                                                 \
  template <>                                                                          \
  struct scalar_type<X> {                                                              \
    enum { cFlag = true };                                                             \
    static inline void construct(X* p) { memset(p, 0, sizeof(X)); }                    \
    static inline void construct(X* p, const X& init) { memcpy(p, &init, sizeof(X)); } \
    static inline void construct_array(X* p, uint n) { memset(p, 0, sizeof(X) * n); }  \
    static inline void destruct(X*) {}                                                 \
    static inline void destruct_array(X*, uint) {}                                     \
  };

CRNLIB_DEFINE_BUILT_IN_TYPE(bool)
CRNLIB_DEFINE_BUILT_IN_TYPE(char)
CRNLIB_DEFINE_BUILT_IN_TYPE(unsigned char)
CRNLIB_DEFINE_BUILT_IN_TYPE(short)
CRNLIB_DEFINE_BUILT_IN_TYPE(unsigned short)
CRNLIB_DEFINE_BUILT_IN_TYPE(int)
CRNLIB_DEFINE_BUILT_IN_TYPE(unsigned int)
CRNLIB_DEFINE_BUILT_IN_TYPE(long)
CRNLIB_DEFINE_BUILT_IN_TYPE(unsigned long)
#ifdef __GNUC__
CRNLIB_DEFINE_BUILT_IN_TYPE(long long)
CRNLIB_DEFINE_BUILT_IN_TYPE(unsigned long long)
#else
CRNLIB_DEFINE_BUILT_IN_TYPE(__int64)
CRNLIB_DEFINE_BUILT_IN_TYPE(unsigned __int64)
#endif
CRNLIB_DEFINE_BUILT_IN_TYPE(float)
CRNLIB_DEFINE_BUILT_IN_TYPE(double)
CRNLIB_DEFINE_BUILT_IN_TYPE(long double)

#undef CRNLIB_DEFINE_BUILT_IN_TYPE

// See: http://erdani.org/publications/cuj-2004-06.pdf

template <typename T>
struct bitwise_movable {
  enum { cFlag = false };
};

// Defines type Q as bitwise movable.
// Bitwise movable: type T may be safely moved to a new location via memcpy, without requiring the old copy to be destructed.
// However, the final version of the object (wherever it winds up in memory) must be eventually destructed (a single time, of course).
// Bitwise movable is a superset of bitwise copyable (all bitwise copyable types are also bitwise movable).
#define CRNLIB_DEFINE_BITWISE_MOVABLE(Q) \
  template <>                            \
  struct bitwise_movable<Q> {            \
    enum { cFlag = true };               \
  };

template <typename T>
struct bitwise_copyable {
  enum { cFlag = false };
};

// Defines type Q as bitwise copyable.
// Bitwise copyable: type T may be safely and freely copied (duplicated) via memcpy, and *does not* require destruction.
#define CRNLIB_DEFINE_BITWISE_COPYABLE(Q) \
  template <>                             \
  struct bitwise_copyable<Q> {            \
    enum { cFlag = true };                \
  };

#define CRNLIB_IS_POD(T) __is_pod(T)

#define CRNLIB_IS_SCALAR_TYPE(T) (scalar_type<T>::cFlag)

#define CRNLIB_IS_BITWISE_COPYABLE(T) (CRNLIB_IS_SCALAR_TYPE(T) || CRNLIB_IS_POD(T) || (bitwise_copyable<T>::cFlag))

#define CRNLIB_IS_BITWISE_COPYABLE_OR_MOVABLE(T) (CRNLIB_IS_BITWISE_COPYABLE(T) || (bitwise_movable<T>::cFlag))

#define CRNLIB_HAS_DESTRUCTOR(T) ((!scalar_type<T>::cFlag) && (!__is_pod(T)))

// From yasli_traits.h:
// Credit goes to Boost;
// also found in the C++ Templates book by Vandevoorde and Josuttis

typedef char (&yes_t)[1];
typedef char (&no_t)[2];

template <class U>
yes_t class_test(int U::*);
template <class U>
no_t class_test(...);

template <class T>
struct is_class {
  enum { value = (sizeof(class_test<T>(0)) == sizeof(yes_t)) };
};

template <typename T>
struct is_pointer {
  enum { value = false };
};

template <typename T>
struct is_pointer<T*> {
  enum { value = true };
};

CRNLIB_DEFINE_BITWISE_COPYABLE(empty_type);
CRNLIB_DEFINE_BITWISE_MOVABLE(empty_type);

}  // namespace crnlib
