#pragma once
#include "ANSODEngine.h"
#include "ANSYOLOOD.h"
#include "ANSTENSORRTOD.h"
#include "ANSTENSORRTCL.h"
#include "ANSOPENVINOCL.h"
#include "ANSOPENVINOOD.h"
#include "ANSYOLOV10RTOD.h"
#include "ANSYOLOV10OVOD.h"
#include "ANSCUSTOMDetector.h"
#include "ANSFD.h"
#include "ANSANOMALIB.h"
#include "ANSPOSE.h"
#include "ANSSAM.h"
#include <pipelines/metadata.h>
#include <models/input_data.h>
#include "utils/visualizer.hpp"
#include <turbojpeg.h>
#include <vector>
#include <map>
#include <string>
#include "ANSLibsLoader.h"
namespace ANSCENTER
{
	//------------------------------------------------------------------------------------------------------------------------//
	// Facial Recognition base class
	void ANSFRBase::CheckLicense() {
		try {
			_licenseValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(_licenseKey, 1004, "ANSFR");//Default productId=1005
		}
		catch (std::exception& e) {
			this->_logger.LogFatal("ANSFRBase::CheckLicense()", e.what(), __FILE__, __LINE__);
		}
	}
	bool ANSFRBase::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword)
	{
		try {
			ANSCENTER::ANSLibsLoader::Initialize();
			_modelFolder = "";
			_modelConfigFile = "";
			_modelFolder.clear();
			_modelConfigFile.clear();
			// 0. Check if the modelZipFilePath exist?
			if (!FileExist(modelZipFilePath)) {
				this->_logger.LogFatal("ANSFRBase::LoadModel", "Model zip file is not exist", __FILE__, __LINE__);
			}
			// 1. Unzip model zip file to a special location with folder name as model file (and version)
			std::string outputFolder;
			std::vector<std::string> passwordArray;
			if (!modelZipPassword.empty()) passwordArray.push_back(modelZipPassword);
			passwordArray.push_back("Sh7O7nUe7vJ/417W0gWX+dSdfcP9hUqtf/fEqJGqxYL3PedvHubJag==");
			passwordArray.push_back("3LHxGrjQ7kKDJBD9MX86H96mtKLJaZcTYXrYRdQgW8BKGt7enZHYMg==");
			passwordArray.push_back("AnsCustomModels20@$");
			passwordArray.push_back("AnsDemoModels20@!");
			std::string modelName = GetFileNameWithoutExtension(modelZipFilePath);
			//this->_logger.LogDebug("ANSFRBase::LoadModel. Model name", modelName, __FILE__, __LINE__);
			size_t vectorSize = passwordArray.size();
			for (size_t i = 0; i < vectorSize; i++) {
				if (ExtractPasswordProtectedZip(modelZipFilePath, passwordArray[i], modelName, _modelFolder, false))
					break; // Break the loop when the condition is met.
			}
			// 2. Check if the outputFolder exist
			if (!std::filesystem::exists(_modelFolder)) {
				this->_logger.LogError("ANSFRBase::LoadModel. Output model folder is not exist", _modelFolder, __FILE__, __LINE__);
				return false; // That means the model file is not exist or the password is not correct
			}
			// 3. Check if the model has the configuration file
			std::string modelConfigName = "model_config.json";
			_modelConfigFile = CreateFilePath(_modelFolder, modelConfigName);
			return true;
		}
		catch (std::exception& e) {
			this->_logger.LogFatal("ANSFRBase::LoadModel", e.what(), __FILE__, __LINE__);
			return false;
		}
	}
	bool ANSFRBase::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) {
		try {
			ANSCENTER::ANSLibsLoader::Initialize();
			_licenseKey = licenseKey;
			_licenseValid = false;
			_modelFolder = "";
			_modelConfigFile = "";
			_modelFolder.clear();
			_modelConfigFile.clear();
			CheckLicense();
			if (!_licenseValid) {
				this->_logger.LogError("ANSFRBase::Initialize", "Invalid License", __FILE__, __LINE__);
				return false;
			}
			// 0. Check if the modelZipFilePath exist?
			if (!FileExist(modelZipFilePath)) {
				this->_logger.LogFatal("ANSFRBase::Initialize", "Model zip file is not exist", __FILE__, __LINE__);
			}
			// 1. Unzip model zip file to a special location with folder name as model file (and version)
			std::string outputFolder;
			std::vector<std::string> passwordArray;
			if (!modelZipPassword.empty()) passwordArray.push_back(modelZipPassword);
			passwordArray.push_back("Sh7O7nUe7vJ/417W0gWX+dSdfcP9hUqtf/fEqJGqxYL3PedvHubJag==");
			passwordArray.push_back("3LHxGrjQ7kKDJBD9MX86H96mtKLJaZcTYXrYRdQgW8BKGt7enZHYMg==");
			passwordArray.push_back("AnsCustomModels20@$");
			passwordArray.push_back("AnsDemoModels20@!");
			std::string modelName = GetFileNameWithoutExtension(modelZipFilePath);
			//this->_logger.LogDebug("ANSFRBase::Initialize. Model name", modelName, __FILE__, __LINE__);
			size_t vectorSize = passwordArray.size();
			for (size_t i = 0; i < vectorSize; i++) {
				if (ExtractPasswordProtectedZip(modelZipFilePath, passwordArray[i], modelName, _modelFolder, false))
					break; // Break the loop when the condition is met.
			}
			// 2. Check if the outputFolder exist
			if (!std::filesystem::exists(_modelFolder)) {
				this->_logger.LogError("ANSFRBase::Initialize. Output model folder is not exist", _modelFolder, __FILE__, __LINE__);
				return false; // That means the model file is not exist or the password is not correct
			}
			// 3. Check if the model has the configuration file
			std::string modelConfigName = "model_config.json";
			_modelConfigFile = CreateFilePath(_modelFolder, modelConfigName);
			return true;
		}
		catch (std::exception& e) {
			this->_logger.LogFatal("ANSFRBase::Initialize", e.what(), __FILE__, __LINE__);
			return false;
		}
	}
	bool ANSFRBase::OptimizeModel(bool fp16, std::string& optimizedModelFolder) {
		try {
			ANSCENTER::ANSLibsLoader::Initialize();
			return true;
		}
		catch (std::exception& e) {
			this->_logger.LogFatal("ANSFRBase::OptimizeModel", e.what(), __FILE__, __LINE__);
			return false;
		}
	}
	ANSFRBase::~ANSFRBase() {
		try {
		}
		catch (std::exception& e) {
			this->_logger.LogFatal("ANSFRBase::~ANSFRBase", e.what(), __FILE__, __LINE__);
		}
	}

	std::vector<float> ANSFRBase::L2Normalize(const std::vector<float>& values) {
		try {
			size_t num_val = values.size();
			if (num_val == 0) {
				return {};
			}

			std::vector<float> norm(num_val);
			float l2_sum_val = 0.f;

			for (const auto& value : values) {
				l2_sum_val += (value * value);
			}

			float l2_sum_sqrt = std::sqrt(l2_sum_val);

			// Check for zero division using machine epsilon
			if (std::abs(l2_sum_sqrt) < std::numeric_limits<float>::epsilon())
			{
				// Return a default value or handle the error as needed
				return std::vector<float>(num_val, 0.0f);
			}

			for (size_t i = 0; i < num_val; ++i) {
				norm[i] = values[i] / l2_sum_sqrt;
			}

			return norm;
		}
		catch (std::exception& e) {
			this->_logger.LogFatal("ANSFRBase::L2Normalize", e.what(), __FILE__, __LINE__);
			return {};
		}

	}
	float ANSFRBase::CosineSimilarity(const std::vector<float>& a, const std::vector<float>& b, bool normalized) {

		try {
			if (a.size() != b.size() || a.empty()) {
				// Return a default value or handle the error as needed
				return 1.0f;
			}

			size_t num_val = a.size();
			float mul_a = 0.f, mul_b = 0.f, mul_ab = 0.f;

			if (normalized) {
				for (size_t i = 0; i < num_val; ++i) {
					mul_a += (a[i] * a[i]);
					mul_b += (b[i] * b[i]);
					mul_ab += (a[i] * b[i]);
				}
			}
			else {
				auto norm_a = L2Normalize(a);
				auto norm_b = L2Normalize(b);
				for (size_t i = 0; i < num_val; ++i) {
					mul_a += (norm_a[i] * norm_a[i]);
					mul_b += (norm_b[i] * norm_b[i]);
					mul_ab += (norm_a[i] * norm_b[i]);
				}
			}
			// Check for zero division using machine epsilon
			float denominator = std::sqrt(mul_a) * std::sqrt(mul_b);
			if (std::abs(denominator) < std::numeric_limits<float>::epsilon()) {
				// Return a default value or handle the error as needed
				return 1.0f;
			}

			return (mul_ab / denominator);
		}
		catch (std::exception& e) {
			this->_logger.LogFatal("ANSFRBase::CosineSimilarity", e.what(), __FILE__, __LINE__);
			return 1.0f;
		}
	}

}
