/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2013 Raytrix GmbH. All rights reserved.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma once

#ifndef RX_STATIC
    #ifdef RXCOREEX_EXPORTS
		#define RXCOREEX_API __declspec(dllexport)
    #else
		#define RXCOREEX_API __declspec(dllimport)
    #endif
#else
	#define RXCOREEX_API
#endif

#include "Rx.Interop.Runtime28/RxIfcImage.h"
#include "Rx.Core/RxString.h"

#include <stdio.h>
#include "Rx.Core/RxException.h"

// Define CUDA macros if compiled with CUDA compiler
#ifdef __CUDACC__
    #ifndef RX_CUDA_EXP
		#define RX_CUDA_EXP __host__ __device__ __forceinline__
    #endif
#else
    #ifndef RX_CUDA_EXP
		#define RX_CUDA_EXP
    #endif
#endif

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// namespace: Rx
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Rx
{
	/**
	\addtogroup RxCore_Image
	**/
	/// @{

	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 	Image format descriptor.
	/// </summary>
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	class RXCOREEX_API CRxImageFormat
	{
	public:

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Default constructor.
		/// </summary>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RX_CUDA_EXP CRxImageFormat()
		{
			#if !defined(__CUDACC__)
				m_iWidth     = 0;
				m_iHeight    = 0;
				m_ePixelType = Interop::Runtime28::EPixelType::None;
				m_eDataType  = Interop::Runtime28::EDataType::Void;
			#endif
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Constructor. Defines only the width and the height.
		/// </summary>
		///
		/// <param name="iWidthPX">  The width in pixels. </param>
		/// <param name="iHeightPX"> The height in pixels. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RX_CUDA_EXP CRxImageFormat(int iWidthPX, int iHeightPX)
		{
			m_iWidth     = iWidthPX;
			m_iHeight    = iHeightPX;
			m_ePixelType = Interop::Runtime28::EPixelType::None;
			m_eDataType  = Interop::Runtime28::EDataType::Void;
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Default constructor.
		/// </summary>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RX_CUDA_EXP CRxImageFormat(int iWidth, int iHeight, Interop::Runtime28::EPixelType::ID ePixelType, Interop::Runtime28::EDataType::ID eDataType)
		{
			m_iWidth     = iWidth;
			m_iHeight    = iHeight;
			m_ePixelType = ePixelType;
			m_eDataType  = eDataType;
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Constructor that creates an image format that is equal to the image format of the given image interface.
		/// </summary>
		///
		/// <param name="pxImg"> The image interface pointer. </param>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CRxImageFormat(const Interop::Runtime28::IImage* pxImg)
		{
			pxImg->GetType(m_ePixelType, m_eDataType);
			pxImg->GetSize(m_iWidth, m_iHeight);
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Destructor.
		/// </summary>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RX_CUDA_EXP ~CRxImageFormat()
		{
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Resets this CRxImageFormat.
		/// </summary>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RX_CUDA_EXP void Reset()
		{
			m_iWidth     = 0;
			m_iHeight    = 0;
			m_ePixelType = Interop::Runtime28::EPixelType::None;
			m_eDataType  = Interop::Runtime28::EDataType::Void;
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Convert this CRxImageFormat into a string representation.
		/// </summary>
		///
		/// <returns>	This CRxImageFormat as a std::string. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		CRxString ToString() const
		{
			return RX_S m_iWidth << "x" << m_iHeight << ", " << PixelType2String(m_ePixelType) << ", " << DataType2String(m_eDataType);
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Converts the given pixel type to a string.
		/// </summary>
		///
		/// <param name="ePixelType"> The pixel type. </param>
		///
		/// <returns> The pixel type as a string. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		static CRxString PixelType2String(Interop::Runtime28::EPixelType::ID ePixelType)
		{
			switch (ePixelType)
			{
			case Interop::Runtime28::EPixelType::None:    return "None";
			case Interop::Runtime28::EPixelType::BayerBG: return "BayerBG";
			case Interop::Runtime28::EPixelType::BayerGB: return "BayerGB";
			case Interop::Runtime28::EPixelType::BayerGR: return "BayerGR";
			case Interop::Runtime28::EPixelType::BayerRG: return "BayerRG";
			case Interop::Runtime28::EPixelType::Lum:     return "Lum";
			case Interop::Runtime28::EPixelType::LumA:    return "LumA";
			case Interop::Runtime28::EPixelType::RGB:     return "RGB";
			case Interop::Runtime28::EPixelType::BGR:     return "BGR";
			case Interop::Runtime28::EPixelType::RGBA:    return "RGBA";
			case Interop::Runtime28::EPixelType::BGRA:    return "BGRA";
			default: return "Unknown";
			}
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Converts the given data type to a string.
		/// </summary>
		///
		/// <param name="eDataType"> The data type. </param>
		///
		/// <returns> The data type as a string. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		static CRxString DataType2String(Interop::Runtime28::EDataType::ID eDataType)
		{
			switch (eDataType)
			{
			case Interop::Runtime28::EDataType::Void:              return "Void";
			case Interop::Runtime28::EDataType::Byte:              return "Byte";
			case Interop::Runtime28::EDataType::UByte:             return "UByte";
			case Interop::Runtime28::EDataType::Short:             return "Short";
			case Interop::Runtime28::EDataType::UShort:            return "UShort";
			case Interop::Runtime28::EDataType::Int:               return "Int";
			case Interop::Runtime28::EDataType::UInt:              return "UInt";
			case Interop::Runtime28::EDataType::Float:             return "Float";
			case Interop::Runtime28::EDataType::Double:            return "Double";
			case Interop::Runtime28::EDataType::Custom_10in16_LSB: return "Custom_10in16_LSB";
			case Interop::Runtime28::EDataType::Custom_10in16_MSB: return "Custom_10in16_MSB";
			case Interop::Runtime28::EDataType::Custom_12in16_LSB: return "Custom_12in16_LSB";
			case Interop::Runtime28::EDataType::Custom_12in16_MSB: return "Custom_12in16_MSB";
			default: return "Unknown";
			}
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Equality operator. </summary>
		///
		/// <param name="xFormat">	Describes the format to use. </param>
		///
		/// <returns>	True if the parameters are considered equivalent. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RX_CUDA_EXP bool operator==(const CRxImageFormat& xFormat) const
		{
			return m_iWidth == xFormat.m_iWidth && m_iHeight == xFormat.m_iHeight && m_ePixelType == xFormat.m_ePixelType && m_eDataType == xFormat.m_eDataType;
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Inequality operator. </summary>
		///
		/// <param name="xFormat">	Describes the format to use. </param>
		///
		/// <returns>	True if the parameters are not considered equivalent. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RX_CUDA_EXP bool operator!=(const CRxImageFormat& xFormat) const
		{
			return !(*this == xFormat);
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Test whether format is of particular pixel type.
		///
		/// 	This function uses the static function IsOfType() of the pixel types to check whether the given template parameter
		/// 	pixel type matches the pixel and data types of this instance.
		/// </summary>
		///
		/// <typeparam name="TPix">	Type of the pix. </typeparam>
		///
		/// <returns>	True if of type&lt; t pix&gt;, false if not. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		template<class TPix>
		RX_CUDA_EXP bool IsOfType() const
		{
			return TPix::IsOfType(m_ePixelType, m_eDataType);
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Query if this CRxImageFormat is bayer pixel type.
		/// </summary>
		///
		/// <returns>	True if bayer pixel type, false if not. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RX_CUDA_EXP bool IsBayerPixelType() const
		{
			return m_ePixelType == Interop::Runtime28::EPixelType::BayerBG ||
			       m_ePixelType == Interop::Runtime28::EPixelType::BayerGB ||
			       m_ePixelType == Interop::Runtime28::EPixelType::BayerGR ||
			       m_ePixelType == Interop::Runtime28::EPixelType::BayerRG;
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the component count.
		/// </summary>
		///
		/// <returns>	The component count. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RX_CUDA_EXP int GetComponentCount() const
		{
			switch (m_ePixelType)
			{
			case Interop::Runtime28::EPixelType::None:
				return 0;
			case Interop::Runtime28::EPixelType::BayerBG:
			case Interop::Runtime28::EPixelType::BayerGB:
			case Interop::Runtime28::EPixelType::BayerGR:
			case Interop::Runtime28::EPixelType::BayerRG:
			case Interop::Runtime28::EPixelType::Lum:
				return 1;
			case Interop::Runtime28::EPixelType::YUV420P:
			case Interop::Runtime28::EPixelType::LumA:
				return 2;
			case Interop::Runtime28::EPixelType::RGB:
			case Interop::Runtime28::EPixelType::BGR:
				return 3;
			case Interop::Runtime28::EPixelType::RGBA:
			case Interop::Runtime28::EPixelType::BGRA:
				return 4;

			default:
				#if defined(__CUDACC__)
					return 0;
				#else
					RX_THROW(Rx::CRxException, "Unknown pixel type");
				#endif
			}
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the bytes per channel.
		/// </summary>
		///
		/// <returns> The bytes per channel. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RX_CUDA_EXP int GetBytesPerChannel() const
		{
			switch (m_eDataType)
			{
			case Interop::Runtime28::EDataType::Void:
				return 0;
			case Interop::Runtime28::EDataType::Byte:
			case Interop::Runtime28::EDataType::UByte:
				return 1;
			case Interop::Runtime28::EDataType::Short:
			case Interop::Runtime28::EDataType::UShort:
			case Interop::Runtime28::EDataType::Custom_10in16_LSB:
			case Interop::Runtime28::EDataType::Custom_10in16_MSB:
			case Interop::Runtime28::EDataType::Custom_12in16_LSB:
			case Interop::Runtime28::EDataType::Custom_12in16_MSB:
				return 2;
			case Interop::Runtime28::EDataType::Int:
			case Interop::Runtime28::EDataType::UInt:
			case Interop::Runtime28::EDataType::Float:
				return 4;
			case Interop::Runtime28::EDataType::Double:
				return 8;

			default:
				#if defined(__CUDACC__)
					return 0;
				#else
					RX_THROW(Rx::CRxException, RX_S "Unknown data type '" << DataType2String(m_eDataType) << "'");
				#endif
			}
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the bit depth. This is the number of bits per channel.
		/// </summary>
		///
		/// <returns> The bit depth. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RX_CUDA_EXP int GetBitDepth() const
		{
			return GetBytesPerChannel() * 8;
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the bytes per pixel.
		/// </summary>
		///
		/// <returns> The bytes per pixel. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RX_CUDA_EXP int GetBytesPerPixel() const
		{
			if ((m_eDataType == Rx::Interop::Runtime28::EDataType::SplitPacked10Bits) ||
			    (m_eDataType == Rx::Interop::Runtime28::EDataType::SplitPacked12Bits))
			{
				#if defined(__CUDACC__)
					return 0;
				#else
					RX_THROW(Rx::CRxException, "Unable to determine bytes per pixel from integral data formats");
				#endif
			}

			return GetBytesPerChannel() * GetComponentCount();
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the pixel count.
		/// </summary>
		///
		/// <returns> The pixel count. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RX_CUDA_EXP unsigned GetPixelCount() const
		{
			return (unsigned) (m_iWidth * m_iHeight);
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Gets the total number of bytes allocated by images that are of this image format.
		/// </summary>
		///
		/// <returns> The number of byte. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RX_CUDA_EXP unsigned GetByteCount() const
		{
			if (m_eDataType == Rx::Interop::Runtime28::EDataType::SplitPacked10Bits)
			{
				return (unsigned) ((double) (GetPixelCount()) * 1.25);
			}

			if (m_eDataType == Rx::Interop::Runtime28::EDataType::SplitPacked12Bits)
			{
				return (unsigned) ((double) (GetPixelCount()) * 1.5);
			}

			if (m_eDataType == Rx::Interop::Runtime28::EDataType::Custom_Packed10)
			{
				return (GetPixelCount() * GetComponentCount() * 10) / 8;
			}

			if (m_eDataType == Rx::Interop::Runtime28::EDataType::Custom_Packed12)
			{
				return (GetPixelCount() * GetComponentCount() * 12) / 8;
			}

			return (unsigned) GetBytesPerPixel() * GetPixelCount();
		}

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Returns true if the data type of this image format is one of the custom (non primitive) data types.
		/// </summary>
		///
		/// <returns> True if the data type is custom, false if not. </returns>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RX_CUDA_EXP bool HasCustomDataType() const
		{
			switch (m_eDataType)
			{
			case Interop::Runtime28::EDataType::Custom_10in16_LSB:
			case Interop::Runtime28::EDataType::Custom_10in16_MSB:
			case Interop::Runtime28::EDataType::Custom_12in16_LSB:
			case Interop::Runtime28::EDataType::Custom_12in16_MSB:
			case Interop::Runtime28::EDataType::Custom_Packed10:
			case Interop::Runtime28::EDataType::Custom_Packed12:
			case Interop::Runtime28::EDataType::SplitPacked10Bits:
			case Interop::Runtime28::EDataType::SplitPacked12Bits:
				return true;
			default:
				return false;
			}
		}

	public:

		int m_iWidth, m_iHeight;
		Interop::Runtime28::EDataType::ID m_eDataType;
		Interop::Runtime28::EPixelType::ID m_ePixelType;
	};
	/// @}
}

#pragma make_public(Rx::CRxImageFormat)
