/*
 *  Copyright (C) 2009-2018 Intel Corporation.  All Rights Reserved.
 *
 *  The source code contained or described herein and all documents related
 *  to the source code ("Material") are owned by Intel Corporation or its
 *  suppliers or licensors.  Title to the Material remains with Intel
 *  Corporation or its suppliers and licensors.  The Material is protected
 *  by worldwide copyright laws and treaty provisions.  No part of the
 *  Material may be used, copied, reproduced, modified, published, uploaded,
 *  posted, transmitted, distributed, or disclosed in any way without
 *  Intel's prior express written permission.
 *
 *  No license under any patent, copyright, trade secret or other
 *  intellectual property right is granted to or conferred upon you by
 *  disclosure or delivery of the Materials, either expressly, by
 *  implication, inducement, estoppel or otherwise.  Any license under such
 *  intellectual property rights must be express and approved by Intel in
 *  writing.
 *
*/

#ifndef __USE_INTEL_ATOMICS
  #if _MSC_VER > 0 && _MSC_VER < 1700
    #define __USE_INTEL_ATOMICS 1
  #endif
  #if __GNUC__ > 0 && (__GNUC__ < 4 || __GNUC__ == 4 && __GNUC_MINOR__ < 5) && \
        !defined(__INTEL_CLANG_COMPILER)
    #define __USE_INTEL_ATOMICS 1
  #endif
  #if defined(__VXWORKS__)
    #define __USE_INTEL_ATOMICS 1
  #endif


#if defined(__MACH__) && (__clang_major__ >= 9)
// next clause only works in clang mode
#if  __has_extension(c_atomic)
#define __INTEL_USE_CLANG_ATOMICS 1
#endif // c_atomic
#endif // MACH && clang

#if defined(__ANDROID__) && defined(__clang_major__)
#define __INTEL_USE_CLANG_ATOMICS 1
#endif

#if __INTEL_USE_CLANG_ATOMICS && !defined(__INTEL_CLANG_ATOMIC_DEFS)
#define __INTEL_CLANG_ATOMIC_DEFS 1
/* Define these clang-style functions using C11. */

#if __cplusplus >= 201103L
#include <type_traits>
#endif // C++11

template<typename _Ty>
static inline void __c11_atomic_init(_Atomic(_Ty) *dst, _Ty val) {
    __atomic_store(
        (_Ty *)dst,
        &val,
        __ATOMIC_RELAXED);
}

static inline void __c11_atomic_thread_fence(int order) {
    __atomic_thread_fence(order);
}

static inline void __c11_atomic_signal_fence(int order) {
    __atomic_signal_fence(order);
}

template<typename _Ty>
static inline _Ty __c11_atomic_load(_Atomic(_Ty) *obj, int order) {
  _Ty tmp;
  __atomic_load((_Ty *)obj, &tmp, order);
  return tmp;
}

template<typename _Ty>
static inline _Ty __c11_atomic_load(volatile _Atomic(_Ty) *obj, int order) {
  _Ty tmp;
  __atomic_load((_Ty *)obj, &tmp, order);
  return tmp;
}

template<typename _Ty>
static inline void __c11_atomic_store(_Atomic(_Ty) *dst, _Ty src, int order) {
    __atomic_store((_Ty *)dst, &src, order);
}

template<typename _Ty>
static inline void __c11_atomic_store(volatile _Atomic(_Ty) *dst, _Ty src, int order) {
    __atomic_store((_Ty *)dst, &src, order);
}

template <typename _Ty>
struct _ptrscale {
    static constexpr size_t val = 1;
};

template <typename _Ty>
struct _ptrscale<_Ty*> {
    static constexpr size_t val = sizeof(_Ty);
};

template<typename _Ty, typename _Ty2>
static inline _Atomic(_Ty) __c11_atomic_fetch_add(
    _Atomic(_Ty) *dst,
    _Ty2 val,
    int order)
{
    return __atomic_fetch_add_explicit((_Ty *)dst,
                                       val * _ptrscale<_Ty>::val,
                                       order);
}

template<typename _Ty, typename _Ty2>
static inline _Atomic(_Ty) __c11_atomic_fetch_add(
    volatile _Atomic(_Ty) *dst,
    _Ty2 val,
    int order)
{
    return __atomic_fetch_add_explicit((_Ty *)dst,
                                       val * _ptrscale<_Ty>::val,
                                       order);
}

template<typename _Ty, typename _Ty2>
static inline _Atomic(_Ty) __c11_atomic_fetch_sub(
    _Atomic(_Ty) *dst,
    _Ty2 val,
    int order)
{
    return __atomic_fetch_sub_explicit((_Ty *)dst,
                                       val * _ptrscale<_Ty>::val,
                                       order);
}

template<typename _Ty, typename _Ty2>
static inline _Atomic(_Ty) __c11_atomic_fetch_sub(
    volatile _Atomic(_Ty) *dst,
    _Ty2 val,
    int order)
{
    return __atomic_fetch_sub_explicit((_Ty *)dst,
                                       val * _ptrscale<_Ty>::val,
                                       order);
}

template<typename _Ty, typename _Ty2>
static inline _Atomic(_Ty) __c11_atomic_fetch_or(
    _Atomic(_Ty) *dst,
    _Ty2 val,
    int order)
{
    return __atomic_fetch_or_explicit((_Ty *)dst, val, order);
}

template<typename _Ty, typename _Ty2>
static inline _Atomic(_Ty) __c11_atomic_fetch_or(
    volatile _Atomic(_Ty) *dst,
    _Ty2 val,
    int order)
{
    return __atomic_fetch_or_explicit((_Ty *)dst, val, order);
}

template<typename _Ty, typename _Ty2>
static inline _Atomic(_Ty) __c11_atomic_fetch_xor(
    _Atomic(_Ty) *dst,
    _Ty2 val,
    int order)
{
    return __atomic_fetch_xor_explicit((_Ty *)dst, val, order);
}


template<typename _Ty, typename _Ty2>
static inline _Atomic(_Ty) __c11_atomic_fetch_xor(
    volatile _Atomic(_Ty) *dst,
    _Ty2 val,
    int order)
{
    return __atomic_fetch_xor_explicit((_Ty *)dst, val, order);
}

template<typename _Ty, typename _Ty2>
static inline _Atomic(_Ty) __c11_atomic_fetch_and(
    _Atomic(_Ty) *dst,
    _Ty2 val,
    int order)
{
    return __atomic_fetch_and_explicit((_Ty *)dst, val, order);
}

template<typename _Ty, typename _Ty2>
static inline _Atomic(_Ty) __c11_atomic_fetch_and(
    volatile _Atomic(_Ty) *dst,
    _Ty2 val,
    int order)
{
    return __atomic_fetch_and_explicit((_Ty *)dst, val, order);
}

template<typename _Ty>
static inline _Atomic(_Ty) __c11_atomic_exchange(_Atomic(_Ty) *obj, _Ty desr, int order) {
    _Ty tmp;
    __atomic_exchange((_Ty *)obj, &desr, &tmp, order);
    return tmp;
}

template<typename _Ty, typename _Ty2>
static inline _Atomic(_Ty) __c11_atomic_exchange(volatile _Atomic(_Ty) *obj, _Ty2 desr, int order) {
    _Ty tmp;
    __atomic_exchange((_Ty *)obj, &desr, &tmp, order);
    return tmp;
}

template<typename _Ty, typename _Ty2, typename _Ty3>
static inline bool __c11_atomic_compare_exchange_weak(
    _Atomic(_Ty) *obj,
    _Ty2 *exp,
    _Ty3 val,
    int order_s,
    int order_f)
{
    return __atomic_compare_exchange((_Ty *)obj, exp, &val, true, order_s, order_f);
}

template<typename _Ty, typename _Ty2, typename _Ty3>
static inline bool __c11_atomic_compare_exchange_weak(
    volatile _Atomic(_Ty) *obj,
    _Ty2 *exp,
    _Ty3 val,
    int order_s,
    int order_f)
{
    return __atomic_compare_exchange((_Ty *)obj, exp, &val, true, order_s, order_f);
}

template<typename _Ty, typename _Ty2, typename _Ty3>
static inline bool __c11_atomic_compare_exchange_strong(
    _Atomic(_Ty) *obj,
    _Ty2 *exp,
    _Ty3 val,
    int order_s,
    int order_f)
{
    return (bool)__atomic_compare_exchange((_Ty *)obj, exp, &val, false, order_s, order_f);
}

template<typename _Ty, typename _Ty2, typename _Ty3>
static inline bool __c11_atomic_compare_exchange_strong(
    volatile _Atomic(_Ty) *obj,
    _Ty2 *exp,
    _Ty3 val,
    int order_s,
    int order_f)
{
    return (bool)__atomic_compare_exchange((_Ty *)obj, exp, &val, false, order_s, order_f);
}

static inline int __c11_atomic_is_lock_free(size_t __size) {
    return 1;
}

#endif /* __INTEL_USE_CLANG_ATOMICS && !__INTEL_CLANG_ATOMIC_DEFS */

#endif /* !__USE_INTEL_ATOMICS */

#if __USE_INTEL_ATOMICS
  #include <stdatomic.h>
#else
  #include_next <atomic>
#endif
