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

#pragma once

#include "glmCoreAPI.h"
#include "glmAssert.h"
#include "glmMemory.h"

namespace glm
{
	/**
	* \brief A pointer class implementing a particular copy-on-write strategy using the clone() method for copies.
	*
	* \ingroup genericPatterns
	* \author Golaem <dev@golaem.com>
	*
	* \param TPointeeType The pointee type which must implement a clone method.
	*/
	template< class TPointeeType > 
	class CloneOnWritePointer
	{
	public:

		GLM_CLASS_STAT_ID( glm::MemoryStatIds::GolaemShared)

		/**
		* \brief Default constructor
		*/
		inline CloneOnWritePointer() ;

		/**
		* \brief Constructor
		*/
		inline CloneOnWritePointer( const TPointeeType * pointer ) ;

		/**
		* \brief Copy constructor
		*/
		inline CloneOnWritePointer( const CloneOnWritePointer<TPointeeType> & pointer ) ;

		/**
		* \brief Generic copy constructor
		*/
		template <class TOtherPointeeType> 
		inline CloneOnWritePointer( const CloneOnWritePointer<TOtherPointeeType>& pointer ) ;

		/**
		* \brief Virtual destructor
		*/
		inline virtual ~CloneOnWritePointer() ;

		/**
		* \brief Assignment operator
		*
		* \param pointer value to be assigned
		* \return the assigned SmartPointer
		*/
		inline const CloneOnWritePointer<TPointeeType> & operator=( const CloneOnWritePointer<TPointeeType> & pointer ) ;

		/**
		* \brief Generic assignment operator
		*
		* \param pointer value to be assigned
		* \return the assigned SmartPointer
		*/ 
		template <class TOtherPointeeType>
		inline const CloneOnWritePointer<TPointeeType> & operator=( const CloneOnWritePointer<TOtherPointeeType> & pointer ) ;

		/**
		* \brief Read access to the pointee.
		*/
		inline const TPointeeType * getPointee() const;

		/**
		* \brief Read/write access to the pointee (this method cause the pointee to be cloned).
		*/
		inline TPointeeType * touchPointee() ;

		/**
		* \brief user-defined conversion to TPointeeType*
		*/
		inline operator TPointeeType*() ;

		/**
		* \brief added conversion to void*, to prevent delete from compiling and becomes ambiguous
		*/
		inline operator void*() ;
	private:
		const TPointeeType * _originalPointee;
		TPointeeType * _touchedPointee;
	};

	//*****************************************************************************
	// CloneOnWritePointer inline definitions
	//*****************************************************************************

	//-----------------------------------------------------------------------------
	// Constructor
	//-----------------------------------------------------------------------------
	template< class TPointeeType > 
	CloneOnWritePointer<TPointeeType>::CloneOnWritePointer()
		:_originalPointee(NULL)
		,_touchedPointee(NULL)
	{

	}

	//-----------------------------------------------------------------------------
	// Constructor from a pointer
	//-----------------------------------------------------------------------------
	template< class TPointeeType > 
	CloneOnWritePointer<TPointeeType>::CloneOnWritePointer( const TPointeeType * pointer )
		:_originalPointee(pointer)
		,_touchedPointee(NULL)
	{
	}

	//-----------------------------------------------------------------------------
	// Copy constructor
	//-----------------------------------------------------------------------------
	template< class TPointeeType > 
	CloneOnWritePointer<TPointeeType>::CloneOnWritePointer( const CloneOnWritePointer<TPointeeType> & pointer )
		:_originalPointee(NULL)
		,_touchedPointee(NULL)
	{
		(*this) = pointer;
	}

	//-----------------------------------------------------------------------------
	// Generic copy constructor
	//-----------------------------------------------------------------------------
	template< class TPointeeType > 
	template <class TOtherPointeeType> 
	CloneOnWritePointer<TPointeeType>::CloneOnWritePointer( const CloneOnWritePointer<TOtherPointeeType>& pointer )
		:_originalPointee(NULL)
		,_touchedPointee(NULL)
	{
		(*this) = pointer;
	}

	//-----------------------------------------------------------------------------
	// Virtual destructor
	//-----------------------------------------------------------------------------
	template< class TPointeeType > 
	CloneOnWritePointer<TPointeeType>::~CloneOnWritePointer()
	{
		_originalPointee = NULL;
		if (_touchedPointee != NULL)
		{
			GLM_SAFE_DELETE(_touchedPointee);
		}
	}

	//-----------------------------------------------------------------------------
	// Assignment operator
	//-----------------------------------------------------------------------------
	template< class TPointeeType > 
	const CloneOnWritePointer<TPointeeType> & CloneOnWritePointer<TPointeeType>::operator=( const CloneOnWritePointer<TPointeeType> & pointer )
	{
		if (this != &pointer)
		{
			_originalPointee = pointer.getPointee();
			if (_touchedPointee != NULL)
			{
				GLM_SAFE_DELETE(_touchedPointee);
			}
		}
		return *this;
	}

	//-----------------------------------------------------------------------------
	// Generic assignment operator
	//-----------------------------------------------------------------------------
	template< class TPointeeType > 
	template< class TOtherPointeeType > 
	const CloneOnWritePointer<TPointeeType> & CloneOnWritePointer<TPointeeType>::operator=( const CloneOnWritePointer<TOtherPointeeType> & pointer )
	{
		_originalPointee = pointer.getPointee();
		if (_touchedPointee != NULL)
		{
			GLM_SAFE_DELETE(_touchedPointee);
		}
		return *this;
	}

	//-----------------------------------------------------------------------------
	// getPointee
	//-----------------------------------------------------------------------------
	template< class TPointeeType > 
	const TPointeeType * CloneOnWritePointer<TPointeeType>::getPointee() const
	{
		if (_touchedPointee != NULL)
		{
			return _touchedPointee;
		}
		else
		{
			return _originalPointee;
		}
	}

	//-----------------------------------------------------------------------------
	// touchPointee
	//-----------------------------------------------------------------------------
	template< class TPointeeType > 
	TPointeeType * CloneOnWritePointer<TPointeeType>::touchPointee()
	{
		if (_touchedPointee != NULL)
		{
			return _touchedPointee;
		}
		else
		{
			if (_originalPointee != NULL)
			{
				_touchedPointee = dynamic_cast<TPointeeType *>(_originalPointee->clone());
				GLM_ASSERT(_touchedPointee != NULL);
			}
			return _touchedPointee;
		}
	}

	//-----------------------------------------------------------------------------
	// transtyping operator to TPointeeType *
	//-----------------------------------------------------------------------------
	template< class TPointeeType > 
	CloneOnWritePointer<TPointeeType>::operator TPointeeType*()
	{
		return touchPointee();
	}

	//-----------------------------------------------------------------------------
	// transtyping operator to void *
	//-----------------------------------------------------------------------------
	template< class TPointeeType > 
	CloneOnWritePointer<TPointeeType>::operator void*()
	{
		return touchPointee();
	}
}

