/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
***************************************************************************/

#pragma once
#ifndef GLMATOMIC_H
#define GLMATOMIC_H

#include "glmCoreAPI.h"
#include "glmTypes.h"

#include <atomic>

namespace glm
{
	//NOTE: atomicSignalFence...() prevent compiler reordering only
	extern GLM_CORE_API void atomicSignalFenceAcquire();
	extern GLM_CORE_API void atomicSignalFenceRelease();
	extern GLM_CORE_API void atomicSignalFenceSequentiallyConsistent();
	extern GLM_CORE_API void atomicThreadFenceAcquire();
	extern GLM_CORE_API void atomicThreadFenceRelease();
	extern GLM_CORE_API void atomicThreadFenceSequentiallyConsistent();

	struct GLM_CORE_API atomic32_t
	{
        std::atomic<int32_t> _atomic;
		typedef int32_t value_t;
		typedef int32_t add_operand_t;
		typedef int32_t and_operand_t;
		typedef int32_t or_operand_t;

		atomic32_t();
        atomic32_t(const atomic32_t& other);
        atomic32_t& operator=(const atomic32_t& other);
	};

	struct GLM_CORE_API atomic64_t
	{
        std::atomic<int64_t> _atomic;

		typedef int64_t value_t;
		typedef int64_t add_operand_t;
		typedef int64_t and_operand_t;
		typedef int64_t or_operand_t;

		atomic64_t();
		atomic64_t(const atomic64_t& other);
        atomic64_t& operator=(const atomic64_t& other);
	};

	//struct atomicptr_t
	//{
	//	std::atomic<void*> _atomic;
	//	typedef void* value_t;
	//};

	template <typename atomicT>
	GLM_C_INLINE typename atomicT::value_t atomicLoadRelaxed(const atomicT &object)
	{
	}

	template <>
	GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE atomic32_t::value_t atomicLoadRelaxed<atomic32_t>(const atomic32_t &object)
	{
        return std::atomic_load_explicit(&object._atomic, std::memory_order_relaxed);
	}
	template <>
	GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE atomic64_t::value_t atomicLoadRelaxed<atomic64_t>(const atomic64_t &object)
	{
        return std::atomic_load_explicit(&object._atomic, std::memory_order_relaxed);
	}
	//template <>
	//GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE atomicptr_t::value_t atomicLoadRelaxed<atomicptr_t>(const atomicptr_t &object)
	//{
	//	return mint_load_ptr_relaxed(const_cast<mint_atomicPtr_t*>(&object._atomic));
	//}

	template <typename atomicT>
	GLM_C_INLINE typename atomicT::value_t atomicLoadAcquire(const atomicT &object)
	{
	}

	template <>
	GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE atomic32_t::value_t atomicLoadAcquire<atomic32_t>(const atomic32_t &object)
	{
        atomicThreadFenceAcquire();
        return std::atomic_load_explicit(&object._atomic, std::memory_order_acquire);
	}
	template <>
	GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE atomic64_t::value_t atomicLoadAcquire<atomic64_t>(const atomic64_t &object)
	{
        atomicThreadFenceAcquire();
        return std::atomic_load_explicit(&object._atomic, std::memory_order_acquire);
	}

	template <typename atomicT>
	GLM_C_INLINE void atomicStoreRelaxed(atomicT &object, typename atomicT::value_t value)
	{
	}

	template <>
	GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE void atomicStoreRelaxed<atomic32_t>(atomic32_t &object, atomic32_t::value_t value)
	{
        std::atomic_store_explicit(&object._atomic, value, std::memory_order_relaxed);
	}

	template <>
	GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE void atomicStoreRelaxed<atomic64_t>(atomic64_t &object, atomic64_t::value_t value)
	{
        std::atomic_store_explicit(&object._atomic, value, std::memory_order_relaxed);
	}

	//template <>
	//GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE void atomicStoreRelaxed<atomicptr_t>(atomicptr_t &object, atomicptr_t::value_t value)
	//{
	//	mint_store_ptr_relaxed(const_cast<mint_atomicPtr_t*>(&object._atomic), value);
	//}

	template <typename atomicT>
	GLM_C_INLINE void atomicStoreRelease(atomicT &object, typename atomicT::value_t value)
	{
	}

	template <>
	GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE void atomicStoreRelease<atomic32_t>(atomic32_t &object, atomic32_t::value_t value)
	{
        std::atomic_store_explicit(&object._atomic, value, std::memory_order_release);
		atomicThreadFenceRelease();
	}
	template <>
	GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE void atomicStoreRelease<atomic64_t>(atomic64_t &object, atomic64_t::value_t value)
	{
        std::atomic_store_explicit(&object._atomic, value, std::memory_order_release);
		atomicThreadFenceRelease();
	}

	//NOTE: returns initial value of object
	template <typename atomicT>
	GLM_C_INLINE typename atomicT::value_t atomicCompareExchangeRelaxed(atomicT &object, typename atomicT::value_t value, typename atomicT::value_t comparAnd)
	{
	}

	template <>
	GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE atomic32_t::value_t atomicCompareExchangeRelaxed<atomic32_t>(atomic32_t &object, atomic32_t::value_t value, atomic32_t::value_t comparAnd)
	{
        std::atomic_compare_exchange_strong_explicit(&object._atomic, &comparAnd, value, std::memory_order_relaxed, std::memory_order_relaxed);
        return comparAnd;
		
	}
	template <>
	GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE atomic64_t::value_t atomicCompareExchangeRelaxed<atomic64_t>(atomic64_t &object, atomic64_t::value_t value, atomic64_t::value_t comparAnd)
	{
        std::atomic_compare_exchange_strong_explicit(&object._atomic, &comparAnd, value, std::memory_order_relaxed, std::memory_order_relaxed);
        return comparAnd;
	}
	//template <>
	//GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE atomicptr_t::value_t atomicCompareExchangeRelaxed<atomicptr_t>(atomicptr_t &object, atomicptr_t::value_t value, atomicptr_t::value_t comparAnd)
	//{
	//	return mint_compare_exchange_strong_ptr_relaxed(&object._atomic, comparAnd, value);
	//}

	template <typename atomicT>
	GLM_C_INLINE typename atomicT::value_t atomicFetchAddRelaxed(atomicT &object, typename atomicT::add_operand_t operand)
	{
	}

	template <>
	GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE atomic32_t::value_t atomicFetchAddRelaxed<atomic32_t>(atomic32_t &object, atomic32_t::add_operand_t operand)
	{
        return std::atomic_fetch_add_explicit(&object._atomic, operand, std::memory_order_relaxed);
	}
	template <>
	GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE atomic64_t::value_t atomicFetchAddRelaxed<atomic64_t>(atomic64_t &object, atomic64_t::add_operand_t operand)
	{
        return std::atomic_fetch_add_explicit(&object._atomic, operand, std::memory_order_relaxed);
    }

	//NOTE: returns initial value of object
	template <typename atomicT>
	GLM_C_INLINE typename atomicT::value_t atomicFetchAddSequentiallyConsistent(atomicT &object,typename atomicT::add_operand_t operand)
	{
	}

	template <>
	GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE atomic32_t::value_t atomicFetchAddSequentiallyConsistent<atomic32_t>(atomic32_t &object, atomic32_t::add_operand_t operand)
	{
		const atomic32_t::value_t value = std::atomic_fetch_add_explicit(&object._atomic, operand, std::memory_order_seq_cst);
		return value;
	}
	template <>
	GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE atomic64_t::value_t atomicFetchAddSequentiallyConsistent<atomic64_t>(atomic64_t &object, atomic64_t::add_operand_t operand)
	{
        const atomic64_t::value_t value = std::atomic_fetch_add_explicit(&object._atomic, operand, std::memory_order_seq_cst);
        return value;
    }

	//NOTE: returns initial value of object
	template <typename atomicT>
	GLM_C_INLINE typename atomicT::value_t atomicFetchAndRelaxed(atomicT &object, typename atomicT::and_operand_t operand)
	{
	}

	template <>
	GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE atomic32_t::value_t atomicFetchAndRelaxed<atomic32_t>(atomic32_t &object, atomic32_t::and_operand_t operand)
	{
        return std::atomic_fetch_and_explicit(&object._atomic, operand, std::memory_order_relaxed);
	}
	template <>
	GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE atomic64_t::value_t atomicFetchAndRelaxed<atomic64_t>(atomic64_t &object, atomic64_t::and_operand_t operand)
	{
        return std::atomic_fetch_and_explicit(&object._atomic, operand, std::memory_order_relaxed);
	}

	//NOTE: returns initial value of object
	template <typename atomicT>
	GLM_C_INLINE typename atomicT::value_t atomicFetchOrRelaxed(atomicT &object, typename atomicT::or_operand_t operand)
	{
	}

	template <>
	GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE atomic32_t::value_t atomicFetchOrRelaxed<atomic32_t>(atomic32_t &object, atomic32_t::or_operand_t operand)
	{
        return std::atomic_fetch_or_explicit(&object._atomic, operand, std::memory_order_relaxed);
	}
	template <>
	GLM_FULLY_SPECIALIZED_TEMPLATE_INLINE atomic64_t::value_t atomicFetchOrRelaxed<atomic64_t>(atomic64_t &object, atomic64_t::or_operand_t operand)
	{
        return std::atomic_fetch_or_explicit(&object._atomic, operand, std::memory_order_relaxed);
    }

	GLM_C_INLINE bool atomicLoadRelaxedBool(const atomic32_t &object)
	{
		return (atomicLoadRelaxed<atomic32_t>(object) != 0);
	}

	GLM_C_INLINE bool atomicLoadAcquireBool(const atomic32_t &object)
	{
		return (atomicLoadAcquire<atomic32_t>(object) != 0);
	}

	GLM_C_INLINE void atomicStoreRelaxedBool(atomic32_t &object, bool value)
	{
		const atomic32_t::value_t objectValue = (value)? 1 : 0;
		atomicStoreRelaxed<atomic32_t>(object, objectValue);
	}

	GLM_C_INLINE void atomicStoreReleaseBool(atomic32_t &object, bool value)
	{
		const atomic32_t::value_t objectValue = (value)? 1 : 0;
		atomicStoreRelease<atomic32_t>(object, objectValue);
	}

	GLM_C_INLINE bool atomicCompareExchangeRelaxedBool(atomic32_t &object, bool value, bool comparAnd)
	{
		const atomic32_t::value_t objectValue = (value)? 1 : 0;
		const atomic32_t::value_t objectComparAnd = (comparAnd)? 1 : 0;

		const atomic32_t::value_t objectValueOld = atomicCompareExchangeRelaxed<atomic32_t>(object, objectValue, objectComparAnd);
		return (objectValueOld != 0);
	}
}

#endif // GLMATOMIC_H
