/**
 * \file    Mimic.cpp
 * \author  Silvan Murer
 * \date    11 October 2022
 *
 * \brief   Mimic - Library for access GenICam based devices
 */

#include "Mimic.hpp"

#include "spdlog/spdlog.h"
#ifdef _WIN32
#include "spdlog/sinks/msvc_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#else
#include "spdlog/sinks/stdout_color_sinks.h"
#endif

#include <sstream>
#include <stdint.h>
#include <stdlib.h> /* getenv */
#include <string>

#ifndef MIMIC_VERSION
#define MIMIC_VERSION ("0.0.42")
#endif

namespace fs = std::filesystem;

namespace mimic {

Mimic::Mimic()
{
#ifdef _WIN32
	auto windbg_sink = std::make_shared<spdlog::sinks::windebug_sink_mt>();
	//windbg_sink->set_level(spdlog::level::debug);
	logger = std::make_shared<spdlog::logger>("Mimic", windbg_sink);
	//logger = spdlog::stdout_color_mt("Mimic");
	logger->set_level(spdlog::level::debug);
#else
	logger = spdlog::stdout_color_mt("Mimic");
#endif // _WIN32
}

Mimic::~Mimic()
{ }

std::vector<fs::path> Mimic::getGenICam_GenTL_Path()
{
#if defined(_WIN64) || defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64)
	std::string envVar = "GENICAM_GENTL64_PATH";
#elif defined(_WIN32) || defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(armv7)
	std::string envVar = "GENICAM_GENTL32_PATH";
#else
	assert(true);
	std::string envVar = "";
#endif

	std::vector<fs::path> genTL_path;
	char* pEnvVarValue = getenv(envVar.c_str());
	if (pEnvVarValue == NULL) {
		return genTL_path;
	}
	std::string envVarValue = std::string(pEnvVarValue);
	logger->debug("{0}: {1}", envVar, envVarValue);

	std::string line;
	std::stringstream ss(envVarValue);
	while (std::getline(ss, line, ';')) {
		genTL_path.push_back(line);
	}
	return genTL_path;
}

Mimic& Mimic::getInstance()
{
	static Mimic instance;
	return instance;
}

std::string Mimic::getVersion()
{
	logger->debug("getVersion call.");
	return std::string(MIMIC_VERSION);
}

uint32_t Mimic::update()
{
	logger->debug("update call.");
	auto genTL_pathList = getGenICam_GenTL_Path();
	producerList.clear();
	for (auto& genTL_path : genTL_pathList) {
		if (fs::is_directory(genTL_path)) {
			for (auto const& path_entry : std::filesystem::directory_iterator{ genTL_path })
			{
				if (path_entry.is_regular_file()) {
					if (!path_entry.path().extension().compare(".cti")) {
						logger->debug("append {0}", path_entry.path().u8string());
						producerList.push_back(path_entry.path());
					}
				}
			}
		}
	}
	return (uint32_t)producerList.size();
}

std::string Mimic::getProducerFilename(uint32_t tlNo)
{
	logger->debug("getProducerFilename({0}) call.", tlNo);
	if (tlNo > producerList.size()) {
		throw Mimic_OutOfRange_Exception("GenTL Producer index out of range!", -100);
	}
	return producerList.at(tlNo).filename().u8string();
}

std::string Mimic::getProducerPath(uint32_t tlNo)
{
	logger->debug("getProducerPath({0}) call.", tlNo);
	if (tlNo > producerList.size()) {
		throw Mimic_OutOfRange_Exception("GenTL Producer index out of range!", -100);
	}
	fs::path selectedProducer = producerList.at(tlNo);
	return selectedProducer.remove_filename().u8string();
}

std::string Mimic::getProducerName(uint32_t tlNo)
{
	logger->debug("getProducerName({0}) call.", tlNo);
	if (tlNo > producerList.size()) {
		throw Mimic_OutOfRange_Exception("GenTL Producer index out of range!", -100);
	}
	return producerList.at(tlNo).stem().u8string();
}

Mimic_Producer Mimic::openProducer(uint32_t tlNo)
{
	logger->debug("openProducer({0}) call.", tlNo);
	if (tlNo > producerList.size()) {
		throw Mimic_OutOfRange_Exception("GenTL Producer index out of range!", -100);
	}
	return Mimic_Producer(producerList.at(tlNo));
}

Mimic_Producer Mimic::openProducer(const fs::path producer)
{
	logger->debug("openProducer({0}) call.", producer.string());
	return Mimic_Producer(producer);
}

} /* namespace genSDK */
