/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016 Raytrix GmbH. All rights reserved.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "DLLInterface.h"

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// namespace: Rx.LFR
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Rx
{
	namespace LFR
	{
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	The PIMPL base class. Creates, holds and destroys an instance of the given generic type parameter.
		/// </summary>
		///
		/// <typeparam name="TImpl">		  Generic type parameter. </typeparam>
		/// <typeparam name="TEnumInterface"> The enum type of the interface ID. </typeparam>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		template<class TImpl, class TEnumInterface>
		class RX_API CPimpl
		{
		protected:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Protected constructor. Creates the implementation instance.
			/// </summary>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CPimpl()
				: m_pxImpl(new TImpl())
			{
				m_bFreeOnDelete = true;
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Protected constructor. Takes the ownership of the implementation instance.
			/// </summary>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CPimpl(TImpl* pxImpl, bool bFreeOnDelete)
				: m_pxImpl(pxImpl)
			{
				m_bFreeOnDelete = bFreeOnDelete;
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Protected destructor. Deletes the implementation instance.
			/// </summary>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			~CPimpl()
			{
				if (m_bFreeOnDelete)
				{
					// It's safe to delete a null pointer
					delete m_pxImpl;
				}

				m_pxImpl = nullptr;
			}

		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Gets the non constant implementation.
			/// </summary>
			///
			/// <returns> The implementation. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			TImpl& GetImpl()
			{
				return *m_pxImpl;
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Gets the constant implementation.
			/// </summary>
			///
			/// <returns> The implementation. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			const TImpl& GetImpl() const
			{
				return *m_pxImpl;
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Gets the interface defined by the given interface ID.
			/// </summary>
			///
			/// <param name="eInterface"> The interface ID. </param>
			///
			/// <returns> Null if it fails, else the interface. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void* GetInterface(TEnumInterface eInterface);

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Gets the interface defined by the given interface ID.
			/// </summary>
			///
			/// <param name="eInterface"> The interface ID. </param>
			///
			/// <returns> Null if it fails, else the interface. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			const void* GetInterface(TEnumInterface eInterface) const;

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Gets the interface defined by the given interface ID.
			/// </summary>
			///
			/// <typeparam name="TInterface"> The interface type. </typeparam>
			/// <param name="eInterface"> The interface. </param>
			///
			/// <returns> Null if it fails, else the interface. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			template<typename TInterface>
			TInterface* GetInterface(TEnumInterface eInterface)
			{
				return static_cast<TInterface*>(GetInterface(eInterface));
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Gets the interface defined by the given interface ID.
			/// </summary>
			///
			/// <typeparam name="TInterface"> The interface type. </typeparam>
			/// <param name="eInterface"> The interface ID. </param>
			///
			/// <returns> Null if it fails, else the interface. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			template<typename TInterface>
			const TInterface* GetInterface(TEnumInterface eInterface) const
			{
				return static_cast<const TInterface*>(GetInterface(eInterface));
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Queries if this object has the given interface.
			/// </summary>
			///
			/// <param name="eInterface"> The interface to query. </param>
			///
			/// <returns> True if this class has the given interface, false if not. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			bool HasInterface(TEnumInterface eInterface)
			{
				return GetInterface(eInterface) != nullptr;
			}

		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Move constructor.
			/// </summary>
			///
			/// <param name="xPimpl"> [in,out] The pimpl to move. Gets invalid after this call. </param>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CPimpl(CPimpl<TImpl, TEnumInterface>&& xPimpl)
			{
				// Get implementation pointer
				m_pxImpl = xPimpl.m_pxImpl;

				// Reset the other instance
				xPimpl.m_pxImpl = nullptr;
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Move assignment operator.
			/// </summary>
			///
			/// <param name="xPimpl"> [in,out] The pimpl to move. Gets invalid after this call. </param>
			///
			/// <returns> This instance. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CPimpl& operator=(CPimpl<TImpl, TEnumInterface>&& xPimpl)
			{
				// Get implementation pointer
				m_pxImpl = xPimpl.m_pxImpl;

				// Reset the other instance
				xPimpl.m_pxImpl = nullptr;
				return *this;
			}

		private:

			/// <summary> The constant pointer to the non constant implementation instance. </summary>
			TImpl* m_pxImpl;
			/// <summary> true to free on delete. </summary>
			bool m_bFreeOnDelete;

		private:

			CPimpl(const CPimpl&);	// Forbidden
			CPimpl& operator=(const CPimpl& xPimpl);// Forbidden
		};
	}
}
