/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2015 Raytrix GmbH. All rights reserved.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma once

#include "RxCore.h"
#include "RxString.h"
#include "CodeLocation.h"
#include "RxExceptionDefines.h"
#include "ExceptionGUIDs.h"

#include "Rx.Interop.Runtime30/Guid.h"
#include "Rx.Interop.Runtime30/CIException.h"
#include "Rx.Interop.Runtime31/IException.h"

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// namespace: Rx
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Rx
{
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 	Container for an Interop::Runtime31::IException pointer that simplifies copying of exceptions.
	/// </summary>
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	class RXCORE_API CExceptionSafePointer
	{
	public:

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Default constructor.
		/// </summary>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CExceptionSafePointer();

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Copy constructor. Creates a copy of the exception provided by the given pointer.
		/// </summary>
		///
		/// <param name="xExceptionSafePointer"> The exception safe pointer to copy. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CExceptionSafePointer(const CExceptionSafePointer& xExceptionSafePointer);

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Move constructor.
		/// </summary>
		///
		/// <param name="xExceptionSafePointer"> [in,out] The exception safe pointer to move. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CExceptionSafePointer(CExceptionSafePointer&& xExceptionSafePointer);

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Copy Constructor. Creates a copy of the given exception.
		/// </summary>
		///
		/// <param name="ex">	The exception to copy. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CExceptionSafePointer(const IException30& ex);

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Copy Constructor. Creates a copy of the given exception.
		/// </summary>
		///
		/// <param name="ex">	The exception to copy. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CExceptionSafePointer(const IException31& ex);

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Move assignment operator.
		/// </summary>
		///
		/// <param name="xExceptionSafePointer"> [in,out] The exception safe pointer to move. </param>
		///
		/// <returns> A moved version of the given pointer. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CExceptionSafePointer& operator=(CExceptionSafePointer&& xExceptionSafePointer);

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Destructor. Deletes the assigned exception.
		/// </summary>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		~CExceptionSafePointer();

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the exception pointer.
		/// </summary>
		///
		/// <returns> Null if it fails, else the exception pointer. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		const IException31* Get() const;

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the exception pointer dynamically casted to the given template argument.
		/// </summary>
		///
		/// <typeparam name="TException"> Type of the exception. </typeparam>
		///
		/// <returns> Null if it fails, else the exception pointer. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		template<typename TException>
		const TException* Get() const
		{
			return dynamic_cast<TException*>(m_pxException);
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Query if this object is valid.
		/// </summary>
		///
		/// <returns> True if valid, false if not. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		bool IsValid() const;

	private:

		/// <summary> The exception pointer. </summary>
		IException31* m_pxException;
	};

	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 	Raytrix exception base class. All exceptions are based on this class.
	/// </summary>
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	class RXCORE_API CRxException : public IException31
	{
	public:

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Constructor.
		/// </summary>
		///
		/// <param name="sxMsg">  The message string. </param>
		/// <param name="pcFile"> The file. </param>
		/// <param name="pcFunc"> The function. </param>
		/// <param name="iLine">  The line. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CRxException(const CRxString& sxMsg, const char* pcFile, const char* pcFunc, int iLine);

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Constructor.
		/// </summary>
		///
		/// <param name="sxMsg">  The message string. </param>
		/// <param name="pcFile"> The file. </param>
		/// <param name="pcFunc"> The function. </param>
		/// <param name="iLine">  The line. </param>
		/// <param name="ex">	  The inner exception. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CRxException(const CRxString& sxMsg, const char* pcFile, const char* pcFunc, int iLine, const IException30& ex);

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Constructor.
		/// </summary>
		///
		/// <param name="sxMsg">  The message string. </param>
		/// <param name="pcFile"> The file. </param>
		/// <param name="pcFunc"> The function. </param>
		/// <param name="iLine">  The line. </param>
		/// <param name="ex">	  The inner exception. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CRxException(const CRxString& sxMsg, const char* pcFile, const char* pcFunc, int iLine, const IException31& ex);

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Virtual destructor.
		/// </summary>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual ~CRxException()
		{
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RX_EXCEPTION_BASE_IMPLEMENTATION(CRxException, "Rx.RxException", "{491260D2-0D75-4CCD-A2C5-46428B450B66}");

	public:

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Sets the GUID of this exception. This is not guaranteed to be the GUID of this exception type.
		/// </summary>
		///
		/// <param name="xGUID"> Unique identifier. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void SetGUID(const CGuid30& xGUID)
		{
			m_xGUID = xGUID;
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Sets the given exception as the inner exception.
		/// </summary>
		///
		/// <param name="ex"> The exception. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void SetInnerException(const IException31& ex)
		{
			m_xInnerException = ex;
		}

	public:

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Sets additional exception data.
		/// </summary>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual void SetExceptionData();

	public:

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Convert this exception into a string representation.
		/// </summary>
		///
		/// <returns> This exception as a string. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual CRxString ToString() const;

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Convert this exception into a string representation.
		/// </summary>
		///
		/// <param name="bIncludeInner"> True to include all inner exceptions. </param>
		///
		/// <returns> This exception as a string. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CRxString ToString(bool bIncludeInner) const;

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the most inner exception.
		/// </summary>
		///
		/// <returns> The most inner exception. If there is no inner exception, this exception is returned. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		const IException31* GetMostInnerException() const;

	public:

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets message text.
		/// </summary>
		///
		/// <returns> Null if it fails, else the message text. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual const char* GetMessageText() const;

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the unique identifier of this exception. This is not guaranteed to be the GUID of this exception type.
		/// </summary>
		///
		/// <returns>	The unique identifier. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual const CGuid30& GetGUID() const;

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the name of this exception.
		/// </summary>
		///
		/// <returns>	The name of this exception. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual const char* GetName() const;

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the function name.
		/// </summary>
		///
		/// <returns>	The function name. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual const char* GetFunction() const;

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the file name.
		/// </summary>
		///
		/// <returns>	The file name. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual const char* GetFile() const;

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the line.
		/// </summary>
		///
		/// <returns>	The line number. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual int GetLine() const;

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the inner exception if one is available. Otherwise nullptr is returned.
		/// </summary>
		///
		/// <returns> The inner exception. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual const IException31* GetInnerException() const;

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Query if this exception or one of its inner exceptions contains an exception of the given type.
		/// </summary>
		///
		/// <typeparam name="TException"> The exception type to test for. </typeparam>
		///
		/// <returns>
		/// 	True if this exception or one of its inner exceptions contains an exception of the given type, false if not.
		/// </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		template<class TException> bool HasException() const
		{
			return HasException(TException::GetTypeGUID());
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Query if this exception or one of its inner exceptions contains an exception of the given GUID.
		/// </summary>
		///
		/// <param name="xGUID"> [in] The GUID of the exception to look for. </param>
		///
		/// <returns>
		/// 	True if this exception or one of its inner exceptions contains an exception of the given GUID, false if not.
		/// </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual bool HasException(const CGuid30& xGUID) const;

	protected:

		CGuid30 m_xGUID;
		CRxString m_sxName;
		CRxString m_sxMessage;
		CCodeLocation m_xLocation;
		CExceptionSafePointer m_xInnerException;
	};
}

#pragma make_public(Rx::CRxException)
