#include "Utility.h"
#include <hwinfo/PCIMapper.h>
#include <hwinfo/hwinfo.h>
#include <iomanip>

#include "spdlog/async.h"
#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/spdlog.h"
#include "spdlog/cfg/env.h"   // support for loading levels from the environment variable
#include "spdlog/fmt/ostr.h"  // support for user defined types
#include "spdlog/sinks/stdout_color_sinks.h"
#include "base64.h"
#include <spdlog/sinks/base_sink.h>
#include <winnt.h>
#include <algorithm>
#include <atomic>
#include <cctype>
#include <chrono>
#include <filesystem>
#include <system_error>
#include <cstdlib>

// Forward-declare OutputDebugStringA without pulling in <windows.h> (which
// drags winsock conflicts as noted in ANSLicense.h). Used by ANSCENTER_DebugLog.
extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* lpOutputString);

#define MAX_CUSTOMER_NAME_LEN 100
#define FEATURE_1 0x1
#define FEATURE_2 0x2
#define FEATURE_3 0x4
#define FEATURE_4 0x8
#define FEATURE_5 0x10
#define FEATURE_6 0x20
#define FEATURE_7 0x40
#define FEATURE_8 0x80

// ----------------------------------------------------------------------------
// Log-prefix scrubber.
//
// We don't want noisy fully-qualified per-machine paths in log lines:
//   "model loaded from C:\ProgramData\Jh7O7nUe7vS\Models\EngineModels\X"
// becomes
//   "model loaded from Models\EngineModels\X"
//
// The prefix list is built once on first use and combines:
//   - hardcoded ANSCENTER ProgramData roots (edit kStaticPrefixes below),
//   - the OS temp directory resolved at runtime via std::filesystem,
//   - per-OS conventional temp/env-var paths (TEMP/TMP on Windows;
//     /tmp/, /var/tmp/, TMPDIR on POSIX).
//
// Each prefix is normalised to lowercase, has a trailing separator forced,
// and is added in BOTH `\` and `/` slash flavours so the matcher works on
// either form. Prefixes are sorted longest-first so a more specific prefix
// (e.g. C:\Users\Bob\AppData\Local\Temp\) wins over a shorter overlapping one.
//
// Case-insensitive, handles both `\` and `/` separators. Single pass, O(N*P)
// where N is message length and P is prefix count (~5–10 in practice). Called
// from every SPDLogger::Log* path and from ANSCENTER_DebugLog (the choke point
// used by ANS_DBG / ANSLIB_DBG).
// ----------------------------------------------------------------------------
namespace {

    inline std::string to_lower_ascii(const std::string& s) {
        std::string out(s.size(), '\0');
        for (size_t i = 0; i < s.size(); ++i) {
            out[i] = static_cast<char>(std::tolower(static_cast<unsigned char>(s[i])));
        }
        return out;
    }

    // Redacts sensitive strings (license keys, activation keys, hardware IDs)
    // for logging while preserving enough surface for support-call correlation.
    // Keeps the first 4 and last 4 chars; replaces the middle with '*'.
    // Strings <= 8 chars are fully masked. Empty strings pass through.
    inline std::string mask_secret(const std::string& s) {
        if (s.empty()) return s;
        if (s.size() <= 8) return std::string(s.size(), '*');
        return s.substr(0, 4) + std::string(s.size() - 8, '*') + s.substr(s.size() - 4);
    }

    // SDK identifier passed to ANSLSHelper for licensing-service auth. Despite
    // the variable name "privateKey" used at call sites, this is an 11-byte
    // SDK token that identifies the ANSCENTER product to the licensing
    // service — NOT a cryptographic signing key. Centralised here so any
    // future rotation (or move to runtime lookup via env var / config file)
    // touches one place instead of duplicated literals scattered across the
    // file. Narrow form is the source of truth; wide form is derived to
    // avoid two literals drifting out of sync.
    inline std::string  GetSdkKey()  { return "AQlSAiRTNtS7X20="; }
    inline std::wstring GetSdkKeyW() { return String2WString(GetSdkKey()); }

    // Append both `\` and `/` slash flavours of `raw` to `dst`, lower-cased
    // and with a trailing separator forced. Empty / pathologically short
    // entries are skipped.
    inline void push_prefix_variants(std::vector<std::string>& dst, const std::string& raw) {
        if (raw.size() < 2) return;
        std::string normalised = raw;
        char last = normalised.back();
        if (last != '\\' && last != '/') normalised.push_back('\\');
        std::string lowerBs = to_lower_ascii(normalised);
        std::string lowerFs = lowerBs;
        for (char& c : lowerFs) if (c == '\\') c = '/';
        dst.push_back(lowerBs);
        if (lowerFs != lowerBs) dst.push_back(lowerFs);
    }

    // Build the prefix table once. Function-local static is initialised
    // exactly once per process (C++11 magic-static rule, thread-safe).
    const std::vector<std::string>& GetLogPrefixesLower() {
        static const std::vector<std::string> kPrefixesLower = []() {
            std::vector<std::string> v;

            // 1. Hardcoded ANSCENTER ProgramData roots — edit this list to
            //    add or remove well-known noise prefixes that aren't tied to
            //    the OS temp/user dir.
            static const char* kStaticPrefixes[] = {
                "C:\\ProgramData\\ANSCENTER\\",
                "C:\\ProgramData\\Jh7O7nUe7vS\\",
                nullptr,
            };
            for (int i = 0; kStaticPrefixes[i]; ++i) {
                push_prefix_variants(v, kStaticPrefixes[i]);
            }

            // 2. OS temp directory (Windows: usually %LOCALAPPDATA%\Temp;
            //    Linux: usually /tmp; macOS: /private/var/folders/...).
            try {
                push_prefix_variants(v, std::filesystem::temp_directory_path().string());
            } catch (...) { /* ignore — fallbacks below cover most cases */ }

            // 3. Per-OS conventional fallbacks in case temp_directory_path()
            //    differs from what the env vars say or returns nothing useful.
#ifdef _WIN32
            if (const char* p = std::getenv("TEMP")) push_prefix_variants(v, p);
            if (const char* p = std::getenv("TMP"))  push_prefix_variants(v, p);
            push_prefix_variants(v, "C:\\Windows\\Temp\\"); // SYSTEM/services temp
#else
            if (const char* p = std::getenv("TMPDIR")) push_prefix_variants(v, p);
            push_prefix_variants(v, "/tmp/");
            push_prefix_variants(v, "/var/tmp/");
#endif

            // 4. De-duplicate (env vars + filesystem API often agree).
            std::sort(v.begin(), v.end());
            v.erase(std::unique(v.begin(), v.end()), v.end());

            // 5. Sort longest-first so specific prefixes win over shorter
            //    overlapping ones (e.g. AppData\Local\Temp\ before AppData\).
            std::sort(v.begin(), v.end(),
                      [](const std::string& a, const std::string& b) {
                          return a.size() > b.size();
                      });

            return v;
        }();
        return kPrefixesLower;
    }

    inline std::string strip_log_prefixes(const std::string& input) {
        if (input.empty()) return input;

        const auto& prefixes = GetLogPrefixesLower();
        std::string lower = to_lower_ascii(input);

        std::string out;
        out.reserve(input.size());
        size_t i = 0;
        while (i < input.size()) {
            bool matched = false;
            for (const auto& p : prefixes) {
                if (i + p.size() <= input.size() &&
                    lower.compare(i, p.size(), p) == 0) {
                    i += p.size();
                    matched = true;
                    break;
                }
            }
            if (!matched) {
                out.push_back(input[i]);
                ++i;
            }
        }
        return out;
    }
}

namespace ANSCENTER
{
	template <typename T> T GetData(const boost::property_tree::ptree& pt, const std::string& key)
	{
		T ret;
		if (boost::optional<T> data = pt.get_optional<T>(key))
		{
			ret = data.get();
		}
		return ret;
	}

	class WindowsEventSink : public spdlog::sinks::base_sink<std::mutex>
	{
	public:
		explicit WindowsEventSink(const std::wstring& sourceName = L"ANSLogger")
			: _sourceName(sourceName)
		{
			// Register the event source (you should ideally pre-register in registry for production use)
			_eventSource = RegisterEventSourceW(nullptr, _sourceName.c_str());
		}

		~WindowsEventSink()
		{
			if (_eventSource)
			{
				DeregisterEventSource(_eventSource);
				_eventSource = nullptr;
			}
		}

	protected:
		void sink_it_(const spdlog::details::log_msg& msg) override
		{
			if (!_eventSource)
				return;

			// Convert log message to wide string
			std::wstring widePayload(msg.payload.begin(), msg.payload.end());
			LPCWSTR strings[1] = { widePayload.c_str() };

			// Map spdlog level to Windows Event Log type
			WORD eventType = EVENTLOG_INFORMATION_TYPE;
			switch (msg.level)
			{
			case spdlog::level::critical:
			case spdlog::level::err:
				eventType = EVENTLOG_ERROR_TYPE;
				break;
			case spdlog::level::warn:
				eventType = EVENTLOG_WARNING_TYPE;
				break;
			case spdlog::level::info:
			case spdlog::level::trace:
			case spdlog::level::debug:
			default:
				eventType = EVENTLOG_INFORMATION_TYPE;
				break;
			}

			// Use a non-zero event ID
			DWORD eventID = 1000 + static_cast<int>(msg.level); // Custom event ID per log level

			// Write to the event log
			ReportEventW(
				_eventSource,      // Event source handle
				eventType,         // Event type
				0,                 // Event category
				eventID,           // Event identifier (should be non-zero)
				nullptr,           // No user SID
				1,                 // Number of strings
				0,                 // No binary data
				strings,           // Array of strings
				nullptr);          // No binary data
		}

		void flush_() override {}

	private:
		HANDLE _eventSource = nullptr;
		std::wstring _sourceName;
	};
	/// <summary>
	/// SPDLogger
	/// </summary>

	SPDLogger::~SPDLogger() {
		try {
			Release();
		}
		catch (const std::exception& e) {
			std::cout << "SPDLogger::~SPDLogger" << e.what() << std::endl;
		}
	}

	bool SPDLogger::Release() {
		static bool isReleased = false; // Track whether Release has already been called

		if (isReleased) {
			std::cout << "SPDLogger::Release has already been called. Skipping." << std::endl;
			return true; // Return true since it was already successfully released
		}

		try {
			// Check if the "anslogger" exists and drop it explicitly
			auto anslogger = spdlog::get("anslogger");
			if (anslogger) {
				spdlog::drop("anslogger");
				std::cout << "Released anslogger" << std::endl;
			}

			// Drop all remaining loggers
			spdlog::drop_all();
			std::cout << "Dropped all loggers" << std::endl;

			// Shutdown the spdlog library
			spdlog::shutdown();
			std::cout << "Shutdown spdlog" << std::endl;

			isReleased = true; // Mark release as done to prevent repeated shutdowns
			return true;
		}
		catch (const std::exception& e) {
			std::cout << "SPDLogger::Release exception: " << e.what() << std::endl;
			return false;
		}
	}

	void SPDLogger::Init() {
		static std::once_flag initFlag;

		std::call_once(initFlag, [this]() {
			try {
				// Two worker threads so the slow Event Log sink (RPC to event log
				// service) cannot starve the console sink. Queue size unchanged.
				spdlog::init_thread_pool(8192, 2);
				ConfigureLogger();
			}
			catch (const spdlog::spdlog_ex& ex) {
				std::cerr << "Logger::Init spdlog exception: " << ex.what() << std::endl;
			}
			catch (const std::exception& ex) {
				std::cerr << "Logger::Init standard exception: " << ex.what() << std::endl;
			}
			catch (...) {
				std::cerr << "Logger::Init unknown exception" << std::endl;
			}
			});
	}

	void SPDLogger::ConfigureLogger() {
		try {
			// Console sink (shared by console logger)
			std::vector<spdlog::sink_ptr> consoleSinks;
			_consoleLoggerEnable = true; // Enable console logger by default
			if (_consoleLoggerEnable) {
				auto stdout_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
				stdout_sink->set_level(spdlog::level::trace);
				consoleSinks.push_back(stdout_sink);

				// overrun_oldest: producer never blocks. If the queue fills (8192
				// pending msgs), the OLDEST queued msg is dropped to make room.
				// Why: block + 1-thread pool + slow WindowsEventSink can stall
				// inference threads on logging and, if shutdown races a blocked
				// producer, surface as system_error("resource deadlock would occur").
				auto consoleLogger = std::make_shared<spdlog::async_logger>(
					"anslogger_console", consoleSinks.begin(), consoleSinks.end(),
					spdlog::thread_pool(), spdlog::async_overflow_policy::overrun_oldest);
				consoleLogger->set_level(spdlog::level::trace);
				consoleLogger->flush_on(spdlog::level::info);
				spdlog::register_logger(consoleLogger);
			}

			// Windows Event Log sink (used only for LogError and LogFatal)
			auto win_event_sink = std::make_shared<WindowsEventSink>(L"ANSLogger");
			win_event_sink->set_level(spdlog::level::info);
			std::vector<spdlog::sink_ptr> eventSinks{ win_event_sink };

			// Same overrun_oldest policy as console — see note above. Especially
			// important for this logger because WindowsEventSink::sink_it_ does
			// a synchronous ReportEventW RPC which can be slow under load.
			auto eventLogger = std::make_shared<spdlog::async_logger>(
				"anslogger_event", eventSinks.begin(), eventSinks.end(),
				spdlog::thread_pool(), spdlog::async_overflow_policy::overrun_oldest);
			eventLogger->set_level(spdlog::level::info);
			eventLogger->flush_on(spdlog::level::info);
			spdlog::register_logger(eventLogger);

		}
		catch (const std::exception& ex) {
			std::cerr << "SPDLogger::ConfigureLogger - Exception: " << ex.what() << std::endl;
		}
	}
	void SPDLogger::Disable() {
		try {
			spdlog::set_level(spdlog::level::off);
		}
		catch (const std::exception& e) {
			std::cout << "Logger::Disable:" << e.what() << std::endl;
		}
	}
	void SPDLogger::Enable() {
		try {
			spdlog::set_level(spdlog::level::info);
		}
		catch (const std::exception& e) {
			std::cout << "Logger::Enable:" << e.what() << std::endl;
		}
	}
	//__FUNCTION__
	void SPDLogger::LogTrace(const std::string& source, const std::string& message, const std::string& fileSource, int lineSource) {
		std::string msg = "[" + path_to_filename(fileSource) + "] [" + std::to_string(lineSource) + "] " + source;
		std::string scrubbed = strip_log_prefixes(message);

		try {
			auto logger = spdlog::get("anslogger_console");
			if (logger) logger->trace("{}: {}", msg, scrubbed);

			auto eventLogger = spdlog::get("anslogger_event");
			if (eventLogger) eventLogger->trace("{}: {}", msg, scrubbed);
		}
		catch (const std::exception& e) {
			std::cerr << "SPDLogger::LogError - Exception: " << e.what() << std::endl;
		}
	}
	void SPDLogger::LogInfo(const std::string& source, const std::string& message, const std::string& fileSource, int lineSource) {
		std::string msg = "[" + path_to_filename(fileSource) + "] [" + std::to_string(lineSource) + "] " + source;
		std::string scrubbed = strip_log_prefixes(message);

		try {
			auto logger = spdlog::get("anslogger_console");
			if (logger) {
				logger->info("{}: {}", msg, scrubbed);
			}
			auto eventLogger = spdlog::get("anslogger_event");
			if (eventLogger) eventLogger->info("{}: {}", msg, scrubbed);

		}
		catch (const std::exception& e) {
			std::cerr << "SPDLogger::LogInfo - Exception: " << e.what() << std::endl;
		}
	}
	void SPDLogger::LogWarn(const std::string& source, const std::string& message, const std::string& fileSource, int lineSource) {
		std::string messageSource = "[" + path_to_filename(fileSource) + "] [" + std::to_string(lineSource) + "] " + source;
		std::string scrubbed = strip_log_prefixes(message);

		try {
			auto logger = spdlog::get("anslogger_console");
			if (logger) {
				logger->warn("{}: {}", messageSource, scrubbed);
			}
			else {
				std::cerr << "SPDLogger::LogWarn - Logger not initialized: anslogger_console" << std::endl;
			}
		}
		catch (const std::exception& e) {
			std::cerr << "SPDLogger::LogWarn - Exception: " << e.what() << std::endl;
		}
	}
	void SPDLogger::LogDebug(const std::string& source, const std::string& message, const std::string& fileSource, int lineSource) {
		std::string msg = "[" + path_to_filename(fileSource) + "] [" + std::to_string(lineSource) + "] " + source;
		std::string scrubbed = strip_log_prefixes(message);

		try {
			auto logger = spdlog::get("anslogger_console");
			if (logger) {
				logger->debug("{}: {}", msg, scrubbed);
			}
		}
		catch (const std::exception& e) {
			std::cerr << "SPDLogger::LogInfo - Exception: " << e.what() << std::endl;
		}
	}
	void SPDLogger::LogError(const std::string& source, const std::string& message, const std::string& fileSource, int lineSource) {
		std::string msg = "[" + path_to_filename(fileSource) + "] [" + std::to_string(lineSource) + "] " + source;
		std::string scrubbed = strip_log_prefixes(message);

		try {
			auto logger = spdlog::get("anslogger_console");
			if (logger) logger->error("{}: {}", msg, scrubbed);

			auto eventLogger = spdlog::get("anslogger_event");
			if (eventLogger) eventLogger->error("{}: {}", msg, scrubbed);
		}
		catch (const std::exception& e) {
			std::cerr << "SPDLogger::LogError - Exception: " << e.what() << std::endl;
		}
	}
	void SPDLogger::LogFatal(const std::string& source, const std::string& message, const std::string& fileSource, int lineSource) {
		std::string msg = "[" + path_to_filename(fileSource) + "] [" + std::to_string(lineSource) + "] " + source;
		std::string scrubbed = strip_log_prefixes(message);

		try {
			auto logger = spdlog::get("anslogger_console");
			if (logger) logger->critical("{}: {}", msg, scrubbed);

			auto eventLogger = spdlog::get("anslogger_event");
			if (eventLogger) eventLogger->critical("{}: {}", msg, scrubbed);
		}
		catch (const std::exception& e) {
			std::cerr << "SPDLogger::LogFatal - Exception: " << e.what() << std::endl;
		}
	}
}

// C facade — exported with stable C linkage so ANSLIB (and any other consumer)
// can resolve via GetProcAddress without coupling to the C++ class ABI.
// Each call delegates to the process-wide SPDLogger singleton; the magic-static
// inside GetInstance handles thread-safe one-time initialisation.
extern "C" {
    ANSLICENSE_API void ANSLogger_LogInfo(const char* src, const char* msg, const char* file, int line) {
        try {
            ANSCENTER::SPDLogger::GetInstance("ANSLIB").LogInfo(
                src ? src : "", msg ? msg : "", file ? file : "", line);
        } catch (...) {}
    }
    ANSLICENSE_API void ANSLogger_LogError(const char* src, const char* msg, const char* file, int line) {
        try {
            ANSCENTER::SPDLogger::GetInstance("ANSLIB").LogError(
                src ? src : "", msg ? msg : "", file ? file : "", line);
        } catch (...) {}
    }
    ANSLICENSE_API void ANSLogger_LogFatal(const char* src, const char* msg, const char* file, int line) {
        try {
            ANSCENTER::SPDLogger::GetInstance("ANSLIB").LogFatal(
                src ? src : "", msg ? msg : "", file ? file : "", line);
        } catch (...) {}
    }

    // DebugView emit path used by ANS_DBG and ANSLIB_DBG. Single choke point
    // so the prefix scrubber lives in one place; macros just hand off the
    // already-formatted "[Tag] body\n" buffer here.
    ANSLICENSE_API void ANSCENTER_DebugLog(const char* buf) {
        if (!buf) return;
        try {
            std::string scrubbed = strip_log_prefixes(buf);
            OutputDebugStringA(scrubbed.c_str());
            std::fputs(scrubbed.c_str(), stderr);
            std::fflush(stderr);
        } catch (...) {}
    }
}

namespace ANSCENTER
{

	EngineType ANSCENTER::ANSLicenseHelper::CheckHardwareInformation()
	{
		ANS_DBG("HWInfo", "CheckHardwareInformation: starting hardware detection");
		bool cpuIntelAvailable = false;
		bool gpuNvidiaAvailable = false;
		bool gpuAMDAvailable = false;
		bool gpuOpenVINOAvailable = false;
		// ----------------------------------------------------------------
		// CPU — Intel socket detection (OpenVINO fallback)
		// ----------------------------------------------------------------
		auto sockets = hwinfo::getAllSockets();
		for (auto& socket : sockets) {
			const auto& cpu = socket.CPU();
			ANS_DBG("HWInfo", "CPU vendor=%s model=%s", cpu.vendor().c_str(), cpu.modelName().c_str());
			if (cpu.vendor() == "GenuineIntel")
				cpuIntelAvailable = true;
		}

		// ----------------------------------------------------------------
		// GPU — classify all adapters by vendor string
		// ----------------------------------------------------------------
		auto gpus = hwinfo::getAllGPUs();
		ANS_DBG("HWInfo", "Found %zu GPU adapter(s)", gpus.size());
		for (auto& gpu : gpus) {
			std::string vendor = gpu.vendor();
			ANS_DBG("HWInfo", "GPU: name=%s vendor=%s", gpu.name().c_str(), vendor.c_str());
			std::cout << "[HWInfo] Detected GPU: " << gpu.name() << " (Vendor: " << vendor << ")" << std::endl;
			// Normalize to uppercase for reliable substring matching
			std::transform(vendor.begin(), vendor.end(),
				vendor.begin(), ::toupper);

			if (vendor.find("NVIDIA") != std::string::npos)
				gpuNvidiaAvailable = true;
			if (vendor.find("INTEL") != std::string::npos)
				gpuOpenVINOAvailable = true;
			// AMD cards report as "AMD", "Advanced Micro Devices", or
			// legacy "ATI Technologies" branding.
			// NOTE: "ATI" alone is too broad — "INTEL CORPORATION" contains
			// "ATI" inside "CORPOR-ATI-ON", causing a false positive.
			// Use "ATI " (with trailing space) to match "ATI Technologies"
			// or "ATI Radeon" without matching "CORPORATION".
			if (vendor.find("AMD") != std::string::npos ||
				vendor.find("ADVANCED MICRO") != std::string::npos ||
				vendor.find("ATI ") != std::string::npos)
				gpuAMDAvailable = true;
		}
		ANS_DBG("HWInfo", "Detection: cpuIntel=%d gpuNvidia=%d gpuAMD=%d gpuOpenVINO=%d",
			cpuIntelAvailable, gpuNvidiaAvailable, gpuAMDAvailable, gpuOpenVINOAvailable);

		// ----------------------------------------------------------------
		// Priority: NVIDIA > AMD > Intel OpenVINO > CPU
		//
		// Dedicated GPU always wins over integrated Intel graphics.
		// OpenVINO runs on the Intel CPU-side so it is last resort
		// before pure CPU fallback.
		// ----------------------------------------------------------------
		if (gpuNvidiaAvailable) {
			std::cout << "[HWInfo] Engine: NVIDIA GPU -> CUDA" << std::endl;
			ANS_DBG("HWInfo", "Selected: NVIDIA_GPU (CUDA)");
			return EngineType::NVIDIA_GPU;
			//return EngineType::OPENVINO_GPU;
		}
		if (gpuAMDAvailable) {
			std::cout << "[HWInfo] Engine: AMD GPU -> DirectML" << std::endl;
			ANS_DBG("HWInfo", "Selected: AMD_GPU (DirectML)");
			return EngineType::AMD_GPU;
		}
		if (gpuOpenVINOAvailable) {
			std::cout << "[HWInfo] Engine: Intel CPU -> OpenVINO" << std::endl;
			ANS_DBG("HWInfo", "Selected: OPENVINO_GPU (OpenVINO)");
			return EngineType::OPENVINO_GPU;
		}

		std::cout << "[HWInfo] Engine: CPU (fallback)" << std::endl;
		ANS_DBG("HWInfo", "Selected: CPU (fallback)");
		return EngineType::CPU;
	}

	std::string ANSLicenseHelper::DecodeBase64(std::string base64String) {
		BASE64 base64;
		vector<unsigned char> result = base64.decode(base64String.c_str(), -1, nullptr);
		string str(result.begin(), result.end());
		return str.c_str();
	}

	bool ANSLicenseHelper::isPathSafe(const std::string& pathStr) {
		// Basic safety checks - customize as needed
		if (pathStr.empty()) return false;
		if (pathStr == "/" || pathStr == "\\") return false;
		if (pathStr == "C:\\" || pathStr == "c:\\") return false;
		if (pathStr.find("..") != std::string::npos) return false; // Prevent path traversal

		// Add more safety checks as needed for your use case
		return true;
	}
	bool ANSLicenseHelper::deleteRecursively(const std::string& pathStr, bool safeMode) {
		std::error_code ec;
		std::filesystem::path path(pathStr);

		// Additional safety checks in safe mode
		if (safeMode && !isPathSafe(pathStr)) {
			std::cerr << "Unsafe path detected, deletion aborted: " << pathStr << std::endl;
			return false;
		}

		// Check if path exists
		if (!std::filesystem::exists(path, ec)) {
			if (ec) {
				std::cerr << "Error checking existence: " << ec.message() << std::endl;
				return false;
			}
			std::cout << "Path does not exist: " << pathStr << std::endl;
			return true;
		}

		// Get info about what we're deleting
		bool isDirectory = std::filesystem::is_directory(path, ec);
		if (ec) {
			std::cerr << "Error checking if path is directory: " << ec.message() << std::endl;
			return false;
		}

		std::cout << "Deleting " << (isDirectory ? "directory" : "file") << ": " << pathStr << std::endl;

		// Remove all contents recursively
		std::uintmax_t removed = std::filesystem::remove_all(path, ec);

		if (ec) {
			std::cerr << "Error during deletion: " << ec.message() << std::endl;
			return false;
		}

		std::cout << "Successfully removed " << removed << " files/directories" << std::endl;
		return true;
	}
	bool ANSLicenseHelper::LicenseVerification(std::string licenseKey, int productId, std::string registrationName) {
		//SPDLogger&      _logger = SPDLogger::GetInstance("License", false);
		try {
			int _enableFeature;
			int _productId;
			std::string privateKey = GetSdkKey();
			int activationResult = 0;
			std::string licensingServiceURL = "https://licensingservice.anscenter.com/";
			ANSCENTER::ANSLSHelper ansHelper(privateKey, licensingServiceURL);
			ansHelper.SetupLicenseTemplate();
			std::string hwid = ansHelper.GetCurrentHardwareId();
			std::string licenseDirectory = GetLicenseDir();
			std::string licenseKeyFile;
			std::vector<std::string> licenseKeyFiles = ListFilesInFolder(licenseDirectory);

			// 1. License key is supplied
			if (!licenseKey.empty()) {
				std::string licenseKeyFileName = Dash2Underscore(licenseKey) + ".json";
				licenseKeyFile = CreateFilePath(licenseDirectory, licenseKeyFileName);

				if (FileExist(licenseKeyFile)) { // We only need to check if the license key valid and expiration date
					// Read the license file key
					std::string _validationData;
					std::string _activationKey;
					std::string _licenseKey;
					if (ReadLicenseKeyFile(licenseKeyFile, _licenseKey, _activationKey, _validationData)) {
						std::string _registrationName;
						_registrationName = DecodeBase64(_validationData); // Decode 
						std::string licenseData = ansHelper.GetLicenseData(_licenseKey);
						int expirationDate = GetExpirationDate(licenseData, _productId);
						std::cout << "Expiration date:" << expirationDate << std::endl;
						bool validLicenseKey = ansHelper.CheckLicenseKey(_licenseKey, _registrationName, _productId, _enableFeature);
						if (validLicenseKey) {
							if (expirationDate > 0) {
								return true;
							}
							else {
								std::cout<<"ANSUtilityHelper::LicenseVerification.", "License key is expired.";
								return false;
							}
						}
					}
				}
				else { // This should be the validation for a new API
					std::string _activationKey;
					activationResult = ansHelper.ValidateLicense(licenseKey, registrationName, _activationKey);
					if (activationResult == LicenseKeyStatus::SUCCESS) {
						return true; // Success
					}
				}
			}

			// 2. No license key, then list the existing license files
			if (licenseKeyFiles.size() > 0) {
				size_t lSize = licenseKeyFiles.size();
				for (size_t i = 0; i < lSize; i++) {
					std::string licenseFile = licenseKeyFiles[i];
					// Read the license file key
					std::string _validationData;
					std::string _activationKey;
					std::string _licenseKey;
					if (ReadLicenseKeyFile(licenseFile, _licenseKey, _activationKey, _validationData)) {
						std::string _registrationName = DecodeBase64(_validationData); // Decode 
						std::string licenseData = ansHelper.GetLicenseData(_licenseKey);
						int expirationDate = GetExpirationDate(licenseData, _productId);
						if ((_productId == 1003) || (_productId == productId)) { // If it is ANSVIS or correct Product
							bool validLicense = ansHelper.CheckLicenseKey(_licenseKey, _registrationName, _productId, _enableFeature);
							if (validLicense) {
								if (expirationDate > 0) {
									return true;
								}
								else {
									std::cout << "ANSUtilityHelper::LicenseVerification.", "License key is expired.";
									return false;
								}
							}
						}
					}
				}
			}


			// 3. At this stage, it is still no license. we need to generate the trial license
			std::string trialLicenseKey = GetTrialLicenseKey(registrationName);
			if (trialLicenseKey.empty()) {
				// Generate key based on productId for 30 day if it is in run-time mode
				trialLicenseKey = ansHelper.GenerateKey(productId, registrationName, 0, true);
				UpdateTrialLicenseKey(trialLicenseKey, registrationName);
			}
			// We need to check the license key
			bool valid = ansHelper.CheckLicenseKey(trialLicenseKey, registrationName, productId, _enableFeature);
			if (!valid) {
				std::cout << "ANSUtilityHelper::LicenseVerification.", "Trial license is not valid.";
				return false;
			}

			// Check license data
			std::string licenseData = ansHelper.GetLicenseData(trialLicenseKey);
			int expirationDate = GetExpirationDate(licenseData, _productId);
			if (expirationDate > 0) {
				return true;
			}
			else {
				std::cout << "ANSUtilityHelper::LicenseVerification.", "Trial license key is expired.";
				return false;
			}
		}
		catch (std::exception& e) {
			std::cout << "ANSUtilityHelper::LicenseVerification. Error"<< e.what();
			return false;
		}
	}


	int ANSLicenseHelper::ActivateTrialLicense(std::string productName) {
		//SPDLogger&      _logger = SPDLogger::GetInstance("License");
		try {
			int productId;
			std::string registrationName;
			std::string  privateKey = GetSdkKey();
			std::string licensingServiceURL = "https://licensingservice.anscenter.com/";
			ANSCENTER::ANSLSHelper ansHelper(privateKey, licensingServiceURL);
			ansHelper.SetupLicenseTemplate();
			if (productName == "ANNHUB") {//1000
				registrationName = "ANNHUB-LV";
				productId = 1000;
			}
			else if (productName == "DLHUB") {//1001
				registrationName = "DLHUB-LV";
				productId = 1001;
			}
			else if (productName == "ODHUB") {//1002
				registrationName = "ODHUB-LV";
				productId = 1002;
			}
			else if (productName == "ANSVIS") {//1003
				registrationName = "ANSVIS";
				productId = 1003;
			}
			else if (productName == "ANSFR") {//1004
				registrationName = "ANSFR";
				productId = 1004;
			}
			else if (productName == "ANSOCR") {//1005
				registrationName = "ANSOCR";
				productId = 1005;
			}
			else if (productName == "ANSALPR") {//1006
				registrationName = "ANSALPR";
				productId = 1006;
			}	
			else if (productName == "ANSCV") {//1007
				registrationName = "ANSCV";
				productId = 1007;
			}
			else if (productName == "ANSTS") {//1008
				registrationName = "ANSTS";
				productId = 1008;
			}
			else {
				registrationName = productName;
				productId = 1009;
			}

			std::string trialLicenseKey = GetTrialLicenseKey(registrationName);
			if (trialLicenseKey.empty()) {
				// Generate key based on productId for 30 day if it is in run-time mode
				trialLicenseKey = ansHelper.GenerateKey(productId, registrationName, 0, true);
				UpdateTrialLicenseKey(trialLicenseKey, registrationName);
			}
			return 1;
		}
		catch (std::exception& e) {
			std::cout << "ActivateTrialLicense::LicenseVerification. Error"<< e.what();
			return 0;
		}
	}
	int ANSLicenseHelper::DeactivateLicense(std::string productName) {
		std::string licenseDirectory = GetLicenseDir();
		std::vector<std::string> licenseKeyFiles = ListFilesInFolder(licenseDirectory);
		std::string  privateKey = GetSdkKey();
		std::string licensingServiceURL = "https://licensingservice.anscenter.com/";
		ANSCENTER::ANSLSHelper ansHelper(privateKey, licensingServiceURL);
		ansHelper.SetupLicenseTemplate();
		size_t lSize = licenseKeyFiles.size();
		if (lSize > 0) {
			// run for loop from 0 to vecSize
			for (size_t i = 0; i < lSize; i++)
			{
				// Add license to Json format
				std::string licenseFile = licenseKeyFiles[i];
				std::string _validationData;
				std::string _activationKey;
				std::string _licenseKey;
				std::string _licenseData;
				std::string _productName;
				std::string _registrationName;
				int		_enableFeature=0;
				int	   _productId;
				// Read the license file key
				if (ReadLicenseKeyFile(licenseFile, _licenseKey, _activationKey, _validationData)) {
					std::string licenseData = ansHelper.GetLicenseData(_licenseKey);
					int expirationDate = GetExpirationDate(licenseData, _productId);
					switch (_productId) {
					case ANSCENTER::ANSPRODUCTS::ANNHUB://1000
						_productName = "ANNHUB";
						break;
					case ANSCENTER::ANSPRODUCTS::DLHUB://1001
						_productName = "DLHUB";
						break;
					case ANSCENTER::ANSPRODUCTS::ODHUB://1002
						_productName = "ODHUB";
						break;
					case ANSCENTER::ANSPRODUCTS::ANSVIS://1003
						_productName = "ANSVIS";
						break;
					case ANSCENTER::ANSPRODUCTS::ANS_FACERECOGNIZE://1004
						_productName = "ANSFR";
						break;
					case ANSCENTER::ANSPRODUCTS::ANS_OCR://1005
						_productName = "ANSOCR";
						break;
					case ANSCENTER::ANSPRODUCTS::ANS_ALPR://1006
						_productName = "ANSALPR";
						break;
					case ANSCENTER::ANSPRODUCTS::ANS_CV://1007
						_productName = "ANSCV";
						break;
					case ANSCENTER::ANSPRODUCTS::ANS_TS://1008
						_productName = "ANSTS";
						break;
					}
					if (_productName == productName) {
						// Delete the license file
						bool result= DeleteLicenseFile(licenseFile);
						if (result)return 1;
						else return 0;
					}
				}
			}
			return 1;
		}
		return 0;
	}
	int ANSLicenseHelper::ActivateLicense(std::string productName, std::string licenseKey) {
		std::string  privateKey = GetSdkKey();
		std::string licensingServiceURL = "https://licensingservice.anscenter.com/";
		ANSCENTER::ANSLSHelper ansHelper(privateKey, licensingServiceURL);
		ansHelper.SetupLicenseTemplate();
		std::string registrationName; 
		std::string _activationKey;
		if (productName == "ANNHUB") {
			registrationName = "ANNHUB-LV";//1000
		}
		else if (productName == "DLHUB") {
			registrationName = "DLHUB-LV";//1001
		}
		else if (productName == "ODHUB") {
			registrationName = "ODHUB-LV";//1002
		}
		else if (productName == "ANSVIS") {//1003
			registrationName = "ANSVIS";
		}
		else if (productName == "ANSFR") {//1004
			registrationName = "ANSFR";
		}
		else if (productName == "ANSOCR") {//1005
			registrationName = "ANSOCR";
		}
		else if (productName == "ANSALPR") {//1006
			registrationName = "ANSALPR";
		}
		else if (productName == "ANSCV") {//1007
			registrationName = "ANSCV";
		}
		else if (productName == "ANSTS") {//1008
			registrationName = "ANSTS";
		}
		else {
			registrationName = productName;
		}
		int activationResult =ansHelper.ValidateLicense(licenseKey, registrationName, _activationKey);
		return activationResult;
	}

	int ANSLicenseHelper::ActivateLicenseWithCustomHWID(std::string productName, std::string licenseKey, std::string hwid, std::string &activationKey) {
		std::string  privateKey = GetSdkKey();
		std::string licensingServiceURL = "https://licensingservice.anscenter.com/";
		ANSCENTER::ANSLSHelper ansHelper(privateKey, licensingServiceURL);
		ansHelper.SetupLicenseTemplate();
		std::string registrationName;
		if (productName == "ANNHUB") {
			registrationName = "ANNHUB-LV";//1000
		}
		else if (productName == "DLHUB") {
			registrationName = "DLHUB-LV";//1001
		}
		else if (productName == "ODHUB") {
			registrationName = "ODHUB-LV";//1002
		}
		else if (productName == "ANSVIS") {//1003
			registrationName = "ANSVIS";
		}
		else if (productName == "ANSFR") {//1004
			registrationName = "ANSFR";
		}
		else if (productName == "ANSOCR") {//1005
			registrationName = "ANSOCR";
		}
		else if (productName == "ANSALPR") {//1006	
			registrationName = "ANSALPR";
		}
		else if (productName == "ANSCV") {//1007
			registrationName = "ANSCV";
		}
		else if (productName == "ANSTS") {//1008
			registrationName = "ANSTS";
		}
		else {
			registrationName = productName;
		}
		int activationResult = ansHelper.ActivateLicenseWithCustomHWID(licenseKey, registrationName, hwid, activationKey);

		return activationResult;
	}

	std::string ANSLicenseHelper::DecodeValidationData(const char* validationData) {
		std::string licenseKeyValidationData = ANSUtility::DecodeBase64Utility(validationData);
		return licenseKeyValidationData;
	}

	std::string ANSLicenseHelper::GenerateActivationDataFile(std::string activationKey, std::string licenseKey, std::string hardwareId, std::string validationData) {
		try {
			boost::property_tree::ptree root;
			boost::property_tree::ptree dataNode;
			dataNode.put("activation_key", activationKey);
			dataNode.put("license_key", licenseKey);
			dataNode.put("hardware_id", hardwareId);
			dataNode.put("license_key_validation_data", validationData);
			root.add_child("activationData", dataNode);
			std::ostringstream stream;
			boost::property_tree::write_json(stream, root,false);
			std::string st = stream.str();
			return st;
		}
		catch (std::exception& e) {
			std::cerr << "An error occurred: " << e.what() << std::endl;
			return "";
		}
	}


	bool ANSLicenseHelper::RemoveTrialLicenseKeys(std::string registrationName) {
		return RemoveTrialLicenseKey(registrationName);
	}

	std::string ANSLicenseHelper::ListLicenses() {
		//SPDLogger&      _logger = SPDLogger::GetInstance("License");
		try {
			std::vector<ANSCENTER::ANSLicenseInfo> ansLicenseInfos;
			std::vector<std::string> ansLicenseCollection;

			std::string licenseDirectory = GetLicenseDir();
			std::vector<std::string> licenseKeyFiles = ListFilesInFolder(licenseDirectory);
			std::string  privateKey = GetSdkKey();
			std::string licensingServiceURL = "https://licensingservice.anscenter.com/";
			ANSCENTER::ANSLSHelper ansHelper(privateKey, licensingServiceURL);
			ansHelper.SetupLicenseTemplate();
			std::string hwid = ansHelper.GetCurrentHardwareId();
			std::cout << "Current HWID: " << mask_secret(hwid) << std::endl;
			boost::property_tree::ptree root;
			size_t lSize = licenseKeyFiles.size();
			boost::property_tree::ptree children;

			// All ANS Products at this stage
			ansLicenseCollection.push_back("ANNHUB");//1000
			ansLicenseCollection.push_back("DLHUB");//1001
			ansLicenseCollection.push_back("ODHUB");//1002
			ansLicenseCollection.push_back("ANSVIS");//1003
			ansLicenseCollection.push_back("ANSFR");//1004
			ansLicenseCollection.push_back("ANSOCR");//1005
			ansLicenseCollection.push_back("ANSALPR");//1006
			ansLicenseCollection.push_back("ANSCV");//1007
			ansLicenseCollection.push_back("ANSTS");//1008

			for (auto& element : ansLicenseCollection) {
				boost::property_tree::ptree dataNode;
				ANSCENTER::ANSLicenseInfo ansLicenseInfo;	
				ansLicenseInfo.productName = element;
				ansLicenseInfo.licenseStatus = ANSCENTER::ANSLICENSESTATUS::UNLICENSE;
				// 1. Get installed license first
				if (lSize > 0) {
					// run for loop from 0 to vecSize
					for (size_t i = 0; i < lSize; i++)
					{
						// Add license to Json format
						std::string licenseFile = licenseKeyFiles[i];
						std::string _validationData;
						std::string _activationKey;
						std::string _licenseKey;
						std::string _licenseData;
						std::string _productName;
						std::string _registrationName;
						int _enableFeature;
						int expirationDate = 0;
						int	   _productId;
						// Read the license file key
						if (ReadLicenseKeyFile(licenseFile, _licenseKey, _activationKey, _validationData)) {
							try {
								_licenseData = ansHelper.GetLicenseData(_licenseKey);
								expirationDate = GetExpirationDate(_licenseData, _productId);
							}
							catch (std::exception& e) {
								std::cerr << "An error occurred: " << e.what() << std::endl;
								_productId = -1;
								expirationDate = 0;
							}
							if (_productId > 0) {
								switch (_productId) {
								case ANSCENTER::ANSPRODUCTS::ANNHUB:
									_productName = "ANNHUB";
									break;
								case ANSCENTER::ANSPRODUCTS::DLHUB:
									_productName = "DLHUB";
									break;
								case ANSCENTER::ANSPRODUCTS::ODHUB:
									_productName = "ODHUB";
									break;
								case ANSCENTER::ANSPRODUCTS::ANSVIS:
									_productName = "ANSVIS";
									break;
								case ANSCENTER::ANSPRODUCTS::ANS_FACERECOGNIZE:
									_productName = "ANSFR";
									break;
								case ANSCENTER::ANSPRODUCTS::ANS_OCR:
									_productName = "ANSOCR";
									break;
								case ANSCENTER::ANSPRODUCTS::ANS_ALPR:
									_productName = "ANSALPR";
									break;
								case ANSCENTER::ANSPRODUCTS::ANS_CV:
									_productName = "ANSCV";
									break;
								case ANSCENTER::ANSPRODUCTS::ANS_TS:
									_productName = "ANSTS";
									break;
								}
								if (_productName == element) {
									_registrationName = ANSUtility::DecodeBase64Utility(_validationData.c_str());// Decode 
									bool validLicenseKey = ansHelper.CheckLicenseKey(_licenseKey, _registrationName, _productId, _enableFeature);
									ansLicenseInfo.productName = _productName;
									ansLicenseInfo.licenseKey = _licenseKey;
									ansLicenseInfo.activationKey = _activationKey;
									ansLicenseInfo.hardwareId = hwid;
									dataNode.put("product_name", _productName);
									dataNode.put("license_key", _licenseKey);
									dataNode.put("activation_key", _activationKey);
									dataNode.put("hardware_id", hwid);
									if (validLicenseKey) {
										if (expirationDate > 0) {
											ansLicenseInfo.licenseStatus = ANSCENTER::ANSLICENSESTATUS::ACTIVE;
											dataNode.put("license_status", "active");
										}
										else {
											ansLicenseInfo.licenseStatus = ANSCENTER::ANSLICENSESTATUS::EXPIRED;
											dataNode.put("license_status", "expired");
										}
										ansLicenseInfo.remainingDays = expirationDate;
										ansLicenseInfo.enableFeature = _enableFeature;
										dataNode.put("remaining_days", expirationDate);
										dataNode.put("enable_feature", _enableFeature);
										children.push_back(std::make_pair("", dataNode));
									}
									else {
										ansLicenseInfo.licenseStatus = ANSCENTER::ANSLICENSESTATUS::UNLICENSE;
										dataNode.put("license_status", "unlicensed");
									}
								}
							}	
						}
					}
				}

				//2. Check if it is Unlicense then we will check for trial license key
				if (ansLicenseInfo.licenseStatus == ANSCENTER::ANSLICENSESTATUS::UNLICENSE) {
					std::string registrationName;
					int productId;
					if (ansLicenseInfo.productName == "ANNHUB") {
						registrationName = "ANNHUB-LV";
						productId = 1000;
					}
					else if (ansLicenseInfo.productName == "DLHUB") {
						registrationName = "DLHUB-LV";
						productId = 1001;
					}
					else if (ansLicenseInfo.productName == "ODHUB") {
						registrationName = "ODHUB-LV";
						productId = 1002;
					}
					else if (ansLicenseInfo.productName == "ANSVIS") {
						registrationName = "ANSVIS";
						productId = 1003;
					}
					else if (ansLicenseInfo.productName == "ANSFR") {
						registrationName = "ANSFR";
						productId = 1004;
					}
					else if (ansLicenseInfo.productName == "ANSOCR") {
						registrationName = "ANSOCR";
						productId = 1005;
					}
					else if (ansLicenseInfo.productName == "ANSALPR") {
						registrationName = "ANSALPR";
						productId = 1006;
					}
					else if (ansLicenseInfo.productName == "ANSCV") {
						registrationName = "ANSCV";
						productId = 1007;
					}
					else if (ansLicenseInfo.productName == "ANSTS") {
						registrationName = "ANSTS";
						productId = 1008;
					}
					else {
						registrationName = ansLicenseInfo.productName;
						productId = 1009;
					}
					dataNode.put("product_name", ansLicenseInfo.productName);
					std::string trialLicenseKey = GetTrialLicenseKey(registrationName);
					if (trialLicenseKey.empty()) {
						ansLicenseInfo.licenseStatus = ANSCENTER::ANSLICENSESTATUS::UNLICENSE;
						dataNode.put("license_key", "");
						dataNode.put("activation_key", "");
						dataNode.put("hardware_id", hwid);
						dataNode.put("license_status", "unlicensed");
						dataNode.put("remaining_days", 0);
						dataNode.put("enable_feature", 0);

					}
					else {
						ansLicenseInfo.licenseStatus = ANSCENTER::ANSLICENSESTATUS::TRIAL;
						dataNode.put("license_key", trialLicenseKey);
						dataNode.put("activation_key", "");
						dataNode.put("hardware_id", hwid);
						dataNode.put("license_status", "trial");
						std::string licenseData = ansHelper.GetLicenseData(trialLicenseKey);
						int expirationDate = GetExpirationDate(licenseData, productId);
						dataNode.put("remaining_days", expirationDate);
						dataNode.put("enable_feature", 0);
					}	
					children.push_back(std::make_pair("", dataNode));
				}
			}
			root.add_child("licenses", children);
			std::ostringstream stream;
			boost::property_tree::write_json(stream, root,false);
			std::string st = stream.str();
			return st;
		}
		catch (std::exception& e) {
			std::cerr << "ANSUtilityHelper::ListLicenses. Error"<< e.what();
			return "";
		}
	}
	/************************ANSLSHelper Implementation************************************/
	ANSLSHelper::ANSLSHelper()
	{
		try {
			this->_privateKey = GetSdkKeyW();
			this->_licenseServiceURL = _T("https://licensingservice.anscenter.com/");
			this->_sdkLicenseKey = _T("MYNSU-GBQ2Q-SF5U5-S3RVF-5ZKFD");
			SDKRegistration::SetLicenseKey(_sdkLicenseKey.c_str());
			this->SetupLicenseTemplate();
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			ex->Destroy();
			this->_logger.LogFatal("ANSLSHelper::ANSLSHelper", ex->GetExceptionMessage(), __FILE__, __LINE__);
	
		}
	}
	ANSLSHelper::ANSLSHelper(std::string licenseServiceURL)
	{
		try {
			this->_privateKey = GetSdkKeyW();
			this->_licenseServiceURL = String2WString(licenseServiceURL);
			this->_sdkLicenseKey = _T("MYNSU-GBQ2Q-SF5U5-S3RVF-5ZKFD");
			SDKRegistration::SetLicenseKey(_sdkLicenseKey.c_str());
			this->SetupLicenseTemplate();
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			ex->Destroy();
			this->_logger.LogFatal("ANSLSHelper::ANSLSHelper", ex->GetExceptionMessage(), __FILE__, __LINE__);

		}
	}
	ANSLSHelper::ANSLSHelper(std::string privateKey, std::string licenseServiceURL)
	{
		try {
			if (privateKey.empty()) {
				this->_privateKey = GetSdkKeyW();
			}
			else {
				this->_privateKey = String2WString(privateKey);
			}
			if (licenseServiceURL.empty()) {
				this->_licenseServiceURL = _T("https://licensingservice.anscenter.com/");
			}
			this->_licenseServiceURL = String2WString(licenseServiceURL);
			this->_sdkLicenseKey = _T("MYNSU-GBQ2Q-SF5U5-S3RVF-5ZKFD");
			SDKRegistration::SetLicenseKey(_sdkLicenseKey.c_str());
			this->SetupLicenseTemplate();
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			ex->Destroy();
			this->_logger.LogFatal("ANSLSHelper::ANSLSHelper", ex->GetExceptionMessage(), __FILE__, __LINE__);
		}
		
	}
	ANSLSHelper::ANSLSHelper(std::string privateKey, std::string publicKey, std::string licenseServiceURL)
	{
		try {
			if (privateKey.empty()) {
				this->_privateKey = GetSdkKeyW();
			}
			else {
				this->_privateKey = String2WString(privateKey);
			}
			if (publicKey.empty()) {
				this->_publicKey = _T("EKJWDQEL3KQvCPLEjz0fjQiSAF1qChm65z6/PaZISNtL1wib6QA=");
			}
			else {
				this->_publicKey = String2WString(publicKey);
			}
			if (licenseServiceURL.empty()) {
				this->_licenseServiceURL = _T("https://licensingservice.anscenter.com/");
			}
			this->_licenseServiceURL = String2WString(licenseServiceURL);
			this->_sdkLicenseKey = _T("MYNSU-GBQ2Q-SF5U5-S3RVF-5ZKFD");
			SDKRegistration::SetLicenseKey(_sdkLicenseKey.c_str());
			this->SetupLicenseTemplate();
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			ex->Destroy();
			this->_logger.LogFatal("ANSLSHelper::ANSLSHelper", ex->GetExceptionMessage(), __FILE__, __LINE__);
		}
	}
	ANSLSHelper::~ANSLSHelper() {
		try {
			if (this->_license != NULL) {
				//delete _license;   // Do not know why it does not work
				this->_license = NULL;
			}
			if (this->_licenseTemplate != NULL) {
				LicenseTemplate::Destroy(_licenseTemplate);
				this->_licenseTemplate = NULL;
			}
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			ex->Destroy();
			this->_logger.LogFatal("ANSLSHelper::~ANSLSHelper", ex->GetExceptionMessage(), __FILE__, __LINE__);
		}

	}
	void   ANSLSHelper::SetLicenseServiceURL(std::string licenseServiceURL) {
		this->_licenseServiceURL = String2WString(licenseServiceURL);
	}
	void ANSLSHelper::LoadLicense(std::string licenseKey) {

		if (!this->_license != NULL) delete this->_license;
			this->_license = new License();
			std::string licenseFileName = Dash2Underscore(licenseKey)+".json";
			std::string licenseFolder = GetLicenseDir();
			std::string licenseFilePath = CreateFilePath(licenseFolder, licenseFileName);
			this->_licenseFilePath = String2WString(licenseFilePath);
		try {
			if (LoadLicenseContent().empty()) 
			{
				delete this->_license;
				this->_license = NULL;
			} 
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			ex->Destroy();
			this->_logger.LogFatal("ANSLSHelper::LoadLicense", ex->GetExceptionMessage(), __FILE__, __LINE__);
			delete this->_license;
			this->_license = NULL;
		}
	}
	std::string ANSLSHelper::SaveLicense() {
		if (this->_license != NULL) {
			try {
				FILE* licenseFile;
				const char* jsonLicense = this->_license->SaveJson();
				std::string result(jsonLicense);
				if (!result.empty()) {
					if ((licenseFile = _tfopen(WString2String(_licenseFilePath).c_str(), "wt")) != NULL) {
						fputs(jsonLicense, licenseFile);
						fclose(licenseFile);
					}
				}
				else {
					this->_logger.LogError("ANSLSHelper::SaveLicense", "XML content is empty", __FILE__, __LINE__);
				}
				return result;
			}
			catch (ANSCENTER::Licensing::Exception* ex) {
				this->_logger.LogFatal("ANSLSHelper::SaveLicense", ex->GetExceptionMessage(), __FILE__, __LINE__);
				ex->Destroy();
				return "";
			}
		}
		else {
			this->_logger.LogError("ANSLSHelper::SaveLicense", "License object is NULL. Cannot save license content", __FILE__, __LINE__);
			return "";
		}
	}
	std::string ANSLSHelper::LoadLicenseContent() {
		try
		{
			std::string jsonContent = ReadFileContent(WString2String(_licenseFilePath));
			if (!jsonContent.empty()) {
				this->_license->LoadJson(jsonContent.c_str());
				return jsonContent;
			}	
			else {
				this->_logger.LogDebug("ANSLSHelper::LoadLicenseContent", "Json file content is empty", __FILE__, __LINE__);
				this->_license = NULL;
				return "";
			}

		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			this->_logger.LogFatal("ANSLSHelper::LoadLicenseContent", ex->GetExceptionMessage(), __FILE__, __LINE__);
			ex->Destroy();
			this->_license = NULL;
			return "";
		}
	}
	std::string ANSLSHelper::GetCurrentHardwareId() {
		try {
			const wchar_t* hwid = KeyHelper::GetCurrentHardwareId();
			std::wstring result_t = std::wstring(hwid);
			std::string result = WString2String(result_t);
			return result;
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			this->_logger.LogFatal("ANSLSHelper::GetCurrentHardwareId", ex->GetExceptionMessage(), __FILE__, __LINE__);
			ex->Destroy();
			return "";
		}
	}
	bool ANSLSHelper::MatchCurrentHardwareId(std::string hwid) {
		try {
			std::wstring wHwid = String2WString(hwid);
			return KeyHelper::MatchCurrentHardwareId(wHwid.c_str());
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			this->_logger.LogFatal("ANSLSHelper::MatchCurrentHardwareId", ex->GetExceptionMessage(), __FILE__, __LINE__);
			ex->Destroy();
			return false;
		}

	}
	bool ANSLSHelper::DectectClockmanipulation(int year, int month, int date) {
		try {
			return KeyHelper::DetectClockManipulation(year, month, date);
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			this->_logger.LogFatal("ANSLSHelper::DectectClockmanipulation", ex->GetExceptionMessage(), __FILE__, __LINE__);
			ex->Destroy();
			return true;
		}
	}
	bool ANSLSHelper::LoadLicenseTemplate(std::string templateFilePath) {
		try {
			if (FileExist(templateFilePath)) {
				if (this->_licenseTemplate) LicenseTemplate::Destroy(this->_licenseTemplate);
				this->_licenseTemplate = LicenseTemplate::Create();

				std::string result = ReadFileContent(templateFilePath);
				std::string lowercaseExtension = GetFileExtension(templateFilePath);
				// The private key and public key can be found in the license template file already. It should not be overloaded in here.
				if (lowercaseExtension == "json") {
					this->_licenseTemplate->LoadJson(result.c_str());
					this->_licenseTemplate->SetLicensingServiceUrl(this->_licenseServiceURL.c_str());
					const wchar_t* cpKey = this->_licenseTemplate->GetPrivateKey();
					std::wstring wpKey(cpKey);
					if (!wpKey.empty()) this->_privateKey = wpKey;
					return true;
				}
				else { // Otherwise it will be xml
					this->_licenseTemplate->LoadXml(result.c_str());
					this->_licenseTemplate->SetLicensingServiceUrl(this->_licenseServiceURL.c_str());
					const wchar_t* cpKey = this->_licenseTemplate->GetPrivateKey();
					std::wstring wpKey(cpKey);
					if (!wpKey.empty()) this->_privateKey = wpKey;
					return true;
				}
			}
			else {
				this->_logger.LogError("ANSLSHelper::LoadLicenseTemplate", "License template file is not exist or invalid", __FILE__, __LINE__);
				return false;
			}
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			this->_logger.LogFatal("ANSLSHelper::LoadLicenseTemplate", ex->GetExceptionMessage(), __FILE__, __LINE__);
			ex->Destroy();
			return false;
		}
		
	}
	std::string ANSLSHelper::SaveLicenseTemplate(std::string templateFilePath, bool savePrivateKey = false) {
		try {
			if (this->_licenseTemplate != NULL) {
				if (FileExist(templateFilePath)) {
					std::string lowercaseExtension = GetFileExtension(templateFilePath);
					if (lowercaseExtension == "json") {
						const char* licenseTemplateContent = this->_licenseTemplate->SaveJson(savePrivateKey);
						std::string result(licenseTemplateContent);
						return licenseTemplateContent;
					}
					else { // Otherwise it will be xml
						const char* licenseTemplateContent = this->_licenseTemplate->SaveXml(savePrivateKey);
						std::string result(licenseTemplateContent);
						return licenseTemplateContent;
					}
				}
				else {
					this->_logger.LogError("ANSLSHelper::LoadLicenseTemplate", "License template file is not exist or invalid", __FILE__, __LINE__);
					return ""; 
				}
			}
			else {
				this->_logger.LogError("ANSLSHelper::SaveLicenseTemplate", "this->_licenseTemplate is NULL", __FILE__, __LINE__);
				return "";
			} 
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			this->_logger.LogFatal("ANSLSHelper::SaveLicenseTemplate", ex->GetExceptionMessage(), __FILE__, __LINE__);
			ex->Destroy();
			return "";
		}

	}
	void ANSLSHelper::SetupLicenseTemplate() {
		try {
			if (this->_licenseTemplate) LicenseTemplate::Destroy(this->_licenseTemplate);
			this->_licenseTemplate = LicenseTemplate::Create();
			if (this->_licenseServiceURL.empty()) {
				_licenseTemplate->SetLicensingServiceUrl(_T("https://licensingservice.anscenter.com/"));
			}
			else {
				this->_licenseTemplate->SetLicensingServiceUrl(this->_licenseServiceURL.c_str());
			}
			this->_licenseTemplate->SetNumberOfGroups(6);					// characters per group
			this->_licenseTemplate->SetCharactersPerGroup(5);				// bits per character
			this->_licenseTemplate->SetEncoding(ENCODING_BASE32X);
			this->_licenseTemplate->SetGroupSeparator(_T("-"));
			this->_licenseTemplate->SetDataSize(36);						//16 bits for ProductId, 4 bits for FeatureBitMask, and 16 bits for ExpirationData (16+4+16 =36)
			this->_licenseTemplate->SetTemplateId(_T("24880"));
			if(this->_privateKey.empty())
				this->_licenseTemplate->SetPrivateKey(GetSdkKeyW().c_str());
			else this->_licenseTemplate->SetPrivateKey(this->_privateKey.c_str());
			
			if (this->_publicKey.empty())
				this->_licenseTemplate->SetPublicKeyCertificate(_T("EKJWDQEL3KQvCPLEjz0fjQiSAF1qChm65z6/PaZISNtL1wib6QA="));
			else this->_licenseTemplate->SetPublicKeyCertificate(this->_publicKey.c_str());

			this->_licenseTemplate->SetSignatureSize(114); // 114 bits = ((characters per group * number of groups) * bits per character) - data size ;  ((6 * 5) * 5) - (36) =150 -36 =114
			this->_licenseTemplate->AddDataField(_T("ProductId"), FIELD_TYPE_INTEGER, 16, 0); 
			this->_licenseTemplate->AddDataField(_T("FeatureBitMask"),FIELD_TYPE_INTEGER,4,16);
			this->_licenseTemplate->AddDataField(_T("ExpirationDate"),FIELD_TYPE_DATE16,16,20);
			
			this->_licenseTemplate->AddValidationField(_T("RegistrationName"), FIELD_TYPE_STRING, MAX_CUSTOMER_NAME_LEN * 8, 0);
			this->_licenseTemplate->SetValidationDataSize(MAX_CUSTOMER_NAME_LEN * 8);
			this->_licenseTemplate->SetProperty(_T("/Trial/TrialLicenseKey"), NULL, _T("3YASV-BZNTJ-7CC3G-R9J9G-WEJTM-EAAAA"));
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			this->_logger.LogFatal("ANSLSHelper::SetUpLicenseTemplate", ex->GetExceptionMessage(), __FILE__, __LINE__);
			ex->Destroy();
		}
	}
	// It is very simple form of license template. Just ProductId and the key as XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
	void ANSLSHelper::Setup5x5LicenseTemplate() {
		try {
			if (this->_licenseTemplate) LicenseTemplate::Destroy(this->_licenseTemplate);
			this->_licenseTemplate = LicenseTemplate::Create();
			_licenseTemplate->SetLicensingServiceUrl(_T("https://licensingservice.anscenter.com/"));
			_licenseTemplate->SetTemplateId(_T("12345"));// Simple test template
			// Setup the license key appearance as XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
			_licenseTemplate->SetCharactersPerGroup(5);
			_licenseTemplate->SetNumberOfGroups(5);
			_licenseTemplate->SetGroupSeparator(_T("-"));
			_licenseTemplate->SetEncoding(ENCODING_BASE32X);
			// From the license key's 5 * 5 * 5 = 125 bits, 109 are the digital signature and 16 bits are data
			_licenseTemplate->SetSignatureSize(109);
			_licenseTemplate->SetDataSize(16);
			_licenseTemplate->SetPrivateKey(_T("AQnD07PnEw7CRi8="));
			_licenseTemplate->SetPublicKeyCertificate(_T("AI0bDQELdoxzyMNu7n46whOSAEon0PzzErF7AiD1r/HhLy4U1wA="));
			// Now split the 16 data bits into a single field containing a product id number
			_licenseTemplate->AddDataField(_T("ProductId"), FIELD_TYPE_INTEGER, 16, 0);
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			this->_logger.LogFatal("ANSLSHelper::Setup5x5LicenseTemplate", ex->GetExceptionMessage(), __FILE__, __LINE__);
			ex->Destroy();
		}
	}
	std::string ANSLSHelper::GenerateKey(int productId, std::string registrationName, int productFeature, bool trialLicense) {
		try {
			std::wstring _registrationName = String2WString(registrationName);
			this->_licenseTemplate->SetPrivateKey(_privateKey.c_str());  // Only use _private key when we intend to generate a new SN	
			KeyGenerator keyGen;
			keyGen.SetKeyTemplate(this->_licenseTemplate);
			// from the same company
			keyGen.SetKeyData(_T("ProductId"), productId);
			// mark some of the features as enabled
			if (!registrationName.empty()) keyGen.SetValidationData(_T("RegistrationName"), _registrationName.c_str());
			if (productFeature > 0) keyGen.SetKeyData(_T("FeatureBitMask"), productFeature);
			try {
				boost::posix_time::ptime timeLocal = boost::posix_time::second_clock::local_time();
				int currentYear = timeLocal.date().year();
				int currentMonth = timeLocal.date().month();
				int currentDate = timeLocal.date().day();
				std::string logMessage = str(boost::format("Current date: %1%-%2%-%3% ") % currentDate % currentMonth % currentYear);
				_logger.LogDebug("ANSLSHelper::GenerateKey", logMessage, __FILE__, __LINE__);			
				if (trialLicense) {// Add 3 months
					currentMonth= currentMonth+3;
					keyGen.SetKeyData(_T("FeatureBitMask"), 0);// We know it is the trial key
					if (currentMonth > 12) {
						currentMonth = currentMonth-12;
						if (currentMonth < 1) currentMonth = 1;
						currentYear++;
					}
					keyGen.SetKeyData(_T("ExpirationDate"), currentYear, currentMonth, currentDate);
				}
				else {// Add 50 years
					try {
						int expriredYear = currentYear + 50;
						keyGen.SetKeyData(_T("ExpirationDate"), expriredYear, currentMonth, currentDate);

					}
					catch (ANSCENTER::Licensing::Exception* ex){
						keyGen.SetKeyData(_T("ExpirationDate"), 2041, currentMonth, currentDate);
						ex->Destroy();
					}
				}
			}
			catch (ANSCENTER::Licensing::Exception* ex) {
				this->_logger.LogWarn("ANSLSHelper::GenerateKey", "This license template does not support expiration data", __FILE__, __LINE__);
				this->_logger.LogWarn("ANSLSHelper::GenerateKey", ex->GetExceptionMessage(), __FILE__, __LINE__);
				ex->Destroy();
			}
			const wchar_t* licenseKey = keyGen.GenerateKey();
			std::wstring wresult(licenseKey);
			std::string result = WString2String(wresult);
			return result;
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			this->_logger.LogFatal("ANSLSHelper::GenerateKey", ex->GetExceptionMessage(), __FILE__, __LINE__);
			ex->Destroy();
			return "";
		}
	}
	std::string ANSLSHelper::GenerateKey(int productId, std::string registrationName, int productFeature, int expiredYear, int expiredMonth, int expiredDate) {
		try {
			std::wstring _registrationName = String2WString(registrationName);
			this->_licenseTemplate->SetPrivateKey(_privateKey.c_str());  // Only use _private key when we intend to generate a new SN

			KeyGenerator keyGen;
			keyGen.SetKeyTemplate(this->_licenseTemplate);
			// from the same company
			keyGen.SetKeyData(_T("ProductId"), productId);
			// mark some of the features as enabled
			if (!registrationName.empty()) keyGen.SetValidationData(_T("RegistrationName"), _registrationName.c_str());
			if (productFeature > 0) keyGen.SetKeyData(_T("FeatureBitMask"), productFeature);
			try {

				try {
					keyGen.SetKeyData(_T("ExpirationDate"), expiredYear, expiredMonth, expiredDate);
				}
				catch (ANSCENTER::Licensing::Exception* ex) {
					keyGen.SetKeyData(_T("ExpirationDate"), 2041, 1, 1);
					ex->Destroy();
				}
			}
			catch (ANSCENTER::Licensing::Exception* ex) {
				//this->_logger.LogWarn("ANSLSHelper::GenerateKey", "This license template does not support expiration data", __FILE__, __LINE__);
				this->_logger.LogWarn("ANSLSHelper::GenerateKey", ex->GetExceptionMessage(), __FILE__, __LINE__);
				ex->Destroy();
			}
			const wchar_t* licenseKey = keyGen.GenerateKey();
			std::wstring wresult(licenseKey);
			std::string result = WString2String(wresult);
			return result;
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			this->_logger.LogFatal("ANSLSHelper::GenerateKey", ex->GetExceptionMessage(), __FILE__, __LINE__);
			ex->Destroy();
			return "";
		}
	}	
	bool ANSLSHelper::CheckLicenseKey(std::string licenseKey, std::string registrationName, int productId, int& enableFeatures) {
		try {
			bool isValid = false;
			LoadLicense(licenseKey);
			std::wstring _registrationName = String2WString(registrationName);
			std::wstring _licenseKey = String2WString(licenseKey);
			KeyValidator keyValidator(this->_licenseTemplate);
			keyValidator.SetKey(_licenseKey.c_str());
			if (!registrationName.empty())
				keyValidator.SetValidationData(_T("RegistrationName"), _registrationName.c_str());
			if (!keyValidator.IsKeyValid())
			{
				this->_logger.LogError("ANSLSHelper::CheckLicenseKey", "Invalid license key", __FILE__, __LINE__);
				return isValid; 
			}

			// Validate expiration date first
			try {
				boost::posix_time::ptime timeLocal = boost::posix_time::second_clock::local_time();
				unsigned short currentYear = timeLocal.date().year();
				unsigned short currentMonth = timeLocal.date().month();
				unsigned short currentDay = timeLocal.date().day();
				int expiredYear, expiredMonth, expiredDay;
				bool validExpiration = false;
				keyValidator.QueryDateKeyData(_T("ExpirationDate"), &expiredYear, &expiredMonth, &expiredDay);
				boost::gregorian::date currentDate{ currentYear, currentMonth, currentDay };
				boost::gregorian::date expiredDate{ (unsigned short)expiredYear, (unsigned short)expiredMonth, (unsigned short)expiredDay };
				boost::gregorian::date_duration validDuration = expiredDate - currentDate;
				if (validDuration.days() >= 0) {
					// Check if clock is manipulated
					bool changeClock = DectectClockmanipulation(expiredYear, expiredMonth, expiredDay);
					if (changeClock)
					{
						validExpiration = false;
						this->_logger.LogError("ANSLSHelper::CheckLicenseKey", "License expired. Please renew license.", __FILE__, __LINE__);
					}
					else {
						validExpiration = true;
					}
				}

				if (!validExpiration) {
					this->_logger.LogError("ANSLSHelper::CheckLicenseKey", "License is expired", __FILE__, __LINE__);
					isValid = false;
					return isValid;
				}
			}
			catch (ANSCENTER::Licensing::Exception* ex) {
				this->_logger.LogError("ANSLSHelper::CheckLicenseKey", "Expiration data is not supported.", __FILE__, __LINE__);
				ex->Destroy();
			}
			
			// Then validate the product Id
			int validatedProductId = 0;
			int len = sizeof(validatedProductId);
			keyValidator.QueryKeyData(_T("ProductId"), &validatedProductId, &len);
			bool validProductId = false;
		
			if (validatedProductId == productId)validProductId = true;
			if (!validProductId) {
				this->_logger.LogError("ANSLSHelper::CheckLicenseKey", "Invalid Product", __FILE__, __LINE__);
				isValid = false;
				return isValid; // invalid LicenseKeyStatus
			}
			// finally validate features
			try {
				int validatedEnabledFeatures = keyValidator.QueryIntKeyData(_T("FeatureBitMask"));
				enableFeatures = validatedEnabledFeatures;
			}
			catch (ANSCENTER::Licensing::Exception* ex) {
				this->_logger.LogWarn("ANSLSHelper::CheckLicenseKey", "Feature field are not supported.", __FILE__, __LINE__);
				enableFeatures = 0;
				ex->Destroy();
			}
			isValid = true;
			return isValid; // 
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			this->_logger.LogFatal("ANSLSHelper::CheckLicenseKey", ex->GetExceptionMessage(), __FILE__, __LINE__);
			ex->Destroy();
			return false;
		}
	}
	bool ANSLSHelper::IsLicenseKeyValid(std::string licenseKey, std::string registrationName){
		try {
			bool isValid = false;
			LoadLicense(licenseKey);
			std::wstring _registrationName;
			if (!registrationName.empty()) {
				_registrationName = String2WString(registrationName);
			}
			else {
				_registrationName = _T("");
			}
			std::wstring _licenseKey = String2WString(licenseKey);
			KeyValidator keyValidator(this->_licenseTemplate);
			keyValidator.SetKey(_licenseKey.c_str());
			if (!_registrationName.empty()) 
				keyValidator.SetValidationData(_T("RegistrationName"), _registrationName.c_str());
			if (!keyValidator.IsKeyValid())
			{
				this->_logger.LogError("ANSLSHelper::IsLicenseKeyValid", "Invalid license key", __FILE__, __LINE__);
				return isValid;
			}
			// Validate expiration date first
			try {
				boost::posix_time::ptime timeLocal = boost::posix_time::second_clock::local_time();
				unsigned short currentYear = timeLocal.date().year();
				unsigned short currentMonth = timeLocal.date().month();
				unsigned short currentDay = timeLocal.date().day();
				int expiredYear, expiredMonth, expiredDay;
				bool validExpiration = false;
				keyValidator.QueryDateKeyData(_T("ExpirationDate"), &expiredYear, &expiredMonth, &expiredDay);
				boost::gregorian::date currentDate{ currentYear, currentMonth, currentDay };
				boost::gregorian::date expiredDate{ (unsigned short)expiredYear, (unsigned short)expiredMonth, (unsigned short)expiredDay };
				boost::gregorian::date_duration validDuration = expiredDate - currentDate;
				if (validDuration.days() >= 0) {
					// Check if clock is manipulated
					bool changeClock = DectectClockmanipulation(expiredYear, expiredMonth, expiredDay);
					if (changeClock) 
					{
						validExpiration = false;
						this->_logger.LogError("ANSLSHelper::IsLicenseKeyValid", "License expired. Please renew license.", __FILE__, __LINE__);
					}
					else {
						validExpiration = true;
						this->_logger.LogDebug("ANSLSHelper::IsLicenseKeyValid", "Validation Successfully.", __FILE__, __LINE__);
					}
				} 
				if (!validExpiration) {
					this->_logger.LogError("ANSLSHelper::IsLicenseKeyValid", "License is expired", __FILE__, __LINE__);
					isValid = false;
					return isValid;
				}
			}
			catch (ANSCENTER::Licensing::Exception* ex) {
				this->_logger.LogWarn("ANSLSHelper::IsLicenseKeyValid", "Expiration data is not supported.", __FILE__, __LINE__);
				ex->Destroy();
			}
			isValid = true;
			this->_logger.LogDebug("ANSLSHelper::IsLicenseKeyValid", "License key is valid.", __FILE__, __LINE__);
			return isValid; // 
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			this->_logger.LogFatal("ANSLSHelper::IsLicenseKeyValid", ex->GetExceptionMessage(), __FILE__, __LINE__);
			ex->Destroy();
			return false;
		}
	}
	std::wstring ANSLSHelper::GetProductId(std::string licenseKey) {
		try {
			std::wstring _licenseKey = String2WString(licenseKey);
			KeyValidator keyValidator(this->_licenseTemplate);
			keyValidator.SetKey(_licenseKey.c_str());
			int validatedProductId = 0;
			int len = sizeof(validatedProductId);
			try {
				keyValidator.QueryKeyData(_T("ProductId"), &validatedProductId, &len);
			}
			catch (ANSCENTER::Licensing::Exception* ex) {
				this->_logger.LogWarn("ANSLSHelper::GetLicenseData", ex->GetExceptionMessage(), __FILE__, __LINE__);
				ex->Destroy();
			}
			std::string sProductId = std::to_string(validatedProductId);
			std::wstring wProductId = String2WString(sProductId);
			return wProductId;
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			this->_logger.LogFatal("ANSLSHelper::GetProductId", "License key is invalid", __FILE__, __LINE__);
			ex->Destroy();
			std::string sProductId = "0";
			std::wstring wProductId = String2WString(sProductId);
			return wProductId;
		}
	}
	std::string ANSLSHelper::GetLicenseData(std::string licenseKey) {
		std::wstring _licenseKey = String2WString(licenseKey);
		std::wstring wProductId = GetProductId(licenseKey);
		this->_licenseTemplate->SetTemplateId(wProductId.c_str());
		boost::property_tree::ptree root;
		boost::property_tree::ptree dataNode;
		KeyValidator keyValidator(this->_licenseTemplate);
		try {
			keyValidator.SetKey(_licenseKey.c_str());
			try {
				int validatedProductId = 0;
				int validatedProductIdLen = sizeof(validatedProductId);
				keyValidator.QueryKeyData(_T("ProductId"), &validatedProductId, &validatedProductIdLen);
				dataNode.put("valid_product_id", validatedProductId);
			}
			catch (ANSCENTER::Licensing::Exception* ex) {
				this->_logger.LogWarn("ANSLSHelper::GetLicenseData", "ProductId data is not available.", __FILE__, __LINE__);
				dataNode.put("valid_product_id", 0);
				ex->Destroy();
			}
			try {
				int expiredYear, expiredMonth, expiredDay;
				keyValidator.QueryDateKeyData(_T("ExpirationDate"), &expiredYear, &expiredMonth, &expiredDay);
				if ((expiredYear > 0) && (expiredMonth > 0) && (expiredDay > 0)) {
					boost::posix_time::ptime timeLocal = boost::posix_time::second_clock::local_time();
					unsigned short currentYear = timeLocal.date().year();
					unsigned short currentMonth = timeLocal.date().month();
					unsigned short currentDay = timeLocal.date().day();
					boost::gregorian::date currentDate{ currentYear, currentMonth, currentDay };
					boost::gregorian::date expiredDate{ (unsigned short)expiredYear, (unsigned short)expiredMonth, (unsigned short)expiredDay };
					boost::gregorian::date_duration validDuration = expiredDate - currentDate;
					dataNode.put("remaining_days", validDuration.days());
				}
				else {
					dataNode.put("remaining_days", 0);
				}
			}
			catch (ANSCENTER::Licensing::Exception* ex) {
				this->_logger.LogWarn("ANSLSHelper::GetLicenseData", "Expired date data is not available.", __FILE__, __LINE__);
				dataNode.put("remaining_days", 0);
				ex->Destroy();
			}
			try {
				int validatedEnabledFeatures = keyValidator.QueryIntKeyData(_T("FeatureBitMask"));
				dataNode.put("enabled_features", validatedEnabledFeatures);
			}
			catch (ANSCENTER::Licensing::Exception* ex) {
				this->_logger.LogWarn("ANSLSHelper::GetLicenseData", "Enabled features data is not available.", __FILE__, __LINE__);
				dataNode.put("enabled_features", 0);
				ex->Destroy();
			}
			try {
				int validationDataLen = MAX_CUSTOMER_NAME_LEN;
				unsigned char validationData[MAX_CUSTOMER_NAME_LEN] = "";
				const char* emptyString = "";
				keyValidator.QueryValidationData(NULL, validationData, &validationDataLen);
				int comparisonResult = std::strcmp(reinterpret_cast<const char*>(validationData), emptyString);
				if (comparisonResult != 0) dataNode.put("validation_data", validationData);
				else dataNode.put("validation_data", "");
			}
			catch (ANSCENTER::Licensing::Exception* ex) {
				this->_logger.LogWarn("ANSLSHelper::GetLicenseData", "validation data is not available.", __FILE__, __LINE__);
				dataNode.put("validation_data", "");
				ex->Destroy();
			}
			root.add_child("licenseData", dataNode);
			std::ostringstream stream;
			boost::property_tree::write_json(stream, root,false);
			std::string st = stream.str();
			return st;
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			this->_logger.LogWarn("ANSLSHelper::GetLicenseData", "validation data is not available.", __FILE__, __LINE__);
			dataNode.put("validation_data", "");
			ex->Destroy();
			root.add_child("licenseData", dataNode);
			std::ostringstream stream;
			boost::property_tree::write_json(stream, root,false);
			std::string st = stream.str();
			return st;
		}
		
	}
	int ANSLSHelper::GenerateOfflineActivationData(std::string licenseKey, std::string  registrationName, std::string& offlineActivationData) {
		try {
			std::wstring wHardwareId = KeyHelper::GetCurrentHardwareId();
			std::string hardwareId = WString2String(wHardwareId);
			std::wstring _licenseKey = String2WString(licenseKey);
			std::wstring _registrationName = String2WString(registrationName);
			std::wstring wProductId = GetProductId(licenseKey);
			std::string _productId = WString2String(wProductId);
			this->_licenseTemplate->SetTemplateId(wProductId.c_str());			
			KeyValidator keyValidator(this->_licenseTemplate);
	

			keyValidator.SetKey(_licenseKey.c_str());

			if (!registrationName.empty())
				keyValidator.SetValidationData(_T("RegistrationName"), _registrationName.c_str());

			if (!keyValidator.IsKeyValid())
			{
				this->_logger.LogError("ANSLSHelper::InstallOfflineLicense", "Invalid license key", __FILE__, __LINE__);
				return 0; // invalid
			}

			// We also need to verify if the activation key valid or not.
			int validationDataLen = MAX_CUSTOMER_NAME_LEN;
			unsigned char validationData[MAX_CUSTOMER_NAME_LEN] = "";
			keyValidator.QueryValidationData(NULL, validationData, &validationDataLen);
			std::string licenseKeyValidationData = ANSUtility::EncodeBase64Utility(validationData, validationDataLen,true);

			boost::property_tree::ptree root;
			boost::property_tree::ptree dataNode;
			dataNode.put("license_key", licenseKey);
			dataNode.put("hardware_id", hardwareId);
			dataNode.put("license_key_validation_data", licenseKeyValidationData);
			dataNode.put("product_id", _productId);
			root.add_child("offlineActivationData", dataNode);
			std::ostringstream stream;
			boost::property_tree::write_json(stream, root,false);
			std::string st = stream.str();
			offlineActivationData = st;
			return 1;
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			this->_logger.LogFatal("ANSLSHelper::GenerateOfflineActivationData", ex->GetExceptionMessage(), __FILE__, __LINE__);
			return 0;
		}
	}
	int ANSLSHelper::ValidateLicense(std::string licenseKey, std::string registrationName, std::string& activationKey) {
		LicenseKeyStatus _licenseKeyStatus;
		LoadLicense(licenseKey);
		std::wstring _licenseKey = String2WString(licenseKey);
		std::wstring _activationKey = String2WString(activationKey);
		std::wstring _registrationName = String2WString(registrationName);
		std::wstring hardwareId = KeyHelper::GetCurrentHardwareId();
		std::string _hardwareId = WString2String(hardwareId);
		bool    _overrideLicenseFile = false;
		if (activationKey.empty()) {
			if (this->_license != NULL) {// get it from there
				const wchar_t* cAKey = _license->GetActivationKey();
				std::wstring wAKey(cAKey);
				_activationKey = wAKey;
			}
		}
		std::wstring wProductId = GetProductId(licenseKey);
		this->_licenseTemplate->SetTemplateId(wProductId.c_str());
		KeyValidator keyValidator(this->_licenseTemplate);
		try {
			keyValidator.SetKey(_licenseKey.c_str());
			if (!registrationName.empty())
				keyValidator.SetValidationData(_T("RegistrationName"), _registrationName.c_str());

			if (!keyValidator.IsKeyValid())
			{
				_licenseKeyStatus = INVALID_LICENSE_KEY;
				this->_logger.LogError("ANSLSHelper::ValidateLicenseKey", "Invalid license key", __FILE__, __LINE__);
				activationKey = "";
				return _licenseKeyStatus; // invalid
			}

			int validationDataLen = MAX_CUSTOMER_NAME_LEN;
			unsigned char validationData[MAX_CUSTOMER_NAME_LEN] = "";
			keyValidator.QueryValidationData(NULL, validationData, &validationDataLen);
			const char* emptyString = "";

			LicensingClient licensingClient;
			// SetLicenseKeyValidationData needs to be called before SetActivationKeyTemplate !
			int comparisonResult = std::strcmp(reinterpret_cast<const char*>(validationData), emptyString);
			if (comparisonResult != 0)
				licensingClient.SetLicenseKeyValidationData(validationData, validationDataLen);
			licensingClient.SetLicenseTemplate(this->_licenseTemplate);
			licensingClient.SetLicenseKey(_licenseKey.c_str());
			licensingClient.SetHardwareId(hardwareId.c_str());
			licensingClient.SetActivationKey(_activationKey.c_str());
			this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey. Hardware Id", mask_secret(_hardwareId), __FILE__, __LINE__);

			bool validLicense = licensingClient.IsLicenseValid();
			if (!validLicense)
			{
				try
				{
					this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey", "Product needs to be activated. Requesting license activation from server...", __FILE__, __LINE__);
					licensingClient.SetLicensingServiceUrl(this->_licenseServiceURL.c_str());
					licensingClient.SetLicenseTemplate(this->_licenseTemplate);
					licensingClient.AcquireLicense();
					this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey", "Done.", __FILE__, __LINE__);
					_overrideLicenseFile = true;
				}
				catch (Exception* ex)
				{
					this->_logger.LogFatal("ANSLSHelper::ValidateLicenseKey", ex->GetExceptionMessage(), __FILE__, __LINE__);
					ex->Destroy();
				}
				catch (...)
				{
					this->_logger.LogFatal("ANSLSHelper::ValidateLicenseKey", "Unknown exception during activation attempt !", __FILE__, __LINE__);
				}

				if (!licensingClient.IsLicenseValid())
				{
					int activationStatus = licensingClient.GetLicenseActivationStatus();
					std::string activationStatusStr;

					switch (activationStatus)
					{
					case STATUS_INVALID_ACTIVATION_KEY:
						_licenseKeyStatus = INVALID_ACTIVATION_KEY;
						activationStatusStr = "Activation key is invalid";
						break;

					case STATUS_INVALID_HARDWARE_ID:
						_licenseKeyStatus = INVALID_HARDWARE_ID;
						activationStatusStr = "Hardware id does not match this computer. current HWID is " + _hardwareId + 
											  "; client HWID is " + WString2String(licensingClient.GetHardwareId()) + 
											  "; Current activation key is " + WString2String(_activationKey) +
											  "; client Activation key is " + WString2String(licensingClient.GetActivationKey()) +
											  "; URL is " + WString2String(this->_licenseServiceURL.c_str())+ 
											  "; ProductId is " + WString2String(wProductId) +
											  "; RegistrationName is " + WString2String(_registrationName) +
											  "; Status is" + std::to_string(activationStatus);
						break;

					case STATUS_LICENSE_EXPIRED:
						_licenseKeyStatus = LICENSE_EXPIRED;
						activationStatusStr = "License expired";
						break;
					default:
						_licenseKeyStatus = UNKNOWN;
						activationStatusStr = "Failed to activate license. Unknown issue.";
						break;
					}
					this->_logger.LogError("ANSLSHelper::ValidateLicenseKey", activationStatusStr, __FILE__, __LINE__);
					activationKey = "";
					return _licenseKeyStatus;
				}
				else  // License is now valid, we need to check if 
				{
					int expiredYear, expiredMonth, expiredDay;
					try {
						keyValidator.QueryDateKeyData(_T("ExpirationDate"), &expiredYear, &expiredMonth, &expiredDay);
					}
					catch (ANSCENTER::Licensing::Exception* ex) {
						this->_logger.LogWarn("ANSLSHelper::ValidateLicenseKey", ex->GetExceptionMessage(), __FILE__, __LINE__);
						ex->Destroy();
						licensingClient.GetLicenseExpirationDate(&expiredYear, &expiredMonth, &expiredDay);
					}
					if (expiredYear > 0) {
						boost::posix_time::ptime timeLocal = boost::posix_time::second_clock::local_time();
						unsigned short currentYear = timeLocal.date().year();
						unsigned short currentMonth = timeLocal.date().month();
						unsigned short currentDay = timeLocal.date().day();
						boost::gregorian::date currentDate{ currentYear, currentMonth, currentDay };
						boost::gregorian::date expiredDate{ (unsigned short)expiredYear, (unsigned short)expiredMonth, (unsigned short)expiredDay };
						boost::gregorian::date_duration validDuration = expiredDate - currentDate;
						bool changeClock = DectectClockmanipulation(expiredYear, expiredMonth, expiredDay);
						if (changeClock) {
							_licenseKeyStatus = LICENSE_EXPIRED;
						}
						else {
							_licenseKeyStatus = SUCCESS;
						}
						std::string loggerExpiredMessage = str(boost::format("License Expiration Days: %1%") % validDuration.days());
						this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey", loggerExpiredMessage, __FILE__, __LINE__);
					}
					else {
						_licenseKeyStatus = SUCCESS;
						this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey", "License Expiration Date: Never", __FILE__, __LINE__);
					}

					// Do we need to check license (ProductId, RegistrationName, and features?) No need

					// Save license
					if (_licenseKeyStatus == SUCCESS) {
						const wchar_t* wcActivationKey = licensingClient.GetActivationKey();
						std::wstring wActivationKey(wcActivationKey);
						std::string sActivationKey = WString2String(wActivationKey);

						const wchar_t* hardwareId = licensingClient.GetHardwareId();
						std::wstring wHardwareId(hardwareId);
						std::string sHardwareId = WString2String(wHardwareId);

						std::string loggerActivationMessage = str(boost::format("Activation Successful. The returned activation key: %1%") % mask_secret(sActivationKey));
						std::string loggerHWIDMessage = str(boost::format("Computer's hardware id: %1%") % mask_secret(sHardwareId));
						this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey", "Product is activated", __FILE__, __LINE__);
						this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey", loggerActivationMessage, __FILE__, __LINE__);
						this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey", loggerHWIDMessage, __FILE__, __LINE__);

						const wchar_t* actKey = licensingClient.GetActivationKey();
						std::wstring wact(actKey);
						activationKey = WString2String(wact);

						if (_overrideLicenseFile) {
							this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey", "Override license key.", __FILE__, __LINE__);
							if (!this->_license != NULL) delete this->_license;
							this->_license = new License();
							this->_license->SetLicenseKey(_licenseKey.c_str());
							this->_license->SetLicenseKeyValidationData(validationData, validationDataLen);
							this->_license->SetHardwareId(licensingClient.GetCurrentHardwareId());
							this->_license->SetActivationKey(actKey);
							SaveLicense();
						}
					}
					else {
						this->_logger.LogError("ANSLSHelper::ValidateLicenseKey", "License expired. Please renew license.", __FILE__, __LINE__);
					}

					return _licenseKeyStatus;
				}
			}
			else //License is already valid. No need to do anything
			{
				this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey", "Product is activated", __FILE__, __LINE__);
				_licenseKeyStatus = SUCCESS;
				const wchar_t* actKey = licensingClient.GetActivationKey();
				std::wstring wact(actKey);
				activationKey = WString2String(wact);

				if (_overrideLicenseFile) {
					// Save license
					this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey", "Override license key.", __FILE__, __LINE__);
					if (!this->_license != NULL) delete this->_license;
					this->_license = new License();
					this->_license->SetLicenseKey(_licenseKey.c_str());
					this->_license->SetLicenseKeyValidationData(validationData, validationDataLen);
					this->_license->SetHardwareId(licensingClient.GetCurrentHardwareId());
					this->_license->SetActivationKey(actKey);
					SaveLicense();
				}
				return _licenseKeyStatus;
			}
		}
		catch (Exception* ex)
		{
			this->_logger.LogFatal("ANSLSHelper::ValidateLicenseKey", ex->GetExceptionMessage(), __FILE__, __LINE__);
			ex->Destroy();
			_licenseKeyStatus = INVALID_LICENSE_KEY;
			return _licenseKeyStatus;
		}


	}
	
	int	ANSLSHelper::ValidateLicenseWithCustomHWID(std::string licenseKey, std::string registrationName, std::string customHWID, std::string& activationKey) {
		LicenseKeyStatus _licenseKeyStatus;
		LoadLicense(licenseKey);
		std::wstring _licenseKey = String2WString(licenseKey);
		std::wstring _activationKey = String2WString(activationKey);
		std::wstring _registrationName = String2WString(registrationName);
		std::string _hardwareId = customHWID;
		std::wstring hardwareId = String2WString(customHWID);
		bool    _overrideLicenseFile = false;
		if (activationKey.empty()) {
			if (this->_license != NULL) {// get it from there
				const wchar_t* cAKey = _license->GetActivationKey();
				std::wstring wAKey(cAKey);
				_activationKey = wAKey;
			}
		}
		std::wstring wProductId = GetProductId(licenseKey);
		this->_licenseTemplate->SetTemplateId(wProductId.c_str());
		KeyValidator keyValidator(this->_licenseTemplate);
		try {
			keyValidator.SetKey(_licenseKey.c_str());
			if (!registrationName.empty())
				keyValidator.SetValidationData(_T("RegistrationName"), _registrationName.c_str());

			if (!keyValidator.IsKeyValid())
			{
				_licenseKeyStatus = INVALID_LICENSE_KEY;
				this->_logger.LogError("ANSLSHelper::ValidateLicenseKey", "Invalid license key", __FILE__, __LINE__);
				activationKey = "";
				return _licenseKeyStatus; // invalid
			}

			int validationDataLen = MAX_CUSTOMER_NAME_LEN;
			unsigned char validationData[MAX_CUSTOMER_NAME_LEN] = "";
			keyValidator.QueryValidationData(NULL, validationData, &validationDataLen);
			const char* emptyString = "";

			LicensingClient licensingClient;
			// SetLicenseKeyValidationData needs to be called before SetActivationKeyTemplate !
			int comparisonResult = std::strcmp(reinterpret_cast<const char*>(validationData), emptyString);
			if (comparisonResult != 0)
				licensingClient.SetLicenseKeyValidationData(validationData, validationDataLen);
			licensingClient.SetLicenseTemplate(this->_licenseTemplate);
			licensingClient.SetLicenseKey(_licenseKey.c_str());
			licensingClient.SetHardwareId(hardwareId.c_str());
			licensingClient.SetActivationKey(_activationKey.c_str());
			this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey. Hardware Id", mask_secret(_hardwareId), __FILE__, __LINE__);

			bool validLicense = licensingClient.IsLicenseValid();
			if (!validLicense)
			{
				try
				{
					this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey", "Product needs to be activated. Requesting license activation from server...", __FILE__, __LINE__);
					licensingClient.SetLicensingServiceUrl(this->_licenseServiceURL.c_str());
					licensingClient.SetLicenseTemplate(this->_licenseTemplate);
					licensingClient.AcquireLicense();
					this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey", "Done.", __FILE__, __LINE__);
					_overrideLicenseFile = true;
				}
				catch (Exception* ex)
				{
					this->_logger.LogFatal("ANSLSHelper::ValidateLicenseKey", ex->GetExceptionMessage(), __FILE__, __LINE__);
					ex->Destroy();
				}
				catch (...)
				{
					this->_logger.LogFatal("ANSLSHelper::ValidateLicenseKey", "Unknown exception during activation attempt !", __FILE__, __LINE__);
				}

				if (!licensingClient.IsLicenseValid())
				{
					int activationStatus = licensingClient.GetLicenseActivationStatus();
					std::string activationStatusStr;

					switch (activationStatus)
					{
					case STATUS_INVALID_ACTIVATION_KEY:
						_licenseKeyStatus = INVALID_ACTIVATION_KEY;
						activationStatusStr = "Activation key is invalid";
						break;

					case STATUS_INVALID_HARDWARE_ID:
						_licenseKeyStatus = INVALID_HARDWARE_ID;
						activationStatusStr = "Hardware id does not match this computer. current HWID is " + _hardwareId +
							"; client HWID is " + WString2String(licensingClient.GetHardwareId()) +
							"; Current activation key is " + WString2String(_activationKey) +
							"; client Activation key is " + WString2String(licensingClient.GetActivationKey()) +
							"; URL is " + WString2String(this->_licenseServiceURL.c_str()) +
							"; ProductId is " + WString2String(wProductId) +
							"; RegistrationName is " + WString2String(_registrationName) +
							"; Status is" + std::to_string(activationStatus);
						break;

					case STATUS_LICENSE_EXPIRED:
						_licenseKeyStatus = LICENSE_EXPIRED;
						activationStatusStr = "License expired";
						break;
					default:
						_licenseKeyStatus = UNKNOWN;
						activationStatusStr = "Failed to activate license. Unknown issue.";
						break;
					}
					this->_logger.LogError("ANSLSHelper::ValidateLicenseKey", activationStatusStr, __FILE__, __LINE__);
					activationKey = "";
					return _licenseKeyStatus;
				}
				else  // License is now valid, we need to check if 
				{
					int expiredYear, expiredMonth, expiredDay;
					try {
						keyValidator.QueryDateKeyData(_T("ExpirationDate"), &expiredYear, &expiredMonth, &expiredDay);
					}
					catch (ANSCENTER::Licensing::Exception* ex) {
						this->_logger.LogWarn("ANSLSHelper::ValidateLicenseKey", ex->GetExceptionMessage(), __FILE__, __LINE__);
						ex->Destroy();
						licensingClient.GetLicenseExpirationDate(&expiredYear, &expiredMonth, &expiredDay);
					}
					if (expiredYear > 0) {
						boost::posix_time::ptime timeLocal = boost::posix_time::second_clock::local_time();
						unsigned short currentYear = timeLocal.date().year();
						unsigned short currentMonth = timeLocal.date().month();
						unsigned short currentDay = timeLocal.date().day();
						boost::gregorian::date currentDate{ currentYear, currentMonth, currentDay };
						boost::gregorian::date expiredDate{ (unsigned short)expiredYear, (unsigned short)expiredMonth, (unsigned short)expiredDay };
						boost::gregorian::date_duration validDuration = expiredDate - currentDate;
						bool changeClock = DectectClockmanipulation(expiredYear, expiredMonth, expiredDay);
						if (changeClock) {
							_licenseKeyStatus = LICENSE_EXPIRED;
						}
						else {
							_licenseKeyStatus = SUCCESS;
						}
						std::string loggerExpiredMessage = str(boost::format("License Expiration Days: %1%") % validDuration.days());
						this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey", loggerExpiredMessage, __FILE__, __LINE__);
					}
					else {
						_licenseKeyStatus = SUCCESS;
						this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey", "License Expiration Date: Never", __FILE__, __LINE__);
					}

					// Do we need to check license (ProductId, RegistrationName, and features?) No need

					// Save license
					if (_licenseKeyStatus == SUCCESS) {
						const wchar_t* wcActivationKey = licensingClient.GetActivationKey();
						std::wstring wActivationKey(wcActivationKey);
						std::string sActivationKey = WString2String(wActivationKey);

						const wchar_t* hardwareId = licensingClient.GetHardwareId();
						std::wstring wHardwareId(hardwareId);
						std::string sHardwareId = WString2String(wHardwareId);

						std::string loggerActivationMessage = str(boost::format("Activation Successful. The returned activation key: %1%") % mask_secret(sActivationKey));
						std::string loggerHWIDMessage = str(boost::format("Computer's hardware id: %1%") % mask_secret(sHardwareId));
						this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey", "Product is activated", __FILE__, __LINE__);
						this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey.", loggerActivationMessage, __FILE__, __LINE__);
						this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey.", loggerHWIDMessage, __FILE__, __LINE__);

						const wchar_t* actKey = licensingClient.GetActivationKey();
						std::wstring wact(actKey);
						activationKey = WString2String(wact);

						if (_overrideLicenseFile) {
							this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey", "Override license key.", __FILE__, __LINE__);
							if (!this->_license != NULL) delete this->_license;
							this->_license = new License();
							this->_license->SetLicenseKey(_licenseKey.c_str());
							this->_license->SetLicenseKeyValidationData(validationData, validationDataLen);
							this->_license->SetHardwareId(licensingClient.GetCurrentHardwareId());
							this->_license->SetActivationKey(actKey);
							SaveLicense();
						}
					}
					else {
						this->_logger.LogError("ANSLSHelper::ActivateLicense", "License expired. Please renew license.", __FILE__, __LINE__);
					}

					return _licenseKeyStatus;
				}
			}
			else //License is already valid. No need to do anything
			{
				this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey", "Product is activated", __FILE__, __LINE__);
				_licenseKeyStatus = SUCCESS;
				const wchar_t* actKey = licensingClient.GetActivationKey();
				std::wstring wact(actKey);
				activationKey = WString2String(wact);

				if (_overrideLicenseFile) {
					// Save license
					this->_logger.LogDebug("ANSLSHelper::ValidateLicenseKey", "Override license key.", __FILE__, __LINE__);
					if (!this->_license != NULL) delete this->_license;
					this->_license = new License();
					this->_license->SetLicenseKey(_licenseKey.c_str());
					this->_license->SetLicenseKeyValidationData(validationData, validationDataLen);
					this->_license->SetHardwareId(licensingClient.GetCurrentHardwareId());
					this->_license->SetActivationKey(actKey);
					SaveLicense();
				}
				return _licenseKeyStatus;
			}
		}
		catch (Exception* ex)
		{
			this->_logger.LogFatal("ANSLSHelper::ValidateLicenseKey", ex->GetExceptionMessage(), __FILE__, __LINE__);
			ex->Destroy();
			_licenseKeyStatus = INVALID_LICENSE_KEY;
			return _licenseKeyStatus;
		}
	}

	int ANSLSHelper::ActivateLicense(std::string licenseKey, std::string registrationName, std::string& activationKey ) {
		try
		{
			LicenseKeyStatus _licenseKeyStatus;
			LicenseValidationArgs validationArgs;
			LicensingClient licensingClient;
			LoadLicense(licenseKey);
			std::wstring _licenseKey = String2WString(licenseKey);
			std::wstring wProductId = GetProductId(licenseKey);
			this->_licenseTemplate->SetTemplateId(wProductId.c_str());
			licensingClient.SetLicenseTemplate(this->_licenseTemplate);
			// Always activate
			this->_logger.LogDebug("ANSLSHelper::ActivateLicense", "License was not found, the application needs to be activated", __FILE__, __LINE__);
			validationArgs.SetLicenseKey(_licenseKey.c_str());
			if (!registrationName.empty())
				validationArgs.SetLicenseKeyValidationData(_T("RegistrationName"), String2WString(registrationName).c_str());

			this->_logger.LogDebug("ANSLSHelper::ActivateLicense", "Activating the license....", __FILE__, __LINE__);
			licensingClient.SetLicensingServiceUrl(this->_licenseServiceURL.c_str());

			try {
				LicenseValidationResult* validationResult = licensingClient.ValidateLicense(&validationArgs);
				this->_logger.LogDebug("ANSLSHelper::ActivateLicense", "License was successfully activated", __FILE__, __LINE__);
				//Now check if the license validation process resulted in server sending out a new license to replace the old one
				License* newLicense = validationResult->GetLicense();
				if (newLicense != NULL)
				{
					if (this->_license != NULL) delete this->_license;
					this->_license = newLicense;
					this->_license->SetActivationKey(newLicense->GetActivationKey());
					this->_license->SetHardwareId(newLicense->GetHardwareId());
					this->_license->SetLicenseKey(newLicense->GetLicenseKey());
					const wchar_t* actKey = newLicense->GetActivationKey();
					std::wstring wactKey(actKey);
					activationKey = WString2String(wactKey);
					//delete newLicense;
					this->_logger.LogDebug("ANSLSHelper::ActivateLicense", "New license has been received from the licensing server", __FILE__, __LINE__);
					SaveLicense();
				}
				else {// Existing _license
					const wchar_t* actKey = this->_license->GetActivationKey();
					std::wstring wactKey(actKey);
					activationKey = WString2String(wactKey);
				}
				if (validationResult->IsLicenseExpired())
				{
					this->_logger.LogError("ANSLSHelper::ActivateLicense", "License is expired.", __FILE__, __LINE__);
					_licenseKeyStatus = LICENSE_EXPIRED;
					return _licenseKeyStatus;

				}
				if (validationResult->IsPaymentRequired())
				{
					this->_logger.LogError("ANSLSHelper::ActivateLicense", "Payment is required.", __FILE__, __LINE__);
					_licenseKeyStatus = PAYMENT_REQUIRED;
					return _licenseKeyStatus;
				}
			}
			catch (ANSCENTER::Licensing::Exception* ex) {
				this->_logger.LogFatal("ANSLSHelper::ActivateLicense", ex->GetExceptionMessage(), __FILE__, __LINE__);
				ex->Destroy();
			}
			if (licensingClient.IsLicenseValid()) {
				_licenseKeyStatus = SUCCESS;
				this->_logger.LogError("ANSLSHelper::ActivateLicense", "Activation Successfully.", __FILE__, __LINE__);
				return _licenseKeyStatus;
			}
			else {
				int activationStatus = licensingClient.GetLicenseActivationStatus();
				std::string activationStatusStr;
				switch (activationStatus)
				{
				case STATUS_INVALID_ACTIVATION_KEY:
					_licenseKeyStatus = INVALID_ACTIVATION_KEY;
					activationStatusStr = "Activation key is invalid";
					break;

				case STATUS_INVALID_HARDWARE_ID:
					_licenseKeyStatus = INVALID_HARDWARE_ID;
					activationStatusStr = "Hardware id does not match this computer";
					break;

				case STATUS_LICENSE_EXPIRED:
					_licenseKeyStatus = LICENSE_EXPIRED;
					activationStatusStr = "License expired";
					break;
				default:
					_licenseKeyStatus = UNKNOWN;
					activationStatusStr = "License key activation is failed. Unknonwn status";
					break;
				}
				this->_logger.LogError("ANSLSHelper::ActivateLicense", activationStatusStr, __FILE__, __LINE__);
				return _licenseKeyStatus;
			}
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			this->_logger.LogFatal("ANSLSHelper::ActivateLicense", ex->GetExceptionMessage(), __FILE__, __LINE__);
			ex->Destroy();
			LicenseKeyStatus _licenseKeyStatus;
			_licenseKeyStatus = UNKNOWN;
			return _licenseKeyStatus;
		}
	}

	int ANSLSHelper::ActivateLicenseWithCustomHWID(std::string licenseKey, 
												   std::string registrationName, 
												   std::string hardwareId, 
												   std::string& activationKey) {
		try
		{
			LicenseKeyStatus _licenseKeyStatus;
			LicenseValidationArgs validationArgs;
			LicensingClient licensingClient;
			//LoadLicense(licenseKey);
			std::wstring _licenseKey = String2WString(licenseKey);
			std::wstring _hardwareId = String2WString(hardwareId);
			std::wstring wProductId = GetProductId(licenseKey);
			this->_licenseTemplate->SetTemplateId(wProductId.c_str());
			
			licensingClient.SetLicenseTemplate(this->_licenseTemplate);
			// Always activate
			validationArgs.SetLicenseKey(_licenseKey.c_str());
			if (!registrationName.empty())
				validationArgs.SetLicenseKeyValidationData(_T("RegistrationName"), String2WString(registrationName).c_str());

			this->_logger.LogDebug("ANSLSHelper::ActivateLicenseWithCustomHWID", "Activating the license....", __FILE__, __LINE__);
			licensingClient.SetLicensingServiceUrl(this->_licenseServiceURL.c_str());
			licensingClient.SetHardwareId(_hardwareId.c_str());
			licensingClient.SetLicenseKey(_licenseKey.c_str());
			try {
				LicenseValidationResult* validationResult = licensingClient.ValidateLicense(&validationArgs);
				this->_logger.LogDebug("ANSLSHelper::ActivateLicenseWithCustomHWID", "License was successfully activated", __FILE__, __LINE__);
				//Now check if the license validation process resulted in server sending out a new license to replace the old one
				License* newLicense = validationResult->GetLicense();
				if (newLicense != NULL)
				{
					if (this->_license != NULL) delete this->_license;
					this->_license = newLicense;
					this->_license->SetActivationKey(newLicense->GetActivationKey());
					this->_license->SetHardwareId(_hardwareId.c_str());
					this->_license->SetLicenseKey(newLicense->GetLicenseKey());
					const wchar_t* actKey = this->_license->GetActivationKey();
					std::wstring wactKey(actKey);
					activationKey = WString2String(wactKey);
					//delete newLicense;
					this->_logger.LogDebug("ANSLSHelper::ActivateLicenseWithCustomHWID. New license has been received from the licensing server", mask_secret(activationKey), __FILE__, __LINE__);
					//SaveLicense();
				}
				else {// Existing _license

					const wchar_t* actKey = this->_license->GetActivationKey();
					std::wstring wactKey(actKey);
					activationKey = WString2String(wactKey);
					this->_logger.LogDebug("ANSLSHelper::ActivateLicenseWithCustomHWID", "Get existing activation key", __FILE__, __LINE__);

				}
				if (validationResult->IsLicenseExpired())
				{
					this->_logger.LogError("ANSLSHelper::ActivateLicenseWithCustomHWID", "License is expired.", __FILE__, __LINE__);
					_licenseKeyStatus = LICENSE_EXPIRED;
					return _licenseKeyStatus;

				}
				if (validationResult->IsPaymentRequired())
				{
					this->_logger.LogError("ANSLSHelper::ActivateLicenseWithCustomHWID", "Payment is required.", __FILE__, __LINE__);
					_licenseKeyStatus = PAYMENT_REQUIRED;
					return _licenseKeyStatus;
				}
			}
			catch (ANSCENTER::Licensing::Exception* ex) {
				this->_logger.LogFatal("ANSLSHelper::ActivateLicenseWithCustomHWID", ex->GetExceptionMessage(), __FILE__, __LINE__);
				ex->Destroy();
			}
			
			if (licensingClient.IsLicenseValid()) {
				_licenseKeyStatus = SUCCESS;
				this->_logger.LogDebug("ANSLSHelper::ActivateLicenseWithCustomHWID", "Activation Successfully.", __FILE__, __LINE__);
				return _licenseKeyStatus;
			}
			else {
				int activationStatus = licensingClient.GetLicenseActivationStatus();
				std::string activationStatusStr;
				switch (activationStatus)
				{
				case STATUS_INVALID_ACTIVATION_KEY:
					_licenseKeyStatus = INVALID_ACTIVATION_KEY;
					activationStatusStr = "Activation key is invalid";
					break;

				case STATUS_INVALID_HARDWARE_ID:
					//_licenseKeyStatus = INVALID_HARDWARE_ID;
					_licenseKeyStatus = SUCCESS;
					activationStatusStr = "Hardware id does not match this computer";
					break;

				case STATUS_LICENSE_EXPIRED:
					_licenseKeyStatus = LICENSE_EXPIRED;
					activationStatusStr = "License expired";
					break;
				default:
					_licenseKeyStatus = UNKNOWN;
					activationStatusStr = "License key activation is failed. Unknown status";
					break;
				}
				this->_logger.LogError("ANSLSHelper::ActivateLicense", activationStatusStr, __FILE__, __LINE__);
				return _licenseKeyStatus;
			}
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			this->_logger.LogFatal("ANSLSHelper::ActivateLicenseWithCustomHWID", ex->GetExceptionMessage(), __FILE__, __LINE__);
			ex->Destroy();
			LicenseKeyStatus _licenseKeyStatus;
			_licenseKeyStatus = UNKNOWN;
			return _licenseKeyStatus;
		}
	}
	int ANSLSHelper::InstallOfflineLicense(std::string licenseKey, std::string registrationName, std::string activationKey) {
		try {
			std::wstring _licenseKey = String2WString(licenseKey);
			std::wstring _activationKey = String2WString(activationKey);
			std::wstring _registrationName = String2WString(registrationName);
			std::wstring hardwareId = KeyHelper::GetCurrentHardwareId();

			std::wstring wProductId = GetProductId(licenseKey);
			std::string licenseFileName = Dash2Underscore(licenseKey) + ".json";
			std::string licenseFolder = GetLicenseDir();
			std::string licenseFilePath = CreateFilePath(licenseFolder,licenseFileName);
			this->_licenseFilePath = String2WString(licenseFilePath);

			this->_licenseTemplate->SetTemplateId(wProductId.c_str());
			KeyValidator keyValidator(this->_licenseTemplate);
			keyValidator.SetKey(_licenseKey.c_str());

			if (!registrationName.empty())
				keyValidator.SetValidationData(_T("RegistrationName"), _registrationName.c_str());

			if (!keyValidator.IsKeyValid())
			{
				this->_logger.LogError("ANSLSHelper::InstallOfflineLicense", "Invalid license key", __FILE__, __LINE__);
				return 0; // invalid
			}

			// We also need to verify if the activation key valid or not.
			int validationDataLen = MAX_CUSTOMER_NAME_LEN;
			unsigned char validationData[MAX_CUSTOMER_NAME_LEN] = "";
			keyValidator.QueryValidationData(NULL, validationData, &validationDataLen);
			const char* emptyString = "";

			LicensingClient licensingClient;
			// SetLicenseKeyValidationData needs to be called before SetActivationKeyTemplate !
			int comparisonResult = std::strcmp(reinterpret_cast<const char*>(validationData), emptyString);
			if (comparisonResult != 0)
				licensingClient.SetLicenseKeyValidationData(validationData, validationDataLen);
			licensingClient.SetLicenseTemplate(this->_licenseTemplate);
			licensingClient.SetLicenseKey(_licenseKey.c_str());
			licensingClient.SetHardwareId(hardwareId.c_str());
			licensingClient.SetActivationKey(_activationKey.c_str());
			bool validLicense = licensingClient.IsLicenseValid();

			if (!validLicense)
			{
				this->_logger.LogError("ANSLSHelper::InstallOfflineLicense", "Invalid activation key", __FILE__, __LINE__);
				return 0; // invalid
			}
			else {
				if (!this->_license != NULL) delete this->_license;
				this->_license = new License();
				this->_license->SetLicenseKey(_licenseKey.c_str());
				this->_license->SetLicenseKeyValidationData(validationData, validationDataLen);
				this->_license->SetHardwareId(hardwareId.c_str());
				this->_license->SetActivationKey(_activationKey.c_str());
				SaveLicense();
				return 1;
			}
		}
		catch (ANSCENTER::Licensing::Exception* ex) {
			this->_logger.LogFatal("ANSLSHelper::InstallOfflineLicense", ex->GetExceptionMessage(), __FILE__, __LINE__);
			ex->Destroy();
			return 0;
		}
	}
	
	// Utilities
	int		ANSLSHelper::ExtractModelZipFile(std::string modelZipFile, std::string password, std::string modelName, std::string& extractedOutputFolder) {
		int modelType = 1; // Default is Yolov4
		// the class name should be the same
		// 1. Unzip model zip file to a special location with folder name as model file (and version)	
		std::vector<std::string> passwordArray;
		if (!password.empty()) passwordArray.push_back(password);
		passwordArray.push_back("AnsDemoModels20@!");
		passwordArray.push_back("Sh7O7nUe7vJ/417W0gWX+dSdfcP9hUqtf/fEqJGqxYL3PedvHubJag==");
		passwordArray.push_back("3LHxGrjQ7kKDJBD9MX86H96mtKLJaZcTYXrYRdQgW8BKGt7enZHYMg==");
		size_t vectorSize = passwordArray.size();
		for (size_t  i = 0; i < vectorSize; i++) {
			if (ExtractPasswordProtectedZip(modelZipFile, passwordArray[i], modelName, extractedOutputFolder,true)) {
				break; // Break the loop when the condition is met.
			}
		}
		// 2. Check if the outputFolder exist
		if (!std::filesystem::exists(extractedOutputFolder)) {
			return -1; // That means the model file is not exist or the password is not correct
		}

		// 3. Check the model type based on the model file txt
		std::string tensorflowFile = CreateFilePath(extractedOutputFolder, "tensorflow.txt");
		if (FileExist(tensorflowFile)) {
			modelType = 0;
			return modelType;
		}
		tensorflowFile=CreateFilePath(extractedOutputFolder, "train_last.pb");
		if (FileExist(tensorflowFile)) {
			modelType = 0;
			return modelType;
		}
		std::string yoloFile = CreateFilePath(extractedOutputFolder, "yolo.txt");
		if (FileExist(yoloFile)) {
			modelType = 1;
			return modelType;
		}
		std::string yoloV4File = CreateFilePath(extractedOutputFolder, "yolov4.txt");
		if (FileExist(yoloV4File)) {
			modelType = 1;
			return modelType;
		}
		yoloV4File = CreateFilePath(extractedOutputFolder, "train_last.weights");
		if (FileExist(yoloV4File)) {
			modelType = 1;
			return modelType;
		}
		std::string yolov5File = CreateFilePath(extractedOutputFolder, "yolov5.txt");
		if (FileExist(yolov5File)) {
			modelType = 2;
			return modelType;
		}
		std::string yoloV8File = CreateFilePath(extractedOutputFolder, "yolov8.txt");
		if (FileExist(yoloV8File)) {
			modelType = 3;
			return modelType;
		}
		yoloV8File = CreateFilePath(extractedOutputFolder, "train_last.onnx");
		if (FileExist(yoloV8File)) {
			modelType = 3;
			return modelType;
		}
		std::string tensorRTFile = CreateFilePath(extractedOutputFolder, "tensorrt.txt");
		if (FileExist(tensorRTFile)) {
			modelType = 4;
			return modelType;
		}
		std::string openvinoFile = CreateFilePath(extractedOutputFolder, "openvino.txt");
		if (FileExist(openvinoFile)) {
			modelType = 5;
			return modelType;
		}
		openvinoFile = CreateFilePath(extractedOutputFolder, "train_last.xml");
		if (FileExist(openvinoFile)) {
			modelType = 5;
			return modelType;
		}
		return modelType;
	}
	bool	ANSLSHelper::ZipFolerWithPassword(std::string folderPath, std::string zipFilePath, std::string password) {
		 return ZipFolderWithPassword(folderPath.c_str(), zipFilePath.c_str(), password.c_str());
	}
	int		ANSLSHelper::PrepareEdgeModel(std::string modelZipFile, 
									      std::string zipFilePassword, 
										  std::string edgePassword, 
										  std::string modelName, 
										  std::string edgeSerialNumber, 
										  std::string& configFilePathName, 
										  std::string& labelMapPathName, 
										  std::string& outputModelFolder) {
		//1. First perform upzip and zip folder to a file with edgePassword protection
		std::string extractedOutputFolder;
		int		modelType= ANSLSHelper::ExtractModelZipFile(modelZipFile, zipFilePassword, modelName, extractedOutputFolder);
		if (modelType < 0) return -1;
		// Check if the extractOutputFolder exist
		if (!FolderExist(extractedOutputFolder)) return -2;

		//2. Prepare the model zip file place holder (GenericModel.zip)
		std::string modelFolderName = ExtractFolderName(extractedOutputFolder); // It should return the folder name: GenericModel
		std::string zipFileName = modelName + ".zip"; // GenericModel.zip
		std::string modelFolder = GetParentFolder(extractedOutputFolder); // C:\Users\Alex\AppData\Local\Temp\Models\EdgeModels

		std::string configFilePath="";
		std::string labelFilePath = "";
		std::string modelFilePath = "";

		std::string configFilePathDest = "";
		std::string modelFilePathDest = "";

		std::string configTempName = "";
		std::string modelTempName = "";

		std::string modelFileBinPath = "";
		std::string modelTempBinName = "";
		std::string modelBinFilePathDest = "";
		std::vector<std::string> filesToKeep;
		filesToKeep.clear();
		// 2. Extract the content
		switch (modelType) 
		{
			case 0: //tensorflow
				configFilePath = CreateFilePath(extractedOutputFolder, "pipeline.config");
				labelFilePath = CreateFilePath(extractedOutputFolder, "classes.pbtxt");
				modelFilePath = CreateFilePath(extractedOutputFolder, "train_last.pb");

				// We need to delete all files, except for three files: train.cfg, classes.names, train_last.weights in the extractedOutputFolder
				filesToKeep.push_back("pipeline.config");
				filesToKeep.push_back("classes.pbtxt");
				filesToKeep.push_back("train_last.pb");
				filesToKeep.push_back("tensorflow.txt");
				DeleteFilesInFolderExcept(extractedOutputFolder, filesToKeep);

				// Rename
				configTempName = modelName + ".config";
				modelTempName = modelName + ".pb";
				configFilePathDest = CreateFilePath(extractedOutputFolder, configTempName);
				modelFilePathDest = CreateFilePath(extractedOutputFolder, modelTempName);
				RenameFile(configFilePath, configFilePathDest);
				RenameFile(modelFilePath, modelFilePathDest);

				if (FileExist(configFilePathDest))configFilePathName = configFilePathDest;
				else configFilePathName = "";

				if (FileExist(labelFilePath))labelMapPathName = labelFilePath;
				else labelMapPathName = "";

				if (FolderExist(modelFolder))outputModelFolder = modelFolder;
				else outputModelFolder = "";
				break;
			case 1: //yolov4
				configFilePath = CreateFilePath(extractedOutputFolder, "train.cfg");
				labelFilePath  = CreateFilePath(extractedOutputFolder, "classes.names");
				modelFilePath  = CreateFilePath(extractedOutputFolder, "train_last.weights");

				// We need to delete all files, except for three files: train.cfg, classes.names, train_last.weights in the extractedOutputFolder
				filesToKeep.push_back("train.cfg");
				filesToKeep.push_back("classes.names");
				filesToKeep.push_back("train_last.weights");
				filesToKeep.push_back("yolo.txt");
				filesToKeep.push_back("yolov4.txt");

				DeleteFilesInFolderExcept(extractedOutputFolder, filesToKeep);

				// Rename
				configTempName = modelName + ".cfg";
				modelTempName = modelName + ".weights";
				configFilePathDest = CreateFilePath(extractedOutputFolder, configTempName);
				modelFilePathDest  = CreateFilePath(extractedOutputFolder, modelTempName);
				RenameFile(configFilePath, configFilePathDest);
				RenameFile(modelFilePath, modelFilePathDest);

				if (FileExist(configFilePathDest))configFilePathName = configFilePathDest;
				else configFilePathName = "";

				if (FileExist(labelFilePath))labelMapPathName = labelFilePath;
				else labelMapPathName = "";

				if (FolderExist(modelFolder))outputModelFolder = modelFolder;
				else outputModelFolder = "";

				break;
			case 2: // yolov5
				labelFilePath = CreateFilePath(extractedOutputFolder, "classes.names");
				modelFilePath = CreateFilePath(extractedOutputFolder, "train_last.onnx");

				// We need to delete all files, except for three files: train.cfg, classes.names, train_last.weights in the extractedOutputFolder
				filesToKeep.push_back("classes.names");
				filesToKeep.push_back("train_last.onnx");
				filesToKeep.push_back("yolov5.txt");

				DeleteFilesInFolderExcept(extractedOutputFolder, filesToKeep);

				// Rename
				modelTempName = modelName + ".onnx";
				modelFilePathDest = CreateFilePath(extractedOutputFolder, modelTempName);
				RenameFile(modelFilePath, modelFilePathDest);

				if (FileExist(configFilePathDest))configFilePathName = configFilePathDest;
				else configFilePathName = "";

				if (FileExist(labelFilePath))labelMapPathName = labelFilePath;
				else labelMapPathName = "";

				if (FolderExist(modelFolder))outputModelFolder = modelFolder;
				else outputModelFolder = "";
				break;
			case 3: // yolov8
				labelFilePath = CreateFilePath(extractedOutputFolder, "classes.names");
				modelFilePath = CreateFilePath(extractedOutputFolder, "train_last.onnx");

				// We need to delete all files, except for three files: train.cfg, classes.names, train_last.weights in the extractedOutputFolder
				filesToKeep.push_back("classes.names");
				filesToKeep.push_back("train_last.onnx");
				filesToKeep.push_back("yolov8.txt");

				DeleteFilesInFolderExcept(extractedOutputFolder, filesToKeep);

				// Rename
				modelTempName = modelName + ".onnx";
				modelFilePathDest = CreateFilePath(extractedOutputFolder, modelTempName);
				RenameFile(modelFilePath, modelFilePathDest);

				if (FileExist(configFilePathDest))configFilePathName = configFilePathDest;
				else configFilePathName = "";

				if (FileExist(labelFilePath))labelMapPathName = labelFilePath;
				else labelMapPathName = "";

				if (FolderExist(modelFolder))outputModelFolder = modelFolder;
				else outputModelFolder = "";
				break;
			case 4: // tensorrt
				labelFilePath = CreateFilePath(extractedOutputFolder, "classes.names");
				modelFilePath = CreateFilePath(extractedOutputFolder, "train_last.onnx");

				// We need to delete all files, except for three files: train.cfg, classes.names, train_last.weights in the extractedOutputFolder
				filesToKeep.push_back("classes.names");
				filesToKeep.push_back("train_last.onnx");
				filesToKeep.push_back("tensorrt.txt");

				DeleteFilesInFolderExcept(extractedOutputFolder, filesToKeep);

				// Rename
				modelTempName = modelName + ".onnx";
				modelFilePathDest = CreateFilePath(extractedOutputFolder, modelTempName);
				RenameFile(modelFilePath, modelFilePathDest);

				if (FileExist(configFilePathDest))configFilePathName = configFilePathDest;
				else configFilePathName = "";

				if (FileExist(labelFilePath))labelMapPathName = labelFilePath;
				else labelMapPathName = "";

				if (FolderExist(modelFolder))outputModelFolder = modelFolder;
				else outputModelFolder = "";
				break;
			case 5: // openvino
				labelFilePath = CreateFilePath(extractedOutputFolder, "classes.names");
				modelFilePath = CreateFilePath(extractedOutputFolder, "train_last.xml");
				modelFileBinPath = CreateFilePath(extractedOutputFolder, "train_last.bin");

				// We need to delete all files, except for three files: train.cfg, classes.names, train_last.weights in the extractedOutputFolder
				filesToKeep.push_back("classes.names");
				filesToKeep.push_back("train_last.xml");
				filesToKeep.push_back("train_last.bin");
				filesToKeep.push_back("metadata.yaml");
				filesToKeep.push_back("openvino.txt");
				DeleteFilesInFolderExcept(extractedOutputFolder, filesToKeep);

				// Rename
				modelTempName = modelName + ".xml";
				modelFilePathDest = CreateFilePath(extractedOutputFolder, modelTempName);
				RenameFile(modelFilePath, modelFilePathDest);

				modelTempBinName = modelName + ".bin";
				modelBinFilePathDest = CreateFilePath(extractedOutputFolder, modelTempBinName);
				RenameFile(modelFileBinPath, modelBinFilePathDest);


				if (FileExist(configFilePathDest))configFilePathName = configFilePathDest;
				else configFilePathName = "";

				if (FileExist(labelFilePath))labelMapPathName = labelFilePath;
				else labelMapPathName = "";

				if (FolderExist(modelFolder))outputModelFolder = modelFolder;
				else outputModelFolder = "";
				break;
			default:
				configFilePath = CreateFilePath(extractedOutputFolder, "train.cfg");
				labelFilePath  = CreateFilePath(extractedOutputFolder, "classes.names");
				modelFilePath  = CreateFilePath(extractedOutputFolder, "train_last.weights");

				// We need to delete all files, except for three files: train.cfg, classes.names, train_last.weights in the extractedOutputFolder
				filesToKeep.push_back("train.cfg");
				filesToKeep.push_back("classes.names");
				filesToKeep.push_back("train_last.weights");
				DeleteFilesInFolderExcept(extractedOutputFolder, filesToKeep);

				// Rename
				configTempName = modelName + ".cfg";
				modelTempName = modelName + ".weights";
				configFilePathDest = CreateFilePath(extractedOutputFolder, configTempName);
				modelFilePathDest  = CreateFilePath(extractedOutputFolder, modelTempName);
				RenameFile(configFilePath, configFilePathDest);
				RenameFile(modelFilePath, modelFilePathDest);

				if (FileExist(configFilePathDest))configFilePathName = configFilePathDest;
				else configFilePathName = "";
				if (FileExist(labelFilePath))labelMapPathName = labelFilePath;
				else labelMapPathName = "";
				if (FolderExist(modelFolder))outputModelFolder = modelFolder;
				else outputModelFolder = "";
				break;
		}

		//3. Quit
		return modelType;

	}
	std::string  ANSLSHelper::GetSystemHardwareInformation() {
		//SPDLogger&      _logger = SPDLogger::GetInstance("ANSInfoLog",true);
		try {
			boost::property_tree::ptree root;
			boost::property_tree::ptree cpuNode, 
										osNode, 
										gpuNode, 
										ramNode, 
										mainBoardNode, 
										batteryNode, 
										diskNode;
			// CPU information
			auto sockets = hwinfo::getAllSockets();
			for (auto& s : sockets) {
				const auto& cpu = s.CPU();
				boost::property_tree::ptree cpuNodeChild;
				cpuNodeChild.put("Socket", s.id());
				cpuNodeChild.put("vendor", cpu.vendor());
				cpuNodeChild.put("model", cpu.modelName());
				cpuNodeChild.put("physical_cores", cpu.numPhysicalCores());
				cpuNodeChild.put("logical_cores", cpu.numLogicalCores());
				cpuNodeChild.put("max_frequency", cpu.maxClockSpeed_MHz());
				cpuNodeChild.put("regular_frequency", cpu.regularClockSpeed_MHz());
				cpuNodeChild.put("min_frequency", cpu.minClockSpeed_MHz());
				cpuNodeChild.put("current_frequency", cpu.currentClockSpeed_MHz());
				cpuNodeChild.put("cache_size", cpu.cacheSize_Bytes());
				cpuNode.push_back(std::make_pair("", cpuNodeChild));
			}
			root.add_child("CPU", cpuNode);
			
			// OS information
			hwinfo::OS os;
			osNode.put("Operating_System", os.fullName());
			osNode.put("short_name", os.name());
			osNode.put("version", os.version());
			osNode.put("kernel", os.kernel());
			osNode.put("architecture", (os.is32bit() ? "32 bit" : "64 bit"));
			osNode.put("endianess", os.isLittleEndian() ? "little endian" : "big endian");
			root.add_child("OS", osNode);

			// GPU information
			auto gpus = hwinfo::getAllGPUs();
			for (auto& gpu : gpus) {
				boost::property_tree::ptree gpuNodeChild;
				gpuNodeChild.put("GPU", gpu.id());
				gpuNodeChild.put("vendor", gpu.vendor());
				gpuNodeChild.put("model", gpu.name());
				gpuNodeChild.put("diver_version", gpu.driverVersion());
				gpuNodeChild.put("memory[MiB]", static_cast<double>(gpu.memory_Bytes()) / 1024.0 / 1024.0);
				gpuNodeChild.put("frequency", gpu.frequency_MHz());
				gpuNode.push_back(std::make_pair("", gpuNodeChild));
			}

			root.add_child("GPU", gpuNode);
	

			// RAM information
			hwinfo::RAM ram;
			ramNode.put("vendor", ram.vendor());
			ramNode.put("name", ram.model());
			ramNode.put("serial_number", ram.serialNumber());
			ramNode.put("size[MiB]", ram.total_Bytes() / 1024 / 1024);
			ramNode.put("free[MiB]", ram.free_Bytes() / 1024 / 1024);
			ramNode.put("available[MiB]", ram.available_Bytes() / 1024 / 1024);
			root.add_child("RAM", ramNode);

			// Main board information
			hwinfo::MainBoard main_board;
			mainBoardNode.put("vendor", main_board.vendor());
			mainBoardNode.put("name", main_board.name());
			mainBoardNode.put("version", main_board.version());
			mainBoardNode.put("serial_number", main_board.serialNumber());
			root.add_child("MAINBOARD", mainBoardNode);

			
			// Battery information
			auto batteries = hwinfo::getAllBatteries();
			if (!batteries.empty()) {
				int battery_counter = 0;
				for (auto& battery : batteries) {
					boost::property_tree::ptree  batteryNodeChild;
					batteryNodeChild.put("Battery", battery_counter++);
					batteryNodeChild.put("vendor", battery.vendor());
					batteryNodeChild.put("model", battery.model());
					batteryNodeChild.put("serial_number", battery.serialNumber());
					batteryNodeChild.put("charging", (battery.charging() ? "yes" : "no"));
					batteryNodeChild.put("capacity", battery.capacity());
					batteryNode.push_back(std::make_pair("", batteryNodeChild));
				}
			}
			else {
				boost::property_tree::ptree  batteryNodeChild;
				batteryNodeChild.put("Battery", 0);
				batteryNodeChild.put("vendor", "NA");
				batteryNodeChild.put("model", "NA");
				batteryNodeChild.put("serial_number", "NA");
				batteryNodeChild.put("charging", "NA");
				batteryNodeChild.put("capacity", "NA");
				batteryNode.push_back(std::make_pair("", batteryNodeChild));
			}
			root.add_child("BATTERIES", batteryNode);

			// Disks information
		/*	auto disks = hwinfo::getAllDisks();
			if (!disks.empty()) {
				int disk_counter = 0;
				for (const auto& disk : disks) {
					boost::property_tree::ptree  diskNodeChild;
					diskNodeChild.put("Disk", disk_counter++);
					diskNodeChild.put("vendor", disk.vendor());
					diskNodeChild.put("model", disk.model());
					diskNodeChild.put("serial_number", disk.serialNumber());
					diskNodeChild.put("size", disk.size_Bytes());
					diskNode.push_back(std::make_pair("", diskNodeChild));
				}
			}
			else {
				boost::property_tree::ptree  diskNodeChild;
				diskNodeChild.put("Disk", 0);
				diskNodeChild.put("vendor", "NA");
				diskNodeChild.put("model", "NA");
				diskNodeChild.put("serial_number", "NA");
				diskNodeChild.put("size", "NA");
				diskNode.push_back(std::make_pair("", diskNodeChild));
			}*/

			boost::property_tree::ptree  diskNodeChild;
			diskNodeChild.put("Disk", 0);
			diskNodeChild.put("vendor", "NA");
			diskNodeChild.put("model", "NA");
			diskNodeChild.put("serial_number", "NA");
			diskNodeChild.put("size", "NA");
			diskNode.push_back(std::make_pair("", diskNodeChild));
			root.add_child("DISKS", diskNode);

			try {
				std::ostringstream stream;
				boost::property_tree::write_json(stream, root,false);
				std::string st = stream.str();
				//this->_logger.LogDebug("System Information", st);
				std::cout << st << std::endl;
				return st;
			}
			catch (Exception* ex)
			{
				//this->_logger.LogFatal("ANSLSHelper::ValidateLicenseKey", ex->GetExceptionMessage());
				std::cout << ex->GetExceptionMessage() << std::endl;
				ex->Destroy();
				return "Cannot retrieve system information";
			}
	
		}
		catch (Exception* ex)
		{
			//this->_logger.LogFatal("ANSLSHelper::ValidateLicenseKey", ex->GetExceptionMessage());
			std::cout << ex->GetExceptionMessage() << std::endl;
			ex->Destroy();
			return "Cannot retrieve system information";
		}
		
	}
	bool ANSLSHelper::ReleaseLogger() {
		SPDLogger& logger = SPDLogger::GetInstance("ReleaseLogger", true);
		return logger.Release();
	}

}

// ============================================================================
// Runtime DebugView toggle — gates ANS_DBG without requiring a rebuild.
//
// Precedence per poll: env var ANSCENTER_DBGVIEW wins over the sentinel file.
// Poll interval is 2 s, so toggling takes effect within that window. Hot-path
// cost on a cache hit is a single relaxed atomic load + branch.
// ============================================================================
extern "C" ANSLICENSE_API int ANSCENTER_IsDebugViewEnabled(void) {
	using clock = std::chrono::steady_clock;
	static std::atomic<int> s_enabled{0};
	static std::atomic<long long> s_nextCheckTick{0};

	const long long now = clock::now().time_since_epoch().count();
	const long long next = s_nextCheckTick.load(std::memory_order_relaxed);
	if (now < next) {
		return s_enabled.load(std::memory_order_relaxed);
	}

	int enabled = 0;
	const char* env = std::getenv("ANSCENTER_DBGVIEW");
	if (env && env[0] == '1' && env[1] == '\0') {
		enabled = 1;
	} else if (env && env[0] == '0' && env[1] == '\0') {
		enabled = 0;
	} else {
#ifdef _WIN32
		const wchar_t* kSentinel = L"C:\\ProgramData\\ANSCENTER\\ansvisdebugview.txt";
#else
		const char* kSentinel = "/tmp/ansvisdebugview.txt";
#endif
		std::error_code ec;
		enabled = std::filesystem::exists(kSentinel, ec) ? 1 : 0;
	}

	s_enabled.store(enabled, std::memory_order_relaxed);
	const long long deadline = now +
		std::chrono::duration_cast<clock::duration>(std::chrono::seconds(2)).count();
	s_nextCheckTick.store(deadline, std::memory_order_relaxed);
	return enabled;
}