// =====================================================================================================================
//  fennec, a free and open source game engine
//  Copyright © 2025 - 2026  Medusa Slockbower
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================

#ifndef FENNEC_LANG_DETAIL_ATOMIC_H
#define FENNEC_LANG_DETAIL_ATOMIC_H

// Define _Atomic as volatile
#ifdef _Atomic
#undef _Atomic
#endif
#define _Atomic volatile

#if FENNEC_GLIBC

// memory_order ========================================================================================================

enum memory_order {
	memory_order_relaxed = __ATOMIC_RELAXED,
	memory_order_consume = __ATOMIC_CONSUME,
	memory_order_acquire = __ATOMIC_ACQUIRE,
	memory_order_release = __ATOMIC_RELEASE,
	memory_order_acq_rel = __ATOMIC_ACQ_REL,
	memory_order_seq_cst = __ATOMIC_SEQ_CST,
};



// atomic_init =========================================================================================================

template<typename T>
constexpr void atomic_init(_Atomic T* x, const T val) {
	*x = val;
}



// atomic_flag =========================================================================================================

struct atomic_flag;



// atomic_flag_test_and_set ============================================================================================

constexpr bool atomic_flag_test_and_set(_Atomic atomic_flag* x) {
	return ::__atomic_test_and_set(x, memory_order_seq_cst);
}

constexpr bool atomic_flag_test_and_set_explicit(_Atomic atomic_flag* x, memory_order memorder) {
	return ::__atomic_test_and_set(x, memorder);
}



// atomic_flag_clear ===================================================================================================

constexpr void atomic_flag_clear(_Atomic atomic_flag* x) {
	return ::__atomic_clear(x, memory_order_seq_cst);
}

constexpr void atomic_flag_clear_explicit(_Atomic atomic_flag* x, memory_order memorder) {
	return ::__atomic_clear(x, memorder);
}



// atomic_load =========================================================================================================

template<typename T>
constexpr T atomic_load(_Atomic T* x) {
	return ::__atomic_load_n(x, memory_order_seq_cst);
}

template<typename T>
constexpr T atomic_load_explicit(_Atomic T* x, memory_order memorder) {
	return ::__atomic_load_n(x, memorder);
}



// atomic_store ========================================================================================================

template<typename T>
constexpr void atomic_store(_Atomic T* x, const T val) {
	return ::__atomic_store_n(x, val, memory_order_seq_cst);
}

template<typename T>
constexpr void atomic_store_explicit(_Atomic T* x, const T val, memory_order memorder) {
	return ::__atomic_store_n(x, val, memorder);
}



// atomic_exchange =====================================================================================================

template<typename T>
constexpr T atomic_exchange(_Atomic T* x, const T val) {
	return ::__atomic_exchange_n(x, val, memory_order_seq_cst);
}

template<typename T>
constexpr T atomic_exchange_explicit(_Atomic T* x, const T val, memory_order memorder) {
	return ::__atomic_exchange_n(x, val, memorder);
}



// atomic_compare_exchange_weak ========================================================================================

template<typename T>
constexpr bool atomic_compare_exchange_weak(_Atomic T* x, T* exp, const T des) {
	return ::__atomic_compare_exchange_n(x, exp, des, true, memory_order_seq_cst, memory_order_seq_cst);
}

template<typename T>
constexpr bool atomic_compare_exchange_weak_explicit(_Atomic T* x, T* exp, const T des, memory_order succ, memory_order fail) {
	return ::__atomic_compare_exchange_n(x, exp, des, true, succ, fail);
}



// atomic_compare_exchange_strong ======================================================================================

template<typename T>
constexpr bool atomic_compare_exchange_strong(_Atomic T* x, T* exp, const T des) {
	return ::__atomic_compare_exchange_n(x, exp, des, false, memory_order_seq_cst, memory_order_seq_cst);
}

template<typename T>
constexpr bool atomic_compare_exchange_strong_explicit(_Atomic T* x, T* exp, const T des, memory_order succ, memory_order fail) {
	return ::__atomic_compare_exchange_n(x, exp, des, false, succ, fail);
}



// atomic_fetch_add ====================================================================================================

template<typename T>
constexpr T atomic_fetch_add(_Atomic T* x, const T val) {
	return ::__atomic_fetch_add(x, val, memory_order_seq_cst);
}

template<typename T>
constexpr T atomic_fetch_add_explicit(_Atomic T* x, const T val, memory_order memorder) {
	return ::__atomic_fetch_add(x, val, memorder);
}



// atomic_fetch_sub ====================================================================================================

template<typename T>
constexpr T atomic_fetch_sub(_Atomic T* x, const T val) {
	return ::__atomic_fetch_sub(x, val, memory_order_seq_cst);
}

template<typename T>
constexpr T atomic_fetch_sub_explicit(_Atomic T* x, const T val, memory_order memorder) {
	return ::__atomic_fetch_sub(x, val, memorder);
}



// atomic_fetch_or ====================================================================================================

template<typename T>
constexpr T atomic_fetch_or(_Atomic T* x, const T val) {
	return ::__atomic_fetch_or(x, val, memory_order_seq_cst);
}

template<typename T>
constexpr T atomic_fetch_or_explicit(_Atomic T* x, const T val, memory_order memorder) {
	return ::__atomic_fetch_or(x, val, memorder);
}



// atomic_fetch_xor ====================================================================================================

template<typename T>
constexpr T atomic_fetch_xor(_Atomic T* x, const T val) {
	return ::__atomic_fetch_xor(x, val, memory_order_seq_cst);
}

template<typename T>
constexpr T atomic_fetch_xor_explicit(_Atomic T* x, const T val, memory_order memorder) {
	return ::__atomic_fetch_xor(x, val, memorder);
}



// atomic_fetch_and ====================================================================================================

template<typename T>
constexpr T atomic_fetch_and(_Atomic T* x, const T val) {
	return ::__atomic_fetch_and(x, val, memory_order_seq_cst);
}

template<typename T>
constexpr T atomic_fetch_and_explicit(_Atomic T* x, const T val, memory_order memorder) {
	return ::__atomic_fetch_and(x, val, memorder);
}

#endif

#endif // FENNEC_LANG_DETAIL_ATOMIC_H
