/**
 * \file    Mimic_Buffer.hpp
 * \author  Silvan Murer
 * \date    24 September 2022
 *
 * \brief   Class get access to the buffer component defined by GenTL
 */

#ifndef INC_MIMIC_BUFFER
#define INC_MIMIC_BUFFER

#include "Mimic_NodeMap.hpp"
#include "Mimic_Exception.hpp"

#include <stdint.h>
#include <string>
#include <vector>

namespace spdlog {
	class logger; ///< forward declaration of internal used logger object
}
namespace genSDK {
	class GTL_Producer; ///< forward declaration of GTL_Producer
}
namespace GenTL {
	typedef void* DS_HANDLE;  ///< declaration of GenTL data stream handle
	typedef void* BUFFER_HANDLE;  ///< declaration of GenTL buffer handle
}

namespace mimic {

/**
 * Buffer class of the mimic implementation
 *
 * This class get access to the buffer data of a GenTL producer.
 */
class Mimic_Buffer {
private:
	std::shared_ptr<spdlog::logger> logger; ///< shared pointer to a simple console logger
	std::shared_ptr<genSDK::GTL_Producer> gtl_producer; ///< shared pointer to the gtl_producer
	GenTL::DS_HANDLE dsH; ///< C handle to the GenTL device component
	GenTL::BUFFER_HANDLE bufH;  ///< C handle to the GenTL buffer component

	/**
	 * \brief internal method returns the payload type according to the GenTL definition
	 */
	int64_t getPayloadType();

	/**
	 * \brief read the base address of the raw buffer for the given component
	 */
	void* getBufferBase(int64_t index);
	/**
	 * \brief read the size of the raw buffer for the given component
	 */
	size_t getBufferSize(int64_t index);
	/**
	 * \brief read the raw size of chunk data (0 if no chunk data available)
	 */
	size_t getChunkSize();

public:
	/**
	 * \brief no default constructor is available for this class
	 */
	Mimic_Buffer() = delete;

	/**
	 * \brief constructor to create a Buffer instance
	 *
	 * Don't create a Buffer by your self. Use the Device::getBuffer method!
	 */
	Mimic_Buffer(std::shared_ptr<genSDK::GTL_Producer> gtl_producer, GenTL::DS_HANDLE dsH, GenTL::BUFFER_HANDLE bufH);

	/**
	 * \brief copy constructor
	 */
	Mimic_Buffer(const Mimic_Buffer& other) = delete;
	/**
	 * \brief copy assignment not supported by this class
	 */
	Mimic_Buffer& operator=(const Mimic_Buffer& other) = delete;

	/**
	 * \brief move constructor
	 */
	Mimic_Buffer(Mimic_Buffer&& other) noexcept;
	/**
	 * \brief move assignment not supported by this class
	 */
	Mimic_Buffer& operator=(Mimic_Buffer&& other) = delete;

	/**
	 * \brief destructor
	 */
	~Mimic_Buffer();

	/**
	 * \brief function to release a device.
	 *
	 * A call to this function release all allocated memory and Buffers generated by this device.
	 */
	void release();


	/* buffer data access */
	/**
     * \brief get the number of parts or components included in this buffer
     *
     * \return number of data parts
     */
	int64_t getNumParts();
	/**
	 * \brief get the number of channels included in a part/component
	 *
	 * \param[in]     index    zero based part/component index
	 *
	 * \return number of channels
	 */
	int64_t getNumChannels(int64_t index);

	/**
	 * \brief get the pixelformat of a part or component
	 *
	 * \param[in]     index    zero based part/component index
	 *
	 * \return pixelformat according to GenICam PFNC of the selected part/component
	 */
	int64_t getPartPixelformat(int64_t index);
	/**
	 * \brief get the GenICam PFNC pixelformat of a component or part as a string
	 *
	 * \param[in]     index    zero based part/component index
	 *
	 * \return string with the name of the pixelformat
	 */
	std::string getPartPixelformatName(int64_t index);
	/**
	 * \brief get the image width of the part or component
	 *
	 * \param[in]     index    zero based part/component index
	 *
	 * \return image width
	 */
	int64_t getPartWidth(int64_t index);
	/**
	 * \brief get the image height of the part or component
	 *
	 * \param[in]     index    zero based part/component index
	 *
	 * \return image height
	 */
	int64_t getPartHeight(int64_t index);
	/**
	 * \brief get the padding on the x-axis of the part or component
	 *
	 * \param[in]     index    zero based part/component index
	 *
	 * \return x-axis padding
	 */
	int64_t getPartPaddingX(int64_t index);

	/**
	 * \brief get the part or component as a std::vector
	 *
	 * \param[in]     index    zero based part/component index
	 *
	 * \return vector containing the selected part/component
	 */
	template<typename T>
	std::vector<T> getPartData(int64_t index);
	/**
	 * \brief get a single channel of a component or part as std::vector
	 *
	 * \param[in]     index    zero based component/part index
	 * \param[in]     channel  zero based channel index
	 *
	 * \return vector containing the selected channel of the selected component/part
	 */
	template<typename T>
	std::vector<T> getChannelData(int64_t index, int64_t channel);

	/**
	 * \brief get a single part or component and copy the data into a user allocated buffer
	 *
	 * \param[in/out]  buffer       user allocated memory for storing the data
	 * \param[in/out]  bufferSize   maximum size in bytes of the user allocated buffer
	 * \param[in]      paddingX     x-padding to be used in the given buffer
	 * \param[in]      index        zero based part/component index
	 *
	 */
	template<typename T>
	void getPartData(T* buffer, size_t* bufferSize, size_t paddingX, int64_t index);

};

template std::vector<uint8_t> Mimic_Buffer::getPartData(int64_t index);
template void Mimic_Buffer::getPartData(uint8_t* buffer, size_t* bufferSize, size_t paddingX, int64_t index);
template void Mimic_Buffer::getPartData(uint16_t* buffer, size_t* bufferSize, size_t paddingX, int64_t index);
template void Mimic_Buffer::getPartData(float* buffer, size_t* bufferSize, size_t paddingX, int64_t index);

} /* namespace mimic */

#endif
