#pragma once

#include <mutex>
#include <utility>

namespace psemek::util
{

	template <typename T, typename Mutex = std::mutex>
	struct mutexed
	{
		mutexed() = default;

		mutexed(mutexed const &) = delete;
		mutexed(mutexed &&) = delete;

		mutexed & operator = (mutexed const &) = delete;
		mutexed & operator = (mutexed &&) = delete;

		mutexed(T && value)
			: value_(std::move(value))
		{}

		template <typename ... Args>
		mutexed(std::in_place_t, Args && ... args)
			: value_(std::forward<Args>(args)...)
		{}

		T load() const
		{
			std::lock_guard lock{mutex_};
			return value_;
		}

		void store(T && new_value)
		{
			std::lock_guard lock{mutex_};
			value_ = std::move(new_value);
		}

		T exchange(T && new_value)
		{
			std::lock_guard lock{mutex_};
			std::swap(value_, new_value);
			return new_value;
		}

	private:
		T value_;
		mutable Mutex mutex_;
	};

}
