/**
 * \file    Mimic_NodeMap.hpp
 * \author  Silvan Murer
 * \date    24 September 2022
 *
 * \brief   Class get access to the feature tree
 */

#ifndef INC_MIMIC_NODEMAP
#define INC_MIMIC_NODEMAP

#include "features/Mimic_Node.hpp"
#include "features/Mimic_Boolean.hpp"
#include "features/Mimic_Category.hpp"
#include "features/Mimic_Command.hpp"
#include "features/Mimic_Enumeration.hpp"
#include "features/Mimic_Float.hpp"
#include "features/Mimic_Integer.hpp"
#include "features/Mimic_String.hpp"

#include <memory>
#include <string>

namespace genSDK {
	class GTL_Producer; ///< forward declaration of GTL_Producer
}
namespace GenTL {
	typedef void* PORT_HANDLE; ///< declaration of GenTL port handle
}
namespace GenApi_3_4 {
	struct INodeMap; ///< forware declaration of GenICam node map
}

namespace mimic {

class M_Port; ///< forward declaration of internal port implementation

class Mimic_NodeMap {
private:
	std::shared_ptr<genSDK::GTL_Producer> gtl_producer; ///< shared pointer to the gtl_producer
	std::shared_ptr<M_Port> port; ///< pointer to the port used by the nodeMap
	std::shared_ptr<GenApi_3_4::INodeMap> nodeMap; ///< pointer to the GenICam node map
	

public:
	Mimic_NodeMap() { };
	Mimic_NodeMap(std::shared_ptr<genSDK::GTL_Producer> gtl_producer, GenTL::PORT_HANDLE portH, std::string portName, std::string xmlContent);
	~Mimic_NodeMap() { };

	/**
	 * \brief internal used method to get the raw nodeMap pointer
	 */
	GenApi_3_4::INodeMap* getRawINodeMap();

	std::shared_ptr<Mimic_Node> getNode(std::string name);

	template<typename T>
	T getValue(std::string name) {
		auto var = getNode(name)->getVariant(name);
		try {
			if constexpr (std::is_same_v<bool, T>) {
				return std::get<T>(var);
			}
			else if constexpr (std::is_integral_v<T>) {
				return std::get<int64_t>(var);
			}
			else if constexpr (std::is_floating_point_v<T>) {
				return std::get<double>(var);
			}
			else {
				return std::get<T>(var);
			}
		}
		catch (std::bad_variant_access const& e) {
			throw Mimic_Access_Exception(std::string("Wrong type when reading Node ") + name.c_str() + "! " + e.what(), -650);
		}
		catch (...) {
			throw;
		}
	}

	template<typename T>
	void setValue(std::string name, T value) {
		if constexpr (std::is_same_v<bool, T>) {
			getNode(name)->setVariant(name, value);
		}
		else if constexpr (std::is_integral_v<T>) {
			getNode(name)->setVariant(name, (int64_t)value);
		}
		else if constexpr (std::is_floating_point_v<T>) {
			getNode(name)->setVariant(name, (double)value);
		}
		else if constexpr (std::is_same_v<const char*, T>) {
			getNode(name)->setVariant(name, std::string(value));
		}
		else {
			getNode(name)->setVariant(name, value);
		}
	}
};

} /* namespace genSDK */

#endif
