/******************************************************************************/
/* REFERENCE-COUNTING HELPERS *************************************************/
/******************************************************************************/

#ifndef _H_EBCL_REFCOUNT
#define _H_EBCL_REFCOUNT
#include <ebcl/Utilities.hh>
namespace ebcl {

// A base class and pointer type that can be used to implement classes which
// use in-instance reference counters.


/*= REFERENCE-COUNTED INSTANCES ==============================================*/

// Base class containing the reference counter. It may be an atomic, or it
// may be a simple integer.
template< bool UseAtomic = false >
class T_ReferenceCountedClass
{
    public:
	// Is it atomic ?
	static const bool UsingAtomic = UseAtomic;
	// Reference counter type
	using T_ReferenceCounter = std::conditional_t<
			UseAtomic , std::atomic< uint32_t > , uint32_t
		>;

	// Reference-counting pointers can access the counter
	template< typename > friend class T_RcPtr;

    private:
	T_ReferenceCounter referenceCounter_;

	// Add a reference to the counter
	void increaseReferences_( ) noexcept;

	// Remove a reference from the counter, returning true if the instance
	// is no longer referenced.
	bool decreaseReferences_( ) noexcept;

    protected:
	// Initialise the counter to 0
	T_ReferenceCountedClass( ) noexcept;

    public:
	// Get the reference count
	uint32_t getReferenceCount( ) const noexcept;

	// Deleted copy/move constructors and assignment operators
	T_ReferenceCountedClass(
			T_ReferenceCountedClass< UseAtomic > const& ) = delete;
	T_ReferenceCountedClass< UseAtomic >& operator =(
			T_ReferenceCountedClass< UseAtomic > const& ) = delete;
	T_ReferenceCountedClass(
			T_ReferenceCountedClass< UseAtomic >&& ) = delete;
	T_ReferenceCountedClass< UseAtomic >& operator =(
			T_ReferenceCountedClass< UseAtomic >&& ) = delete;
};

// Shortcuts
using T_AtomicRcClass = T_ReferenceCountedClass< true >;
using T_SimpleRcClass = T_ReferenceCountedClass< false >;


/*= REFERENCE-COUNTING POINTERS ==============================================*/

// Pointers that use T_ReferenceCountedClass in order to track the reference
// counts of the instances it manages.
template< typename Type >
class T_RcPtr
{
	static_assert( MetaOr< std::is_base_of< T_AtomicRcClass , Type > ,
			std::is_base_of< T_SimpleRcClass , Type > >::value ,
		"type does not include a reference counter" );

    public:
	// Target type
	using T_Target = Type;
	// Pointer type
	using T_Self = T_RcPtr< Type >;
	// Base reference counting class
	using T_RcClass = std::conditional_t<
			std::is_base_of< T_AtomicRcClass , Type >::value ,
			T_AtomicRcClass , T_SimpleRcClass
		>;

	// All RcPtr's are friends
	template< typename > friend class T_RcPtr;

    private:
	T_Target* target_;
	void setTarget_( T_Target* t ) noexcept;

    public:

	// ---------------------------------------------------------------------

	T_RcPtr( ) noexcept;
	~T_RcPtr( ) noexcept;

	// Constructor function
	template< typename... AT >
		static T_RcPtr< Type > New( AT&& ... arguments );

	// Initialise from raw pointer. The instance must have a reference
	// count of zero, and must have been allocated using new.
	static T_RcPtr< Type > FromRaw( Type* ptr ) noexcept;

	void clear( ) noexcept;

	// ---------------------------------------------------------------------
	// Copy constructors and operators

	T_RcPtr( T_Self const& other ) noexcept;
	T_Self& operator= ( T_Self const& other ) noexcept;

	template< typename Q , T_EnableForChild< Type , Q > = true >
		T_RcPtr( T_RcPtr< Q > const& source ) noexcept;
	template< typename Q , T_EnableForParent< Type , Q > = false >
		explicit T_RcPtr( T_RcPtr< Q > const& source );

	template< typename Q , T_EnableForChild< Type , Q > = true >
		T_Self& operator= ( T_RcPtr< Q > const& other ) noexcept;
	template< typename Q , T_EnableForParent< Type , Q > = false >
		T_Self& operator= ( T_RcPtr< Q > const& other );

	// ---------------------------------------------------------------------
	// Move constructors and operators

	template< typename Q , T_EnableForChild< Type , Q > = true >
		T_RcPtr( T_RcPtr< Q >&& other ) noexcept;
	template< typename Q , T_EnableForParent< Type , Q > = false >
		explicit T_RcPtr( T_RcPtr< Q >&& other );

	template< typename Q , T_EnableForChild< Type , Q > = true >
		T_Self& operator= ( T_RcPtr< Q >&& other ) noexcept;
	template< typename Q , T_EnableForParent< Type , Q > = false >
		T_Self& operator= ( T_RcPtr< Q >&& other );

	// ---------------------------------------------------------------------
	// Swapping

	T_Self swap( T_Self& other ) noexcept;

	// ---------------------------------------------------------------------
	// Boolean conversion

	operator bool ( ) const noexcept;
	bool operator! ( ) const noexcept;

	// ---------------------------------------------------------------------
	// Content access

	// Access the actual pointer
	Type * get( ) const noexcept;
	explicit operator Type* ( ) const noexcept;

	// Access the object; undefined behavior if get() == nullptr.
	Type* operator -> ( ) const;
	Type& operator * ( ) const;
};

template< typename T >
void swap( T_RcPtr< T >& lhs , T_RcPtr< T >& rhs ) noexcept;

template< typename T >
inline T_RcPtr< T > T_RcPtr< T >::FromRaw(
		T* ptr ) noexcept
{
	assert( ptr->getReferenceCount( ) == 0 );
	T_RcPtr< T > rv;
	ptr->increaseReferences_( );
	rv.target_ = ptr;
	return rv;
}


} // namespace
#endif // _H_EBCL_REFCOUNT
#include <ebcl/inline/RefCount.hh>