/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016 Raytrix GmbH. All rights reserved.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma once

#include "DLLInterface.h"

#include "ParametersPimpl.h"

#include "Rx.Core.Ex/RxImage.h"
#include "Rx.Core.Ex/RxArrayDouble.h"
#include "Rx.Core.Ex/RxArray.h"
#include "Rx.Core.Ex/RxArray2DDouble.h"
#include "Rx.Core.Ex/RxMapping3D.h"

#include "Rx.LF/ESpace.h"

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// namespace: Rx.LFR
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Rx
{
	namespace LFR
	{
		class CCalibration_Impl;

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Defines a single camera or ray image calibration, including a possible gray image.
		/// </summary>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		class RX_API CCalibration : public CParametersPimpl<CCalibration_Impl, Params::ECalib::ID, Interfaces::ECalib::ID>
		{
		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Default constructor. Creates an uninitialized calibration. Call SetToDefault to initialize it.
			/// </summary>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CCalibration();

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Move constructor.
			/// </summary>
			///
			/// <param name="xCalibration"> [in,out]The calibration instance. </param>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CCalibration(CCalibration&& xCalibration);

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Destructor. Frees all used memory including the gray image.
			/// </summary>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			~CCalibration();

		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Sets this calibration to default. All applied parameters are reset. The gray image is not affected.
			/// </summary>
			///
			/// <param name="iHeightPX"> The height of the camera sensor in pixels. </param>
			/// <param name="iWidthPX">  The width of the camera sensor in pixels. </param>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void SetToDefault(int iHeightPX, int iWidthPX);

		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Sets the camera format by pointer. Only for internal purposes.
			/// </summary>
			///
			/// <param name="pvData"> [in] The pointer to the camera format to set. </param>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void SetCameraFormat(const void* pvData);

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Gets the pointer to the internal camera format. Only for internal purposes.
			/// </summary>
			///
			/// <returns> The pointer to the internal camera format. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			const void* GetCameraFormat() const;

		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Queries if this calibration has a gray image.
			/// </summary>
			///
			/// <returns> True if this calibration has a gray image, false if not. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			bool HasGrayImage() const;

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Gets the gray image. The gray image is invalid if this calibration has no gray image.
			/// </summary>
			///
			/// <returns> The gray image. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			const CRxImage& GetGrayImage() const;

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Gets the gray image. The gray image is invalid if this calibration has no gray image.
			/// </summary>
			///
			/// <returns> The gray image. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CRxImage& GetGrayImage();

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Sets the given image as the gray image. This copies the given image into this calibration.
			/// </summary>
			///
			/// <param name="pxImg"> The image. </param>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void SetGrayImage(const Interop::Runtime28::IImage* pxImg);

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Sets the given image as the gray image. This copies the given image into this calibration.
			/// </summary>
			///
			/// <param name="xImg"> The gray image. </param>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void SetGrayImage(const CRxImage& xImg);

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Sets the given image as the gray image. This moves the given image into this calibration.
			/// </summary>
			///
			/// <param name="xImg"> The gray image. Is invalid after this call. </param>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void SetGrayImage(CRxImage&& xImg);

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Query if this object has a depth correction volume.
			/// </summary>
			///
			/// <returns> True if there is a depth correction volume, false if not. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			bool HasDepthCorrectionVolume() const;

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Gets the depth correction volume.
			/// </summary>
			///
			/// <returns> The depth correction volume. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			const CRxMapping3D& GetDepthCorrectionVolume() const;

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Gets the depth correction volume.
			/// </summary>
			///
			/// <returns> The depth correction volume. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CRxMapping3D& GetDepthCorrectionVolume();

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Sets the depth correction volume. This moves the given mapping into this calibration.
			/// </summary>
			///
			/// <param name="xMapping"> [in] The mapping is invalid after this call. </param>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void SetDepthCorrectionVolume(CRxMapping3D&& xMapping);

		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Projects the given source depth value into the given target space.
			/// </summary>
			///
			/// <param name="dSrcDepth"> The source depth value in millimeter or virtual depths. </param>
			/// <param name="eSrcSpace"> The source space. </param>
			/// <param name="bSrcVD">    True if the source depth value is given in virtual depths. Is ignored if the source space isn't
			/// 						 virtual. </param>
			/// <param name="eTrgSpace"> The target space. </param>
			/// <param name="bTrgVD">    True if the target depth value should be given in virtual depths. Is ignored if the target space
			/// 						 isn't virtual. </param>
			///
			/// <returns> The resultant target depth value in millimeter or virtual depths. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			double ProjectDepth(double dSrcDepth, LF::ESpace::ID eSrcSpace, bool bSrcVD, LF::ESpace::ID eTrgSpace, bool bTrgVD) const;

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Projects an array of points from the given source space into the given target space.
			/// </summary>
			///
			/// <param name="adTrgPoints"> [out] The array of projected points. </param>
			/// <param name="adSrcPoints"> The array of points to project. </param>
			/// <param name="eTrgSpace">   The target space. </param>
			/// <param name="eSrcSpace">   The source space. </param>
			/// <param name="xFormat">	   The image format of the source and/or target space. Only required if the space is a view. This
			/// 						   parameter limits this function to allow only projections from an image format to the same image
			/// 						   format. </param>
			///
			/// <returns> True if all projections succeeded, false if at least one projection failed (out of sensor or image). </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			bool Project(CRxArrayDouble& adTrgPoints, const CRxArrayDouble& adSrcPoints, LF::ESpace::ID eTrgSpace, LF::ESpace::ID eSrcSpace,
					const CRxImageFormat& xFormat) const;

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Projects the given 3D point given in virtual space in sensor frame in millimeters through all suitable micro lenses.
			/// </summary>
			///
			/// <param name="adRawPoints"> [out] The projected points. This array contains three entries per projected point. The first
			/// 						   and the second entry are the X and the Y coordinates of the projected point given in pixels in
			/// 						   the raw image. The third entry is the corresponding lens type. </param>
			/// <param name="dSrcX">	   The X coordinate of the source point in virtual space in millimeters. </param>
			/// <param name="dSrcY">	   The Y coordinate of the source point in virtual space in millimeters. </param>
			/// <param name="dSrcZ">	   The Z coordinate of the source point in virtual space in millimeters. </param>
			/// <param name="iRadiusLU">   The radius given in lens units that is used to find the suitable micro lenses. A radius of 2 or
			/// 						   3 lens units is recommended. </param>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void GetRawImagePointsFromVirtualMM_s(CRxArray<double>& adRawPoints, double dSrcX, double dSrcY, double dSrcZ, int iRadiusLU) const;

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Projects the given 3D point given in virtual space in sensor frame in millimeters through all suitable micro lenses.
			/// </summary>
			///
			/// <param name="adRawPoints">						 [out] The projected points. This array contains three entries per projected
			/// 												 point. The first and the second entry are the X and the Y coordinates of
			/// 												 the projected point given in pixels in the raw image. The third entry is
			/// 												 the corresponding lens type. </param>
			/// <param name="dSrcX">							 The X coordinate of the source point in virtual space in millimeters. </param>
			/// <param name="dSrcY">							 The Y coordinate of the source point in virtual space in millimeters. </param>
			/// <param name="dSrcZ">							 The Z coordinate of the source point in virtual space in millimeters. </param>
			/// <param name="iRadiusLU">						 The radius given in lens units that is used to find the suitable micro
			/// 												 lenses. A radius of 2 or 3 lens units is recommended. </param>
			/// <param name="iMinAllowedDistanceToLensBorderPX"> The minimal allowed distance to lens border in pixels. </param>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void GetRawImagePointsFromVirtualMM_s(CRxArray<double>& adRawPoints, double dSrcX, double dSrcY, double dSrcZ, int iRadiusLU, int iMinAllowedDistanceToLensBorderPX) const;

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Projects from the given pixel position in the raw image with the given standard depth through the micro lenses into
			/// 	virtual space.
			/// </summary>
			///
			/// <param name="dTrgX"> [in,out] The X coordinate of the target point in virtual space in mm. </param>
			/// <param name="dTrgY"> [in,out] The Y coordinate of the target point in virtual space in mm. </param>
			/// <param name="dTrgZ"> [in,out] The Z coordinate of the target point in virtual space in mm. </param>
			/// <param name="dSrcX"> The X coordinate of the source point in pixels in raw image format. </param>
			/// <param name="dSrcY"> The Y coordinate of the target point in pixels in raw image format. </param>
			/// <param name="dSrcZ"> The Z coordinate of the target point in standard depths (Between 0.5 and 1.0) </param>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void GetVirtualMM_sFromRawImagePoint(double& dTrgX, double& dTrgY, double& dTrgZ, double dSrcX, double dSrcY,  double dSrcZ) const;

		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Gets the grid data of this calibration.
			///
			/// 	The grid data is a 2D vector of lens information. For each lens on the grid exists a four component entry with the
			/// 	following values:
			///
			/// 	- 1. X position of the lens center given in pixels on the PXA
			/// 	- 2. Y position of the lens center given in pixels on the PXA
			/// 	- 3. The effective radius of the lens
			/// 	- 4. The lens type of the lens.
			/// </summary>
			///
			/// <param name="adLensData2D"> [out] The 2D lens information vector. </param>
			/// <param name="bMLI">		    True to get the MLI grid. False to get the MLA grid. </param>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void GetGridData(CRxArray2DDouble& adLensData2D, bool bMLI) const;

		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Saves some details of this calibration as a XML file.
			/// </summary>
			///
			/// <param name="sxFilename"> The file name. </param>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void SaveAsXmlFile(const CRxString& sxFilename);

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Saves some details of this calibration as a XML string.
			/// </summary>
			///
			/// <param name="sxXml"> [out] The XML string. </param>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void SaveAsXmlString(CRxString& sxXml);

		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 	Move assignment operator.
			/// </summary>
			///
			/// <param name="xCalibration"> [in,out] The calibration instance. </param>
			///
			/// <returns> The instance. </returns>
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CCalibration& operator=(CCalibration&& xCalibration);
		};
	}
}
#pragma make_public(Rx::LFR::CCalibration)
