/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016 Raytrix GmbH. All rights reserved.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "Rx.Core/RxString.h"

#ifndef RX_STATIC
    #ifdef RXCOREEX_EXPORTS
		#define RXCOREEX_API __declspec(dllexport)
    #else
		#define RXCOREEX_API __declspec(dllimport)
    #endif
#else
	#define RXCOREEX_API
#endif

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// namespace: Rx
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Rx
{
	/**
	\addtogroup RxCore_Mapping
	**/

	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 	The mapping 3D. Uses a 3D volume to map a 3d Point to another 3d Point.
	///
	/// 						Dimension X
	/// 		Min Position *_________________
	/// 					/|				  /
	/// 				   / |				 /|
	/// 				  /__|______________/ |Dimension Y
	/// 				  |	 |				| |
	/// 				  |	 |______________|_|
	/// 				  |  /				| /
	/// 	  Dimension Z | /			    |/
	/// 				  |/________________* MaxPosition.
	/// </summary>
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	class RXCOREEX_API CRxMapping3D
	{
	public:

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Default constructor. Creates an invalid mapping without using any memory.
		/// </summary>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CRxMapping3D();

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Copy constructor. Creates a copy of the given image.
		/// </summary>
		///
		/// <param name="xMapping"> The mapping to copy. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CRxMapping3D(const CRxMapping3D& xMapping);

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Move constructor.
		/// </summary>
		///
		/// <param name="xMapping"> [in] The mapping to move. This is invalid after the call. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CRxMapping3D(CRxMapping3D&& xMapping);

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Destructor. All used memory is freed.
		/// </summary>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual ~CRxMapping3D();

	public:

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Assignment operator. Copies the contents of the given mapping.
		/// </summary>
		///
		/// <param name="xMapping"> The mapping. </param>
		///
		/// <returns> A shallow copy of this object. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CRxMapping3D& operator=(const CRxMapping3D& xMapping);

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Move assignment operator.
		/// </summary>
		///
		/// <param name="xMapping"> [in] The mapping to move. This is invalid after the call. </param>
		///
		/// <returns> A shallow copy of this object. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CRxMapping3D& operator=(CRxMapping3D&& xMapping);

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Copies the content of the given mapping data into this mapping.
		/// </summary>
		///
		/// <param name="pfData"> The mapping volume data to copy. </param>
		///
		/// <returns> True if it succeeds, false if it fails. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		bool Set(const float* pfData);

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Creates a copy of the given mapping. Only (re)allocates memory if required.
		/// </summary>
		///
		/// <param name="xMapping"> The mapping to copy. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void Create(const CRxMapping3D& xMapping);

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Creates this mapping by moving the data of the given mapping into this mapping.
		/// </summary>
		///
		/// <param name="xMapping"> [in,out] The mapping to move. Is invalid after this call. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void Create(CRxMapping3D&& xMapping);

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Create a mapping of given size and reserve the appropriate amount of memory.
		/// </summary>
		///
		/// <param name="fMinPositionX"> The min position X. </param>
		/// <param name="fMinPositionY"> The min position Y. </param>
		/// <param name="fMinPositionZ"> The min position Z. </param>
		/// <param name="fMaxPositionX"> The max position X. </param>
		/// <param name="fMaxPositionY"> The max position Y. </param>
		/// <param name="fMaxPositionZ"> The max position Z. </param>
		/// <param name="iDimensionX">   The X dimension. </param>
		/// <param name="iDimensionY">   The Y dimension. </param>
		/// <param name="iDimensionZ">   The Z dimension. </param>
		/// <param name="pfData">		 Information describing the mapping volume. </param>
		///
		/// <returns> True if it succeeds, false if it fails. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		bool Create(float fMinPositionX, float fMinPositionY, float fMinPositionZ, float fMaxPositionX, float fMaxPositionY, float fMaxPositionZ, int iDimensionX, int iDimensionY, int iDimensionZ, const float* pfData);

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Query if this object contains the given fPointX.
		/// </summary>
		///
		/// <param name="fPointX"> The point x coordinate. </param>
		/// <param name="fPointY"> The point y coordinate. </param>
		/// <param name="fPointZ"> The point z coordinate. </param>
		///
		/// <returns> True if the object is in this collection, false if not. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		bool Contains(float fPointX, float fPointY, float fPointZ);

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Maps the given point to another point.
		/// </summary>
		///
		/// <param name="fPointX"> [out] The point x coordinate. </param>
		/// <param name="fPointY"> [out] The point y coordinate. </param>
		/// <param name="fPointZ"> [out] The point z coordinate. </param>
		///
		/// <returns> True if it succeeds, false if it fails. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		bool Map(float& fPointX, float& fPointY, float& fPointZ);

	public:

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Destroys this mapping volume if one has been created. Resets all internal variables.
		/// </summary>
		///
		/// <returns> True if it succeeds, false if it fails. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual bool Destroy();

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Query if this mapping volume is valid.
		/// </summary>
		///
		/// <returns> True if valid, false if not. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual bool IsValid() const
		{
			return m_pfData ? true : false;
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the number of bytes of the mapping volume.
		/// </summary>
		///
		/// <returns> The byte count. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual unsigned GetByteCount() const
		{
			return sizeof(float) * m_iDimensionX * m_iDimensionY * m_iDimensionZ;
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Return the pointer to the mapping data.
		/// </summary>
		///
		/// <returns> Null if it fails, else the data pointer. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual float* GetDataPtr()
		{
			return m_pfData;
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Return the constant pointer to the mapping data.
		/// </summary>
		///
		/// <returns> Null if it fails, else the data pointer. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		virtual const float* GetDataPtr() const
		{
			return m_pfData;
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Function call operator.
		/// </summary>
		///
		/// <param name="fPointX"> The position of the x cell. </param>
		/// <param name="fPointY"> The position of the y cell. </param>
		/// <param name="fPointZ"> The position of the z cell. </param>
		///
		/// <returns> The result of the operation. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		float& operator()(float fPointX, float fPointY, float fPointZ)
		{
			// Compute the indices for the array usage
			int iX = int((fPointX - m_fMinPositionX) / m_fCellSizeX);
			int iY = int((fPointY - m_fMinPositionY) / m_fCellSizeY);
			int iZ = int((fPointZ - m_fMinPositionZ) / m_fCellSizeZ);

			return m_pfData[iZ * (m_iDimensionX * m_iDimensionY) + iX * m_iDimensionY + iY];
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Sets the mapping to zero zero.
		/// </summary>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void SetZero();

	public:

		/// <summary> The depth correction volume data. </summary>
		float* m_pfData;

		/// <summary> The min position x component. </summary>
		float m_fMinPositionX;

		/// <summary> The min position y component. </summary>
		float m_fMinPositionY;

		/// <summary> The min position z component. </summary>
		float m_fMinPositionZ;

		/// <summary> The max position x component. </summary>
		float m_fMaxPositionX;

		/// <summary> The max position y component. </summary>
		float m_fMaxPositionY;

		/// <summary> The max position z component. </summary>
		float m_fMaxPositionZ;

		/// <summary> The dimension x component. </summary>
		int m_iDimensionX;

		/// <summary> The dimension y component. </summary>
		int m_iDimensionY;

		/// <summary> The dimension z component. </summary>
		int m_iDimensionZ;

		/// <summary> The cell size in x direction. </summary>
		float m_fCellSizeX;

		/// <summary> The cell size in y direction. </summary>
		float m_fCellSizeY;

		/// <summary> The cell size in z direction. </summary>
		float m_fCellSizeZ;

		/// <summary> Full pathname of the file. </summary>
		CRxString m_sxFilePath;
	};

	/// @}
}

#pragma make_public(Rx::CRxMapping3D)
