/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016 Raytrix GmbH. All rights reserved.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "DLLInterface.h"
#include "ParametersPimpl.h"

#include "Rx.Core/RxString.h"

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// namespace: Rx.LFR
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Rx
{
	namespace LFR
	{
		class CCudaDevice_Impl;

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Provides methods for interacting with a certain CUDA device.
		/// </summary>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		class RX_API CCudaDevice : public CParametersPimpl<CCudaDevice_Impl, Params::ECudaDevice::ID, Interfaces::ECudaDevice::ID>
		{
		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Constructor. Only for internal purposes.
			/// </summary>
			///
			/// <param name="pxImpl"> [out] The implementation class. </param>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CCudaDevice(CCudaDevice_Impl* pxImpl);

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Move constructor.
			/// </summary>
			///
			/// <param name="xCudaDevice"> [in,out] The cuda device instance. </param>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CCudaDevice(CCudaDevice&& xCudaDevice);

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Destructor.
			/// </summary>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			~CCudaDevice();

		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Queries the free and the total amount of available device memory in bytes.
			/// </summary>
			///
			/// <param name="nFree">  [out] Free device memory in bytes. </param>
			/// <param name="nTotal"> [out] Total device memory in bytes. </param>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void QueryMemoryInfo(size_t& nFree, size_t& nTotal) const;

		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Gets the compute capability of this CUDA device. The minimum compute capability is 3.0.
			/// </summary>
			///
			/// <param name="uMajor"> [out] The major part of the compute capability version. </param>
			/// <param name="uMinor"> [out] The minor part of the compute capability version. </param>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void GetComputeCapability(unsigned& uMajor, unsigned& uMinor) const;

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Queries if this CUDA device supports the given compute capability.
			/// </summary>
			///
			/// <param name="uMajor"> The major part of the compute capability version. </param>
			/// <param name="uMinor"> The minor part of the compute capability version. </param>
			///
			/// <returns> True if this CUDA device supports the given compute capability, false if not. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			bool HasComputeCapability(unsigned uMajor, unsigned uMinor) const;

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Gets the ID of this CUDA device.
			/// </summary>
			///
			/// <returns> The internal CUDA device ID. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			int GetDeviceID() const;

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Makes this CUDA device current for the calling thread.
			/// </summary>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void MakeCurrent() const;

		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Locks this CUDA device. This waits until locked or until the timeout has been expired.
			/// </summary>
			///
			/// <param name="iTimeout"> (optional) Timeout in milliseconds. -1 will wait infinetly. </param>
			///
			/// <returns> True if it succeeds, false on timeout. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			bool Lock(int iTimeout = -1) const;

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Unlocks this CUDA device. Does nothing if not locked by the calling thread.
			/// </summary>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void Unlock() const;

		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Move assignment operator.
			/// </summary>
			///
			/// <param name="xCudaDevice"> [in,out] The cuda device instance. </param>
			///
			/// <returns> The instance. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CCudaDevice& operator=(CCudaDevice&& xCudaDevice);
		};
	}
}
