#ifndef KIT_ATOMIC_H
#define KIT_ATOMIC_H

#include "types.h"

#ifndef _MSC_VER
#  include <stdatomic.h>
#else
#  include <assert.h>

#  ifdef __cplusplus
extern "C" {
#  endif

#  define _Atomic volatile

enum {
  memory_order_relaxed,
  memory_order_consume,
  memory_order_acquire,
  memory_order_release,
  memory_order_acq_rel,
  memory_order_seq_cst
};

void kit_atomic_store_explicit_8(u8 volatile *var, u8 value,
                                 i32 memory_order);

void kit_atomic_store_explicit_16(u16 volatile *var, u16 value,
                                  i32 memory_order);

void kit_atomic_store_explicit_32(u32 volatile *var, u32 value,
                                  i32 memory_order);

void kit_atomic_store_explicit_64(u64 volatile *var, u64 value,
                                  i32 memory_order);

u8 kit_atomic_load_explicit_8(u8 volatile *var, i32 memory_order);

u16 kit_atomic_load_explicit_16(u16 volatile *var, i32 memory_order);

u32 kit_atomic_load_explicit_32(u32 volatile *var, i32 memory_order);

u64 kit_atomic_load_explicit_64(u64 volatile *var, i32 memory_order);

u8 kit_atomic_exchange_explicit_8(u8 volatile *var, u8 value,
                                  i32 memory_order);

u16 kit_atomic_exchange_explicit_16(u16 volatile *var, u16 value,
                                    i32 memory_order);

u32 kit_atomic_exchange_explicit_32(u32 volatile *var, u32 value,
                                    i32 memory_order);

u64 kit_atomic_exchange_explicit_64(u64 volatile *var, u64 value,
                                    i32 memory_order);

i32 kit_atomic_compare_exchange_explicit_8(u8 volatile *var,
                                           u8 *expected, u8 value,
                                           i32 memory_order_succ_,
                                           i32 memory_order_fail_);

i32 kit_atomic_compare_exchange_explicit_16(u16 volatile *var,
                                            u16 *expected, u16 value,
                                            i32 memory_order_succ_,
                                            i32 memory_order_fail_);

i32 kit_atomic_compare_exchange_explicit_32(u32 volatile *var,
                                            u32 *expected, u32 value,
                                            i32 memory_order_succ_,
                                            i32 memory_order_fail_);

i32 kit_atomic_compare_exchange_explicit_64(u64 volatile *var,
                                            u64 *expected, u64 value,
                                            i32 memory_order_succ_,
                                            i32 memory_order_fail_);

u8 kit_atomic_fetch_add_explicit_8(u8 volatile *var, u8 value,
                                   i32 memory_order);

u16 kit_atomic_fetch_add_explicit_16(u16 volatile *var, u16 value,
                                     i32 memory_order);

u32 kit_atomic_fetch_add_explicit_32(u32 volatile *var, u32 value,
                                     i32 memory_order);

u64 kit_atomic_fetch_add_explicit_64(u64 volatile *var, u64 value,
                                     i32 memory_order);

#  define atomic_store_explicit(var_, value_, memory_order_)         \
    do {                                                             \
      assert(sizeof *(var_) == 1 || sizeof *(var_) == 2 ||           \
             sizeof *(var_) == 4 || sizeof *(var_) == 8);            \
      if (sizeof *(var_) == 1)                                       \
        kit_atomic_store_explicit_8((u8 volatile *) (var_),          \
                                    (u8) (value_), (memory_order_)); \
      if (sizeof *(var_) == 2)                                       \
        kit_atomic_store_explicit_16((u16 volatile *) (var_),        \
                                     (u16) (value_),                 \
                                     (memory_order_));               \
      if (sizeof *(var_) == 4)                                       \
        kit_atomic_store_explicit_32((u32 volatile *) (var_),        \
                                     (u32) (value_),                 \
                                     (memory_order_));               \
      if (sizeof *(var_) == 8)                                       \
        kit_atomic_store_explicit_64((u64 volatile *) (var_),        \
                                     (u64) (value_),                 \
                                     (memory_order_));               \
    } while (0)

#  define atomic_load_explicit(var_, memory_order_)              \
    (assert(sizeof *(var_) == 1 || sizeof *(var_) == 2 ||        \
            sizeof *(var_) == 4 || sizeof *(var_) == 8),         \
     (sizeof *(var_) == 1                                        \
          ? kit_atomic_load_explicit_8((u8 volatile *) (var_),   \
                                       (memory_order_))          \
      : sizeof *(var_) == 2                                      \
          ? kit_atomic_load_explicit_16((u16 volatile *) (var_), \
                                        (memory_order_))         \
      : sizeof *(var_) == 4                                      \
          ? kit_atomic_load_explicit_32((u32 volatile *) (var_), \
                                        (memory_order_))         \
          : kit_atomic_load_explicit_64((u64 volatile *) (var_), \
                                        (memory_order_))))

#  define atomic_exchange_explicit(var_, value_, memory_order_)      \
    (assert(sizeof *(var_) == 1 || sizeof *(var_) == 2 ||            \
            sizeof *(var_) == 4 || sizeof *(var_) == 8),             \
     (sizeof *(var_) == 1 ? kit_atomic_exchange_explicit_8(          \
                                (u8 volatile *) (var_),              \
                                (u8) (value_), (memory_order_))      \
      : sizeof *(var_) == 2 ? kit_atomic_exchange_explicit_16(       \
                                  (u16 volatile *) (var_),           \
                                  (u16) (value_), (memory_order_))   \
      : sizeof *(var_) == 4                                          \
          ? kit_atomic_exchange_explicit_32((u32 volatile *) (var_), \
                                            (u32) (value_),          \
                                            (memory_order_))         \
          : kit_atomic_exchange_explicit_64((u64 volatile *) (var_), \
                                            (u64) (value_),          \
                                            (memory_order_))))

#  define atomic_compare_exchange_strong_explicit(            \
      var_, expected_, value_, memory_order_succ_,            \
      memory_order_fail_)                                     \
    (assert(sizeof *(var_) == 1 || sizeof *(var_) == 2 ||     \
            sizeof *(var_) == 4 || sizeof *(var_) == 8),      \
     (sizeof *(var_) == 1                                     \
          ? kit_atomic_compare_exchange_explicit_8(           \
                (u8 volatile *) (var_), (u8 *) (expected_),   \
                (u8) (value_), (memory_order_succ_),          \
                (memory_order_fail_))                         \
      : sizeof *(var_) == 2                                   \
          ? kit_atomic_compare_exchange_explicit_16(          \
                (u16 volatile *) (var_), (u16 *) (expected_), \
                (u16) (value_), (memory_order_succ_),         \
                (memory_order_fail_))                         \
      : sizeof *(var_) == 4                                   \
          ? kit_atomic_compare_exchange_explicit_32(          \
                (u32 volatile *) (var_), (u32 *) (expected_), \
                (u32) (value_), (memory_order_succ_),         \
                (memory_order_fail_))                         \
          : kit_atomic_compare_exchange_explicit_64(          \
                (u64 volatile *) (var_), (u64 *) (expected_), \
                (u64) (value_), (memory_order_succ_),         \
                (memory_order_fail_))))

#  define atomic_compare_exchange_weak_explicit(                     \
      var_, expected_, value_, memory_order_succ_,                   \
      memory_order_fail_)                                            \
    atomic_compare_exchange_strong_explicit(var_, expected_, value_, \
                                            memory_order_succ_,      \
                                            memory_order_fail_)

#  define atomic_fetch_add_explicit(var_, value_, memory_order_)   \
    (assert(sizeof *(var_) == 1 || sizeof *(var_) == 2 ||          \
            sizeof *(var_) == 4 || sizeof *(var_) == 8),           \
     (sizeof *(var_) == 1 ? kit_atomic_fetch_add_explicit_8(       \
                                (u8 volatile *) (var_),            \
                                (u8) (value_), (memory_order_))    \
      : sizeof *(var_) == 2 ? kit_atomic_fetch_add_explicit_16(    \
                                  (u16 volatile *) (var_),         \
                                  (u16) (value_), (memory_order_)) \
      : sizeof *(var_) == 4 ? kit_atomic_fetch_add_explicit_32(    \
                                  (u32 volatile *) (var_),         \
                                  (u32) (value_), (memory_order_)) \
                            : kit_atomic_fetch_add_explicit_64(    \
                                  (u64 volatile *) (var_),         \
                                  (u64) (value_), (memory_order_))))

#  define atomic_store(var_, value_) \
    atomic_store(var_, value_, memory_order_seq_cst)

#  define atomic_load(var_) atomic_load(var_, memory_order_seq_cst)

#  define atomic_exchange(var_, value_) \
    atomic_exchange(var_, value_, memory_order_seq_cst)

#  define atomic_compare_exchange_strong(var_, expected_, value_)    \
    atomic_compare_exchange_strong_explicit(var_, expected_, value_, \
                                            memory_order_seq_cst,    \
                                            memory_order_seq_cst)

#  define atomic_compare_exchange_weak(var_, expected_, value_)    \
    atomic_compare_exchange_weak_explicit(var_, expected_, value_, \
                                          memory_order_seq_cst,    \
                                          memory_order_seq_cst)

#  define atomic_fetch_add(var_, value_) \
    atomic_fetch_add(var_, value_, memory_order_seq_cst)

#  ifdef __cplusplus
}
#  endif
#endif

#define ATOMIC KIT_ATOMIC

#endif