// vim: set sts=8 ts=2 sw=2 tw=99 et:
//
// Copyright (C) 2014, David Anderson and AlliedModders LLC
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 
//  * Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//  * Neither the name of AlliedModders LLC nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#ifndef _include_amtl_cxx_support_h_
#define _include_amtl_cxx_support_h_

#if defined(__clang__)
# if !(defined(__clang_major__) && defined(__clang_minor__))
#  define KE_CLANG_MAJOR 1
#  define KE_CLANG_MINOR __GNUC_MINOR__
# else
#  if defined(__apple_build_version__) && clang_major__ > 3
    // 4.0 => 3.1, 4.1 => 3.2
#   if __clang_major__ == 4
#    define KE_CLANG_MAJOR 3
#    if __clang_minor__ == 0
#     define KE_CLANG_MINOR 1
#    else
#     define KE_CLANG_MINOR 2
#    endif
    // 5.0 => 3.3, 5.1 => 3.4
#   elif __clang_major__ == 5
#    define KE_CLANG_MAJOR 3
#    if __clang_minor__ == 0
#     define KE_CLANG_MINOR 3
#    else
#     define KE_CLANG_MINOR 4
#    endif
#   elif __clang_major__ == 6
#    define KE_CLANG_MAJOR 3
#    define KE_CLANG_MINOR 5
#   endif
#  endif
#  if !defined(KE_CLANG_MAJOR)
#   define KE_CLANG_MAJOR __clang_major__
#  endif
#  if !defined(KE_CLANG_MINOR)
#   define KE_CLANG_MINOR __clang_minor__
#  endif
# endif

// Done with horrible clang version detection.
# define KE_CLANG_AT_LEAST(x, y) \
   ((__clang_major__ > (x)) || (__clang_major__ == x && __clang_minor__ >= y))

# if KE_CLANG_AT_LEAST(2, 9)
#  define KE_CXX_HAS_RVAL_REFS 30
#  define KE_CXX_HAS_DELETE
#  define KE_CXX_HAS_STATIC_ASSERT
#  define KE_CXX_HAS_DOUBLE_GT
#  define KE_CXX_HAS_ENUM_CLASS
# endif
# if KE_CLANG_AT_LEAST(3, 0)
#  define KE_CXX_HAS_OVERRIDE
#  define KE_CXX_HAS_EXPLICIT_BOOL
#  define KE_CXX_HAS_NULLPTR
#  define KE_CXX_HAS_NOEXCEPT
# endif
# if KE_CLANG_AT_LEAST(3, 1)
#  define KE_CXX_HAS_CONSTEXPR
# endif

#elif defined(__GNUC__)
# define KE_GCC_AT_LEAST(x, y) ((__GNUC__ > (x)) || (__GNUC__ == x && __GNUC_MINOR__ >= y))

# if KE_GCC_AT_LEAST(4, 3)
#  define KE_CXX_HAS_RVAL_REFS 10
#  define KE_CXX_HAS_STATIC_ASSERT
#  define KE_CXX_HAS_DOUBLE_GT
# endif
# if KE_GCC_AT_LEAST(4, 4)
#  define KE_CXX_HAS_DELETE
#  define KE_CXX_HAS_ENUM_CLASS
# endif
# if KE_GCC_AT_LEAST(4, 5)
#  define KE_CXX_HAS_EXPLICIT_BOOL
#  undef KE_CXX_HAS_RVAL_REFS
#  define KE_CXX_HAS_RVAL_REFS 21
# endif
# if KE_GCC_AT_LEAST(4, 6)
#  define KE_CXX_HAS_NULLPTR
#  define KE_CXX_HAS_NOEXCEPT
#  define KE_CXX_HAS_CONSTEXPR
#  undef KE_CXX_HAS_RVAL_REFS
#  define KE_CXX_HAS_RVAL_REFS 30
# endif
# if KE_GCC_AT_LEAST(4, 7)
#  define KE_CXX_HAS_OVERRIDE
# endif

#elif defined(_MSC_VER)
# if _MSC_VER >= 1600
#  define KE_CXX_HAS_RVAL_REFS 20
#  define KE_CXX_HAS_STATIC_ASSERT
#  define KE_CXX_HAS_DOUBLE_GT
#  define KE_CXX_HAS_NULLPTR
# endif
# if _MSC_VER >= 1700
#  undef KE_CXX_HAS_RVAL_REFS
#  define KE_CXX_HAS_RVAL_REFS 21
#  define KE_CXX_HAS_OVERRIDE
#  define KE_CXX_HAS_ENUM_CLASS
# endif
# if _MSC_VER >= 1800
#  define KE_CXX_HAS_DELETE
#  define KE_CXX_HAS_EXPLICIT_BOOL
# endif
# if _MSC_VER == 1800 && _MSC_FULL_VER == 180021114
#  define KE_CXX_HAS_CONSTEXPR
# endif
#else
# error Unrecognized compiler.
#endif

// Done with compiler feature detection.

#if defined(KE_CXX_HAS_OVERRIDE)
# define KE_OVERRIDE override
#else
# define KE_OVERRIDE
#endif

#if defined(KE_CXX_HAS_DELETE)
# define KE_DELETE = delete
#else
# define KE_DELETE
#endif

#if defined(KE_CXX_HAS_NOEXCEPT)
# define KE_NOEXCEPT noexcept
#else
# define KE_NOEXCEPT
#endif

#if defined(KE_CXX_HAS_CONSTEXPR)
# define KE_CONSTEXPR constexpr
#else
# define KE_CONSTEXPR
#endif

#if defined(KE_CXX_HAS_STATIC_ASSERT)
# define KE_STATIC_ASSERT(cond) static_assert(cond, #cond)
#else
# define KE_STATIC_ASSERT(cond) extern int static_assert_f(int a[(cond) ? 1 : -1])
#endif

#if !defined(KE_CXX_HAS_RVAL_REFS) || KE_CXX_HAS_RVAL_REFS < 21
//# error AMTL requires rvalue reference 2.1 support (N2844+)
#endif
#if !defined(KE_CXX_HAS_DOUBLE_GT)
# error AMTL requires support for >> in template names
#endif
#if !defined(KE_CXX_HAS_NULLPTR)
# if defined(__GNUC__) && !defined(__clang__)
#  define nullptr __null
#  define KE_CXX_HAS_NULLPTR
# else
#  error AMTL requires nullptr support
# endif
#endif

#define KE_DEFINE_ENUM_OPERATORS(EnumName)                                          \
  static inline EnumName operator |(const EnumName &left, const EnumName &right) {  \
    return EnumName(uint32_t(left) | uint32_t(right));                              \
  }                                                                                 \
  static inline EnumName operator &(const EnumName &left, const EnumName &right) {  \
    return EnumName(uint32_t(left) & uint32_t(right));                              \
  }                                                                                 \
  static inline EnumName operator ^(const EnumName &left, const EnumName &right) {  \
    return EnumName(uint32_t(left) ^ uint32_t(right));                              \
  }                                                                                 \
  static inline EnumName operator ~(const EnumName &flags) {                        \
    return EnumName(~uint32_t(flags));                                              \
  }                                                                                 \
  static inline EnumName & operator |=(EnumName &left, const EnumName &right) {     \
    return left = left | right;                                                     \
  }                                                                                 \
  static inline EnumName & operator &=(EnumName &left, const EnumName &right) {     \
    return left = left & right;                                                     \
  }                                                                                 \
  static inline EnumName & operator ^=(EnumName &left, const EnumName &right) {     \
    return left = left ^ right;                                                     \
  }                                                                                 \
  static inline bool operator !(const EnumName &obj) {                              \
    return uint32_t(obj) == 0;                                                      \
  }

#endif // _include_amtl_cxx_support_h_