/**
 * \file    Mimic_Producer.hpp
 * \author  Silvan Murer
 * \date    24 September 2022
 * 
 * \brief   Mimic_Producer - Class for accessing and handling a single GenICam producer
 */

#ifndef INC_MIMIC_PRODUCER
#define INC_MIMIC_PRODUCER

#include "Mimic_Camera.hpp"
#include "Mimic_Exception.hpp"

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

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* TL_HANDLE; ///< declaration of GenTL system handle
	typedef void* IF_HANDLE; ///< declaration of GenTL interface handle
}

namespace mimic {

/**
 * Producer class of the Mimic library
 *
 * This class handles a single GenICam producer. It allows to scan for cameras
 * and open a connection to it.
 */
class Mimic_Producer {
private:
	struct DeviceInfo {
		DeviceInfo(GenTL::IF_HANDLE ifHandle, std::string name) : ifHandle(ifHandle), cameraName(name) { };
		GenTL::IF_HANDLE ifHandle;
		std::string cameraName;
	};

	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::TL_HANDLE tlHandle; ///< C handle to the GenTL system component
	//std::vector<GenTL::IF_HANDLE> ifHandle_list; ///< list of C handle to the GenTL interface component
	std::vector<DeviceInfo> device_list; ///< internal list containing the  C handle to the GenTL interface component and number of available devices

	/**
	 * \brief release all resources of the objet
	 */
	void release();
	/**
	 * \brief check if the object is correctly initialized
	 */
	void checkObject(); 
public:
	/**
	 * no copy constructors
	 */
	Mimic_Producer(Mimic_Producer const&) = delete;
	Mimic_Producer& operator=(Mimic_Producer const&) = delete;

	/**
	 * \brief default constructor, create an empty Mimic_producer.
	 *	      this producer object is not initialized and additinal 
	 *        method calls throw an exception.
	 */
	Mimic_Producer();

	/**
	 * \brief move constructor 
	 */
	Mimic_Producer(Mimic_Producer&& other);
	/**
	 * \brief move assignment
	 */
	Mimic_Producer& operator=(Mimic_Producer&& other);

	/**
	 * \brief constructor used to create a Mimic_Producer object
	 *
	 * \param[in]     producer    path and filename to the GenICam Producer (*.cti file)
	 */
	Mimic_Producer(const std::filesystem::path producer);
	/**
	 * \brief destructor used to destroy/release the Platform object
	 */
	~Mimic_Producer();
	
	/**
	 * \brief function to reset the whole system
	 *
	 * A call to this function release all allocated memory and release all open connections.
	 */
	//void reset();
	
	/**
	 * \brief read the version of the GenTL producer
	 *
	 * \return a string value including the version information
	 */
	std::string getVersion();
	/**
	 * \brief read the file location of the used GenTL producer
	 *
	 * \return string value including the path of the used genTL producer
	 */
	std::string getLocation();
	
	/**
	 * \brief scan for available GenICam cameras
	 *
	 * GenICam defines multiple components (Systemc, Interface, Device)
	 * where the Cameras are Devices. For a full ducomentation visit:
	 * https://www.emva.org/standards-technology/genicam/genicam-downloads/
	 * This function iterate over all components and sub-components 
	 * (using the given timeout). It updates the internal list.
	 * 
	 * A recall of this method is required after each change (plugin, connect/disconnect of devices).
	 * It can change the object order in the list and makes existing objects invalid. 
	 *
	 * \param[in]     timeout    per component used timeout in ms (default: 900ms)
	 *
	 * \return the number of available Cameras
	 */
	uint32_t update(uint32_t timeout = 900);
	
	/**
	 * \brief read the camera name at the given index.
	 *
	 * Before calling this method, a call to update() is required.
	 *
	 * \param[in]     camNo    zero based index of the camera
	 *
	 * \return a string containing the camera
	 */
	std::string getCameraName(uint32_t camNo);
	
	/**
	 * \brief open a camera
	 *
	 * Open a camera connection. Camera configuration and data acquisition are done through the returned Mimic_Camera object.
	 * Before calling this method, a call to update() is required.
	 *
	 * \param[in]     camNo    zero based index of the camera
	 *
	 * \return Mimic_Camera object
	 */
	Mimic_Camera openCamera(uint32_t camNo);

};

} /* namespace mimic */

#endif
