/******************************************************************************/
/* POINTERS - INLINE CODE *****************************************************/
/******************************************************************************/

#ifndef _H_EBCL_INLINE_POINTERS
#define _H_EBCL_INLINE_POINTERS
#include <ebcl/Pointers.hh>
namespace ebcl {


/*= T_OwnPtr =================================================================*/

template< typename T >
inline T_OwnPtr< T >::T_OwnPtr( T* p ) noexcept
	: p_( p )
{ }

template< typename T >
inline T_OwnPtr< T >::T_OwnPtr( )
	: T_OwnPtr( nullptr )
{ }

/*----------------------------------------------------------------------------*/

template< typename T >
template<
	typename Q ,
	T_EnableForChild< T , Q >
> inline T_OwnPtr< T >::T_OwnPtr( T_OwnPtr< Q >&& source ) noexcept
	: p_( nullptr )
{
	T* temp( source.p_ );
	source.p_ = nullptr;
	p_ = temp;
}

template< typename T >
template<
	typename Q ,
	T_EnableForParent< T , Q >
> inline T_OwnPtr< T >::T_OwnPtr( T_OwnPtr< Q >&& source )
	: p_( nullptr )
{
	T* temp( dynamic_cast< T* >( source.p_ ) );
	if ( source.p_ && !temp ) {
		throw std::bad_cast( );
	}
	source.p_ = nullptr;
	p_ = temp;
}

template< typename T >
template<
	typename Q ,
	T_EnableForChild< T , Q >
> inline T_OwnPtr< T >& T_OwnPtr< T >::operator= ( T_OwnPtr< Q >&& source ) noexcept
{
	T* temp( source.p_ );
	clear( );
	source.p_ = nullptr;
	p_ = temp;
	return *this;
}

template< typename T >
template<
	typename Q ,
	T_EnableForParent< T , Q >
> inline T_OwnPtr< T >& T_OwnPtr< T >::operator= (
		T_OwnPtr< Q >&& source )
{
	T* temp( dynamic_cast< T* >( source.p_ ) );
	if ( source.p_ && !temp ) {
		throw std::bad_cast( );
	}
	clear( );
	source.p_ = nullptr;
	p_ = temp;
	return *this;
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline T_OwnPtr< T >::~T_OwnPtr( )
{
	clear( );
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline void swap( T_OwnPtr< T >& lhs , T_OwnPtr< T >& rhs ) noexcept
{
	std::swap( lhs.p_ , rhs.p_ );
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline void T_OwnPtr< T >::clear( )
{
	T* ptr( nullptr );
	std::swap( p_ , ptr );
	delete ptr;
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline bool T_OwnPtr< T >::operator== ( const T* p ) const
{
	return p == p_;
}

template< typename T >
inline bool T_OwnPtr< T >::operator!= ( const T* p ) const
{
	return p != p_;
}

template< typename T >
inline T_OwnPtr< T >::operator bool ( ) const
{
	return p_ != nullptr;
}

template< typename T >
inline bool T_OwnPtr< T >::operator! ( ) const
{
	return p_ == nullptr;
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline T* T_OwnPtr< T >::get( ) const
{
	return p_;
}

template< typename T >
inline T* T_OwnPtr< T >::operator-> ( ) const
{
	assert( p_ != nullptr );
	return p_;
}

template< typename T >
inline T& T_OwnPtr< T >::operator* ( ) const
{
	assert( p_ != nullptr );
	return *p_;
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline T_SharedPtr< T > T_OwnPtr< T >::makeShared( )
{
	T* temp( nullptr );
	std::swap( p_ , temp );
	return T_SharedPtr< T >( temp );
}


/*= NewOwned / OwnRawPointer =================================================*/

template<
	typename Type ,
	typename... ArgTypes
	>
inline T_OwnPtr< Type > NewOwned( ArgTypes&& ... arguments )
{
	return T_OwnPtr< Type >( new Type( std::forward< ArgTypes >( arguments ) ... ) );
}

template< typename Type >
inline T_OwnPtr< Type > OwnRawPointer( Type*& pointer ) noexcept
{
	assert( pointer != nullptr );
	T_OwnPtr< Type > p( pointer );
	pointer = nullptr;
	return p;
}

template<
	typename Type , typename Other ,
	T_EnableForChild< Type , Other >
> inline T_OwnPtr< Type > OwnRawPointer( Other*& pointer ) noexcept
{
	assert( pointer != nullptr );
	T_OwnPtr< Type > p( pointer );
	pointer = nullptr;
	return p;
}

template<
	typename Type , typename Other ,
	T_EnableForParent< Type , Other >
> inline T_OwnPtr< Type > OwnRawPointer( Other*& pointer )
{
	assert( pointer != nullptr );
	Type* temp( dynamic_cast< Type* >( pointer ) );
	if ( temp == nullptr ) {
		throw std::bad_cast( );
	}
	T_OwnPtr< Type > p( temp );
	pointer = nullptr;
	return p;
}


/*= T_Reference_ =============================================================*/

// Reference counter for shared pointers
class T_Reference_
{
   private:
	typedef std::function< void ( void* ) > F_Destr_;

	friend struct T_WeakChain_;

	void* pointer_;
	F_Destr_ destr_;
	uint32_t count_;
	T_WeakChain_* weaks_;

   public:
	/* References are pooled */
	void* operator new( size_t count ) noexcept;
	void operator delete( void* object ) noexcept;

	T_Reference_( ) = delete;
	T_Reference_( T_Reference_ const& ) = delete;
	T_Reference_( T_Reference_&& ) = delete;

	T_Reference_( void* ptr , F_Destr_ destructor );

	~T_Reference_( );

	void* pointer( ) const;
	T_Reference_ * increase( );
	void decrease( );
	void* extract( );
};


/*----------------------------------------------------------------------------*/

inline void* T_Reference_::pointer( ) const
{
	return pointer_;
}

inline T_Reference_* T_Reference_::increase( )
{
	assert( count_ > 0 );
	count_ ++;
	return this;
}

inline void T_Reference_::decrease( )
{
	assert( count_ > 0 );
	count_ --;
	if ( count_ == 0 ) {
		delete this;
	}
}


/*= T_BasePtr_ ===============================================================*/

template< typename T >
inline T_BasePtr_< T >::T_BasePtr_( T_Reference_* ref ) noexcept
	: ref_( ref )
{ }

template< typename T >
inline T_BasePtr_< T >::operator bool ( ) const
{
	return ref_ != nullptr;
}

template< typename T >
inline bool T_BasePtr_< T >::operator! ( ) const
{
	return ref_ == nullptr;
}

template< typename T >
inline T* T_BasePtr_< T >::operator-> ( ) const
{
	assert( ref_ != nullptr );
	return reinterpret_cast< T* >( ref_->pointer( ) );
}

template< typename T >
inline T_BasePtr_< T >::operator T* ( ) const
{
	return ref_ == nullptr
	       ? nullptr
	       : reinterpret_cast< T* >( ref_->pointer( ) );
}

template< typename T >
inline T& T_BasePtr_< T >::operator* ( ) const
{
	assert( ref_ != nullptr );
	return *reinterpret_cast< T* >( ref_->pointer( ) );
}


/*= X_TooManyReferences ======================================================*/

inline X_TooManyReferences::X_TooManyReferences( )
	: std::runtime_error( "too many references" )
{ }

/*= T_SharedPtr ==============================================================*/

template< typename T >
inline T_Reference_* T_SharedPtr< T >::setRef( T_Reference_* from )
{
	if ( from == nullptr ) {
		return nullptr;
	}
	return from->increase( );
}

template< typename T >
inline void T_SharedPtr< T >::clearRef( )
{
	if ( ref_ != nullptr ) {
		ref_->decrease( );
	}
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline T_SharedPtr< T >::T_SharedPtr( T* ptr )
	: T_Base_( new T_Reference_( ptr ,
				    []( void* p ) {
		delete reinterpret_cast< T* >( p );
	} ) )
{ }

template< typename T >
inline T_SharedPtr< T >::T_SharedPtr( ) noexcept
	: T_Base_( nullptr )
{ }

template< typename T >
inline T_SharedPtr< T >::~T_SharedPtr( )
{
	clearRef( );
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline T_SharedPtr< T >::T_SharedPtr( T_Self_ const& source )
	: T_Base_( setRef( source.ref_ ) )
{ }

template< typename T >
template<
	typename Q ,
	T_EnableForChild< T , Q >
> inline T_SharedPtr< T >::T_SharedPtr( T_SharedPtr< Q > const& other )
	: T_Base_( setRef( other.ref_ ) )
{ }

template< typename T >
template<
	typename Q ,
	T_EnableForParent< T , Q >
> inline T_SharedPtr< T >::T_SharedPtr( T_SharedPtr< Q > const& other )
	: T_Base_( nullptr )
{
	Q* const p( other );
	if ( p != nullptr && dynamic_cast< T* >( p ) == nullptr ) {
		throw std::bad_cast( );
	}
	ref_ = setRef( other.ref_ );
}

/*----------------------------------------------------------------------------*/

template< typename T >
template<
	typename Q ,
	T_EnableForChild< T , Q >
> inline T_SharedPtr< T >::T_SharedPtr( T_SharedPtr< Q >&& other ) noexcept
	: T_Base_( other.ref_ )
{
	other.ref_ = nullptr;
}

template< typename T >
template<
	typename Q ,
	T_EnableForParent< T , Q >
> inline T_SharedPtr< T >::T_SharedPtr( T_SharedPtr< Q >&& other )
	: T_Base_( nullptr )
{
	Q* const p( other );
	if ( p != nullptr && dynamic_cast< T* >( p ) == nullptr ) {
		throw std::bad_cast( );
	}
	using std::swap;
	swap( ref_ , other.ref_ );
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline T_SharedPtr< T >& T_SharedPtr< T >::operator= ( T_SharedPtr< T > const& other )
{
	if ( &other != this && other.ref_ != ref_ ) {
		clearRef( );
		ref_ = setRef( other.ref_ );
	}
	return *this;
}

template< typename T >
template<
	typename Q ,
	T_EnableForChild< T , Q >
> inline T_SharedPtr< T >& T_SharedPtr< T >::operator= ( T_SharedPtr< Q > const& other )
{
	if ( other.ref_ != ref_ ) {
		clearRef( );
		ref_ = setRef( other.ref_ );
	}
	return *this;
}

template< typename T >
template<
	typename Q ,
	T_EnableForParent< T , Q >
> inline T_SharedPtr< T >& T_SharedPtr< T >::operator= ( T_SharedPtr< Q > const& other )
{
	if ( other.ref_ != ref_ ) {
		Q* const p( other );
		if ( p != nullptr && dynamic_cast< T* >( p ) == nullptr ) {
			throw std::bad_cast( );
		}
		clearRef( );
		ref_ = setRef( other.ref_ );
	}
	return *this;
}

/*----------------------------------------------------------------------------*/

template< typename T >
template<
	typename Q ,
	T_EnableForChild< T , Q >
> inline T_SharedPtr< T >& T_SharedPtr< T >::operator= ( T_SharedPtr< Q >&& other ) noexcept
{
	if ( other.ref_ != ref_ ) {
		clearRef( );
		ref_ = nullptr;
		std::swap( ref_ , other.ref_ );
	}
	return *this;
}

template< typename T >
template<
	typename Q ,
	T_EnableForParent< T , Q >
> inline T_SharedPtr< T >& T_SharedPtr< T >::operator= ( T_SharedPtr< Q >&& other )
{
	if ( other.ref_ != ref_ ) {
		Q* const p( other );
		if ( p != nullptr && dynamic_cast< T* >( p ) == nullptr ) {
			throw std::bad_cast( );
		}
		clearRef( );
		ref_ = nullptr;
		std::swap( ref_ , other.ref_ );
	}
	return *this;
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline void swap( T_SharedPtr< T >& lhs , T_SharedPtr< T >& rhs ) noexcept
{
	std::swap( lhs.ref_ , rhs.ref_ );
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline bool T_SharedPtr< T >::operator== ( T_SharedPtr< T > const& other ) const
{
	return other.ref_ == ref_;
}

template< typename T >
inline bool T_SharedPtr< T >::operator!= ( T_SharedPtr< T > const& other ) const
{
	return other.ref_ != ref_;
}

template< typename T >
inline bool T_SharedPtr< T >::operator== ( T_WeakPtr< T > const& other ) const
{
	return other.ref_ == ref_;
}

template< typename T >
inline bool T_SharedPtr< T >::operator!= ( T_WeakPtr< T > const& other ) const
{
	return other.ref_ != ref_;
}

template< typename T >
inline void T_SharedPtr< T >::clear( )
{
	clearRef( );
	ref_ = nullptr;
}

template< typename T >
T_OwnPtr< T > T_SharedPtr< T >::makeOwned( )
{
	if ( ref_ == nullptr ) {
		return T_OwnPtr< T >( );
	}
	void* const p( ref_->extract( ) );
	ref_ = nullptr;
	return T_OwnPtr< T >( reinterpret_cast< T* >( p ) );
}


/*= T_WeakPtr ================================================================*/

template< typename T >
inline T_WeakPtr< T >::T_WeakPtr( T_Reference_* ref )
	: T_BasePtr_< T >( ref ) , chain_( ref_ )
{
	chain_.init( );
}

template< typename T >
inline T_WeakPtr< T >::T_WeakPtr( )
	: T_WeakPtr( nullptr )
{ }

template< typename T >
inline T_WeakPtr< T >::~T_WeakPtr( )
{
	chain_.unchain( );
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline T_WeakPtr< T >::T_WeakPtr( T_WeakPtr< T > const& other )
	: T_WeakPtr( other.ref_ )
{ }

template< typename T >
template< typename Q , T_EnableForChild< T , Q > >
inline T_WeakPtr< T >::T_WeakPtr( T_WeakPtr< Q > const& other )
	: T_WeakPtr( other.ref_ )
{ }

template< typename T >
template< typename Q , T_EnableForParent< T , Q > >
inline T_WeakPtr< T >::T_WeakPtr( T_WeakPtr< Q > const& other )
	: T_WeakPtr( )
{
	Q* const p( other );
	if ( p != nullptr && dynamic_cast< T* >( p ) == nullptr ) {
		throw std::bad_cast( );
	}
	ref_ = other.ref_;
	chain_.init( );
}

/*----------------------------------------------------------------------------*/

template< typename T >
template< typename Q , T_EnableForChild< T , Q > >
inline T_WeakPtr< T >::T_WeakPtr( T_WeakPtr< Q >&& other ) noexcept
	: T_WeakPtr( nullptr )
{
	other.chain_.unchain( );
	ref_ = other.ref_;
	other.ref_ = nullptr;
	chain_.init( );
}

template< typename T >
template< typename Q , T_EnableForParent< T , Q > >
inline T_WeakPtr< T >::T_WeakPtr( T_WeakPtr< Q >&& other )
	: T_WeakPtr( nullptr )
{
	Q* const p( other );
	if ( p != nullptr && dynamic_cast< T* >( p ) == nullptr ) {
		throw std::bad_cast( );
	}
	other.chain_.unchain( );
	ref_ = other.ref_;
	other.ref_ = nullptr;
	chain_.init( );
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline T_WeakPtr< T >::T_WeakPtr( T_SharedPtr< T > const& shared )
	: T_WeakPtr( shared.ref_ )
{ }

template< typename T >
template< typename Q , T_EnableForChild< T , Q > >
inline T_WeakPtr< T >::T_WeakPtr( T_SharedPtr< Q > const& shared )
	: T_WeakPtr( shared.ref_ )
{ }

template< typename T >
template< typename Q , T_EnableForParent< T , Q > >
inline T_WeakPtr< T >::T_WeakPtr( T_SharedPtr< Q > const& shared )
	: T_WeakPtr( nullptr )
{
	Q* const p( shared );
	if ( p != nullptr && dynamic_cast< T* >( p ) == nullptr ) {
		throw std::bad_cast( );
	}
	ref_ = shared.ref_;
	chain_.init( );
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline void swap( T_WeakPtr< T >& lhs , T_WeakPtr< T >& rhs )
{
	if ( lhs.ref_ != rhs.ref_ ) {
		lhs.chain_.unchain( );
		rhs.chain_.unchain( );
		std::swap( lhs.ref_ , rhs.ref_ );
		lhs.chain_.init( );
		rhs.chain_.init( );
	}
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline T_WeakPtr< T >& T_WeakPtr< T >::operator= ( T_WeakPtr< T > const& other )
{
	chain_.unchain( );
	ref_ = other.ref_;
	chain_.init( );
	return *this;
}

template< typename T >
template< typename Q , T_EnableForChild< T , Q > >
inline T_WeakPtr< T >& T_WeakPtr< T >::operator= ( T_WeakPtr< Q > const& other )
{
	chain_.unchain( );
	ref_ = other.ref_;
	chain_.init( );
	return *this;
}

template< typename T >
template< typename Q , T_EnableForParent< T , Q > >
inline T_WeakPtr< T >& T_WeakPtr< T >::operator= ( T_WeakPtr< Q > const& other )
{
	Q* const p( other );
	if ( p != nullptr && dynamic_cast< T* >( p ) == nullptr ) {
		throw std::bad_cast( );
	}
	chain_.unchain( );
	ref_ = other.ref_;
	chain_.init( );
	return *this;
}

/*----------------------------------------------------------------------------*/

template< typename T >
template< typename Q , T_EnableForChild< T , Q > >
inline T_WeakPtr< T >& T_WeakPtr< T >::operator= ( T_WeakPtr< Q >&& other )
{
	ref_ = nullptr;
	chain_.unchain( );
	if ( other.ref_ ) {
		other.chain_.unchain( );
		ref_ = other.ref_;
		other.ref_ = nullptr;
		chain_.init( );
		other.chain_.init( );
	}
	return *this;
}

template< typename T >
template< typename Q , T_EnableForParent< T , Q > >
inline T_WeakPtr< T >& T_WeakPtr< T >::operator= ( T_WeakPtr< Q >&& other )
{
	Q* const p( other );
	if ( p != nullptr && dynamic_cast< T* >( p ) == nullptr ) {
		throw std::bad_cast( );
	}
	ref_ = nullptr;
	chain_.unchain( );
	if ( other.ref_ ) {
		other.chain_.unchain( );
		ref_ = other.ref_;
		other.ref_ = nullptr;
		chain_.init( );
		other.chain_.init( );
	}
	return *this;
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline T_WeakPtr< T >& T_WeakPtr< T >::operator= ( T_SharedPtr< T > const& shared )
{
	chain_.unchain( );
	ref_ = shared.ref_;
	chain_.init( );
	return *this;
}

template< typename T >
template< typename Q , T_EnableForChild< T , Q > >
inline T_WeakPtr< T >& T_WeakPtr< T >::operator= ( T_SharedPtr< Q > const& shared )
{
	chain_.unchain( );
	ref_ = shared.ref_;
	chain_.init( );
	return *this;
}

template< typename T >
template< typename Q , T_EnableForParent< T , Q > >
inline T_WeakPtr< T >& T_WeakPtr< T >::operator= ( T_SharedPtr< Q > const& shared )
{
	Q* const p( shared );
	if ( p != nullptr && dynamic_cast< T* >( p ) == nullptr ) {
		throw std::bad_cast( );
	}
	chain_.unchain( );
	ref_ = shared.ref_;
	chain_.init( );
	return *this;
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline bool T_WeakPtr< T >::operator== ( T_WeakPtr< T > const& other ) const
{
	return other.ref_ == ref_;
}

template< typename T >
inline bool T_WeakPtr< T >::operator!= ( T_WeakPtr< T > const& other ) const
{
	return other.ref_ != ref_;
}

template< typename T >
inline bool T_WeakPtr< T >::operator== ( T_SharedPtr< T > const& other ) const
{
	return other.ref_ == ref_;
}

template< typename T >
inline bool T_WeakPtr< T >::operator!= ( T_SharedPtr< T > const& other ) const
{
	return other.ref_ != ref_;
}

/*----------------------------------------------------------------------------*/

template< typename T >
inline void T_WeakPtr< T >::clear( )
{
	chain_.unchain( );
	ref_ = nullptr;
}


/*= NewShared ================================================================*/

template<
	typename Type ,
	typename... ArgTypes
> inline T_SharedPtr< Type > NewShared( ArgTypes&& ... arguments )
{
	return T_SharedPtr< Type >( new Type( std::forward< ArgTypes >( arguments ) ... ) );
}

template< typename Type >
T_SharedPtr< Type > ShareRawPointer(
		Type*& pointer ) noexcept
{
	assert( pointer != nullptr );
	T_SharedPtr< Type > p( pointer );
	pointer = nullptr;
	return p;
}

template<
	typename Type , typename Other ,
	T_EnableForChild< Type , Other >
> inline T_SharedPtr< Type > ShareRawPointer(
		Other*& pointer ) noexcept
{
	assert( pointer != nullptr );
	T_SharedPtr< Type > p( pointer );
	pointer = nullptr;
	return p;
}

template<
	typename Type , typename Other ,
	T_EnableForParent< Type , Other >
> inline T_SharedPtr< Type > ShareRawPointer(
		Other*& pointer )
{
	assert( pointer != nullptr );
	Type* temp( dynamic_cast< Type* >( pointer ) );
	if ( temp == nullptr ) {
		throw std::bad_cast( );
	}
	T_SharedPtr< Type > p( temp );
	pointer = nullptr;
	return p;
}


} // namespace
#endif // _H_EBCL_INLINE_POINTERS