/********************************************************************************
 * Copyright (c) 2025-2026 ZF Friedrichshafen AG
 *
 * This program and the accompanying materials are made available under the
 * terms of the Apache License Version 2.0 which is available at
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Contributors:
 *   Erik Verhoeven - initial API and implementation
 ********************************************************************************/

#include "app_config.h"
#include "../../global/exec_dir_helper.h"
#include <interfaces/com.h>
#include <interfaces/process.h>
#include <interfaces/ipc.h>
#include <support/crc.h>
#include "toml_parser/parser_toml.h"
#include <support/toml.h>
#include "module_control.h"
#include "repository.h"
#include "app_settings.h"

#if __unix__
#include <utime.h>
#endif

CAppConfig& GetAppConfig()
{
    static CAppConfig app_config;
    return app_config;
}

bool CAppConfig::LoadInstallationManifests()
{
    // Check for allowance
    bool bServerApp = false;
    switch (GetAppSettings().GetContextType())
    {
    case sdv::app::EAppContext::main:
    case sdv::app::EAppContext::isolated:
    case sdv::app::EAppContext::external:
    case sdv::app::EAppContext::maintenance:
        bServerApp = true;
    default:
        break;
    }

    std::filesystem::path pathCore = GetAppSettings().GetFrameworkDir();
    std::filesystem::path pathExe = GetExecDirectory();
    std::filesystem::path pathInstall = GetAppSettings().GetInstallDir();

    // Load the core manifest (should work in any case).
    m_manifestCore = CInstallManifest();
    if (!m_manifestCore.Load(pathCore)) return false;

    // Check whether the exe path is identical to the core. If so, skip the manifest. If there is no manifest, this is not an error.
    if (pathCore != pathExe)
    {
        m_manifestExe = CInstallManifest();
        m_manifestExe.Load(pathExe);
    }
    else
        m_manifestExe.Clear();

    // Done when not server application.
    if (!bServerApp) return true;

    // Get the user manifests. If there is no manifest, this is not an error.
    if (!pathInstall.empty())
    {
        try
        {
            for (auto const& dir_entry : std::filesystem::directory_iterator{pathInstall})
            {
                if (!dir_entry.is_directory())
                    continue;
                if (!std::filesystem::exists(dir_entry.path() / "install_manifest.toml"))
                    continue;
                CInstallManifest manifestUser;
                if (manifestUser.Load(dir_entry.path(), false))
                    m_vecUserManifests.push_back(manifestUser);
            }
        } catch (const std::filesystem::filesystem_error& /*rexcept*/)
        {
            SDV_LOG_ERROR("Failed to iterate installation directory: '", pathInstall.generic_u8string(), "'.");
            return false;
        }
    }
    return true;
}

void CAppConfig::UnloadInstallatonManifests()
{
    m_vecUserManifests.clear();
    m_manifestExe.Clear();
    m_manifestCore.Clear();
}

bool CAppConfig::LoadAppConfigs()
{
    // Isolated and external applications do not load configurations
    if (GetAppSettings().IsIsolatedApplication() || GetAppSettings().IsExternalApplication()) return false;

    // When running as server application, load the system configurations
    if (GetAppSettings().IsMainApplication() || GetAppSettings().IsMaintenanceApplication())
    {
        m_vecSysConfigs.clear();

        auto vecSysConfigs = GetAppSettings().GetSystemConfigPaths();
        for (const std::filesystem::path& rpathConfig : vecSysConfigs)
        {
            CAppConfigFile config(rpathConfig);
            if (!config.IsLoaded())
            {
                SDV_LOG_ERROR("Failed to load system configuration '", rpathConfig.generic_u8string(), "'.");
                return false;
            }

            // Start the configuration by the repository
            // cppcheck warns that the following condition is always true. This is incorrect. Suppress the warning.
            // cppcheck-suppress knownConditionTrueFalse
            if (GetRepository().StartFromConfig(config, false) == sdv::core::EConfigProcessResult::failed)
            {
                SDV_LOG_ERROR("Failed to start the system configuration '", rpathConfig.generic_u8string(), "'.");
                return false;
            }

            // Loading was successful.
            m_vecSysConfigs.push_back(std::move(config));
        }
    }

    // Reset config baseline (all changes from here are logged).
    ResetConfigBaseline();

    // Load the user configuration
    std::filesystem::path pathUserConfig = GetAppSettings().GetUserConfigPath();
    if (pathUserConfig.empty())
        m_configUserConfig.Clear();
    else
    {
        CAppConfigFile config(pathUserConfig);
        if (!config.IsLoaded())
        {
            SDV_LOG_ERROR("Failed to load user configuration '", pathUserConfig.generic_u8string(), "'.");
            return false;
        }

        // Load the configuration by the repository
        // cppcheck warns that the following condition is always true. This is incorrect. Suppress the warning.
        // cppcheck-suppress knownConditionTrueFalse
        if (GetRepository().StartFromConfig(config, false) == sdv::core::EConfigProcessResult::failed)
        {
            SDV_LOG_ERROR("Failed to start the system configuration '", pathUserConfig.generic_u8string(), "'.");
            return false;
        }

        // Loading was successful.
        m_configUserConfig = std::move(config);
    }

    return true;
}

void CAppConfig::SaveAppConfigs()
{
    // User configuration is always saved when changed.
    if (m_configUserConfig.IsLoaded() && !m_configUserConfig.SaveConfigFile())
        SDV_LOG_ERROR("Failed to save the user configuration '", m_configUserConfig.ConfigPath(), "'.");

    // System configurations are only saved when running as maintenance application.
    if (GetAppSettings().IsMaintenanceApplication())
    {
        for (const CAppConfigFile& rconfig : m_vecSysConfigs)
        {
            if (!rconfig.IsLoaded() && !rconfig.SaveConfigFile())
                SDV_LOG_ERROR("Failed to save system configuration '", rconfig.ConfigPath(), "'.");
        }
    }
}

sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8string& /*ssContent*/)
{
    // Check for allowance
    switch (GetAppSettings().GetContextType())
    {
    case sdv::app::EAppContext::main:
    case sdv::app::EAppContext::isolated:
    case sdv::app::EAppContext::external:
    case sdv::app::EAppContext::maintenance:
        return sdv::core::EConfigProcessResult::failed;
    default:
        break;
    }

    if (GetAppControl().GetOperationState() != sdv::app::EAppOperationState::configuring)
        return sdv::core::EConfigProcessResult::failed;

    //// Reset the current baseline
    //ResetConfigBaseline();

    //toml_parser::CParser parser(ssContent);

    //// Check for config file compatibility
    //auto ptrConfigVersion = parser.Root().Direct("Configuration.Version");
    //if (!ptrConfigVersion)
    //{
    //    SDV_LOG_ERROR("Configuration version must be present in the configuration file.");
    //    return sdv::core::EConfigProcessResult::failed;
    //}
    //if (ptrConfigVersion->GetValue() != SDVFrameworkInterfaceVersion)
    //{
    //    SDV_LOG_ERROR("Incompatible configuration file version.");
    //    return sdv::core::EConfigProcessResult::failed;
    //}

    //// If this is not a main, isolated or maintenance application, load all modules in the component and module sections.
    //std::map<std::filesystem::path, sdv::core::TModuleID> mapModules;
    //size_t nLoadable = 0;
    //size_t nNotLoadable = 0;
    //if (!GetAppSettings().IsMainApplication() && !GetAppSettings().IsIsolatedApplication() &&
    //    !GetAppSettings().IsExternalApplication() && !GetAppSettings().IsMaintenanceApplication())
    //{
    //    // Load all modules in the component section
    //    auto ptrComponents = parser.Root().Direct("Component");
    //    if (ptrComponents && ptrComponents->Cast<toml_parser::CArray>())
    //    {
    //        for (uint32_t uiIndex = 0; uiIndex < ptrComponents->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
    //        {
    //            auto ptrComponent = ptrComponents->Cast<toml_parser::CArray>()->Get(uiIndex);
    //            if (ptrComponent)
    //            {
    //                // Get the information from the component.
    //                std::filesystem::path pathModule;
    //                auto ptrModule = ptrComponent->Direct("Path");
    //                if (ptrModule) pathModule = static_cast<std::string>(ptrModule->GetValue());

    //                // If there is no path, this is allowed... skip this module
    //                if (pathModule.empty()) continue;

    //                // Load the module.
    //                sdv::core::TModuleID tModuleID = GetModuleControl().Load(pathModule.generic_u8string());
    //                if (tModuleID)
    //                {
    //                    mapModules[pathModule] = tModuleID;
    //                    nLoadable++;
    //                }
    //                else
    //                {
    //                    SDV_LOG_ERROR("Failed to load module: ", pathModule);
    //                    nNotLoadable++;
    //                    continue;
    //                }
    //            }
    //        }
    //    }

    //    // Load all modules from the module section.
    //    auto ptrModules = parser.Root().Direct("Module");
    //    if (ptrModules && ptrModules->Cast<toml_parser::CArray>())
    //    {
    //        for (uint32_t uiIndex = 0; uiIndex < ptrModules->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
    //        {
    //            auto ptrModule = ptrModules->Cast<toml_parser::CArray>()->Get(uiIndex);
    //            if (ptrModule)
    //            {
    //                // Get the information from the component.
    //                std::filesystem::path pathModule;
    //                auto ptrModulePath = ptrModule->Direct("Path");
    //                if (ptrModulePath) pathModule = static_cast<std::string>(ptrModulePath->GetValue());

    //                if (pathModule.empty())
    //                {
    //                    SDV_LOG_ERROR("Missing module path for component configuration.");
    //                    nNotLoadable++;
    //                    continue;
    //                }

    //                // Load the module.
    //                sdv::core::TModuleID tModuleID = GetModuleControl().Load(pathModule.generic_u8string());
    //                if (tModuleID)
    //                {
    //                    mapModules[pathModule] = tModuleID;
    //                    nLoadable++;
    //                }
    //                else
    //                {
    //                    SDV_LOG_ERROR("Failed to load module: ", pathModule);
    //                    nNotLoadable++;
    //                    continue;
    //                }
    //            }
    //        }
    //    }
    //}

    //// Start all components from the component section
    //std::filesystem::path pathLastModule;
    //auto ptrComponents = parser.Root().Direct("Component");
    //if (ptrComponents && ptrComponents->Cast<toml_parser::CArray>())
    //{
    //    for (uint32_t uiIndex = 0; uiIndex < ptrComponents->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
    //    {
    //        auto ptrComponent = ptrComponents->Cast<toml_parser::CArray>()->Get(uiIndex);
    //        if (ptrComponent)
    //        {
    //            // Get the information from the component.
    //            std::filesystem::path pathModule;
    //            auto ptrModule = ptrComponent->Direct("Path");
    //            if (ptrModule) pathModule = static_cast<std::string>(ptrModule->GetValue());
    //            std::string ssClass;
    //            auto ptrClass = ptrComponent->Direct("Class");
    //            if (ptrClass) ssClass = static_cast<std::string>(ptrClass->GetValue());
    //            std::string ssName;
    //            auto ptrName = ptrComponent->Direct("Name");
    //            if (ptrName) ssName = static_cast<std::string>(ptrName->GetValue());

    //            // If there is a path, store it. If there is none, take the last stored
    //            if (!pathModule.empty())
    //                pathLastModule = pathModule;
    //            else
    //                pathModule = pathLastModule;

    //            if (pathModule.empty())
    //            {
    //                SDV_LOG_ERROR("Missing module path for component configuration.");
    //                nNotLoadable++;
    //                continue;
    //            }
    //            if (ssClass.empty())
    //            {
    //                SDV_LOG_ERROR("Missing component class name in the configuration.");
    //                nNotLoadable++;
    //                continue;
    //            }

    //            // In case of a main, isolated or maintenance application, ignore the module. In all other cases, the module was
    //            // loaded; get the module ID.
    //            sdv::core::TObjectID tObjectID = 0;
    //            if (!GetAppSettings().IsMainApplication() && !GetAppSettings().IsIsolatedApplication() &&
    //                !GetAppSettings().IsExternalApplication() && !GetAppSettings().IsMaintenanceApplication())
    //            {
    //                auto itModule = mapModules.find(pathModule);
    //                if (itModule == mapModules.end()) continue; // Module was not loaded before...
    //                tObjectID = GetRepository().CreateObjectFromModule(itModule->second, ssClass, ssName, ptrComponent->GenerateTOML());
    //            }
    //            else
    //                tObjectID = GetRepository().CreateObject2(ssClass, ssName, ptrComponent->GenerateTOML());

    //            if (!tObjectID)
    //            {
    //                SDV_LOG_ERROR("Failed to load component: ", ssClass);
    //                nNotLoadable++;
    //                continue;
    //            }
    //            else
    //                nLoadable++;
    //        }
    //    }
    //}
    //if (!nNotLoadable)
    //    return sdv::core::EConfigProcessResult::successful;
    //if (!nLoadable)
    //    return sdv::core::EConfigProcessResult::failed;
    return sdv::core::EConfigProcessResult::partially_successful;
}

sdv::core::EConfigProcessResult CAppConfig::LoadConfig(/*in*/ const sdv::u8string& ssConfigPath)
{
    // Even though a server based application is not allowed to call this function, it is called by the startup function. The
    // prevention of access to this function is done through the interface not being available.
    bool bServerApp = false;
    switch (GetAppSettings().GetContextType())
    {
    case sdv::app::EAppContext::main:
    case sdv::app::EAppContext::isolated:
    case sdv::app::EAppContext::maintenance:
        bServerApp = true;
    default:
        break;
    }

    std::filesystem::path pathConfig;
    if (!bServerApp)
    {
        // Close the configuration before
        CloseConfig();

        // Add the current path to the directory search path list.
        AddCurrentPath();

        // Determine the absolute path.
        pathConfig = ComposeConfigPathStandaloneApp(static_cast<std::string>(ssConfigPath));
        if (pathConfig.empty())
        {
            SDV_LOG_ERROR("Configuration file was not found \"", ssConfigPath, "\"");
            return sdv::core::EConfigProcessResult::failed;
        }
    }
    else
        pathConfig = std::filesystem::u8path(static_cast<std::string>(ssConfigPath));

    CAppConfigFile config(pathConfig);
    if (!config.IsLoaded())
    {
        SDV_LOG_ERROR("Failed to load user configuration '", pathConfig.generic_u8string(), "'.");
        return sdv::core::EConfigProcessResult::failed;
    }

    // Load the configuration by the repository
    sdv::core::EConfigProcessResult eResult = GetRepository().StartFromConfig(config, true);
    // cppcheck warns that the following condition is always true. This is incorrect. Suppress the warning.
    // cppcheck-suppress knownConditionTrueFalse
    if (eResult == sdv::core::EConfigProcessResult::failed)
    {
        SDV_LOG_ERROR("Failed to start the system configuration '", pathConfig.generic_u8string(), "'.");
        return sdv::core::EConfigProcessResult::failed;
    }

    // Loading was successful.
    m_configUserConfig = std::move(config);

    return eResult;
}

bool CAppConfig::SaveConfig(/*in*/ const sdv::u8string& ssConfigPath) const
{
    // Check for allowance
    switch (GetAppSettings().GetContextType())
    {
    case sdv::app::EAppContext::main:
    case sdv::app::EAppContext::isolated:
    case sdv::app::EAppContext::maintenance:
        return false;
    default:
        break;
    }

    // TODO... synchronize the configuration from the repository to the configuration file.
    return m_configUserConfig.SaveConfigFile(static_cast<std::string>(ssConfigPath));
}

sdv::u8string CAppConfig::GenerateConfigString() const
{
    // Check for allowance
    switch (GetAppSettings().GetContextType())
    {
    case sdv::app::EAppContext::main:
    case sdv::app::EAppContext::isolated:
    case sdv::app::EAppContext::maintenance:
        return {};
    default:
        break;
    }

    // Get the configuration string
    std::string ssConfig;
    bool bChanged = false;
    if (!m_configUserConfig.UpdateConfigString(ssConfig, bChanged))
        return {};
    return ssConfig;
}

void CAppConfig::CloseConfig()
{
    // Check for allowance
    switch (GetAppSettings().GetContextType())
    {
    case sdv::app::EAppContext::main:
    case sdv::app::EAppContext::isolated:
    case sdv::app::EAppContext::maintenance:
        return;
    default:
        break;
    }

    // TODO... remove all components from baseline
}

bool CAppConfig::AddConfigSearchDir(/*in*/ const sdv::u8string& ssDir)
{
    // Check for allowance
    switch (GetAppSettings().GetContextType())
    {
    case sdv::app::EAppContext::main:
    case sdv::app::EAppContext::isolated:
    case sdv::app::EAppContext::maintenance:
        return false;
    default:
        break;
    }

    if (GetAppControl().GetOperationState() != sdv::app::EAppOperationState::configuring)
        return false;

    // Add initial paths if not done so already
    AddCurrentPath();

    std::unique_lock<std::mutex> lock(m_mtxSearchPaths);

    std::filesystem::path pathDir(ssDir.c_str());

    // Relative paths are always relative of the executable
    if (pathDir.is_relative())
        pathDir = GetExecDirectory() / ssDir.c_str();

    // Remove any indirections
    pathDir = pathDir.lexically_normal();

    // If the current path is not a directory, it cannot be added
    if (!std::filesystem::is_directory(pathDir)) return false;

    // Check whether the path is already in the list
    if (std::find(m_lstSearchPaths.begin(), m_lstSearchPaths.end(), pathDir) != m_lstSearchPaths.end())
        return true; // This is not an error

                     // Add the path
    m_lstSearchPaths.push_back(pathDir);

    return true;
}

void CAppConfig::ResetConfigBaseline()
{
    //// Check for allowance
    //switch (GetAppSettings().GetContextType())
    //{
    //case sdv::app::EAppContext::main:
    //case sdv::app::EAppContext::isolated:
    //case sdv::app::EAppContext::maintenance:
    //    return;
    //default:
    //    break;
    //}

    GetRepository().ResetConfigBaseline();
}

#ifdef _MSC_VER
    // Prevent bogus warning about uninitialized memory for the variable *hFile.
    #pragma warning(push)
    #pragma warning(disable : 6001)
#endif

bool CAppConfig::Install(/*in*/ const sdv::u8string& /*ssInstallName*/, /*in*/ const sdv::sequence<sdv::installation::SFileDesc>& /*seqFiles*/)
{
//    // Installations can only be done in the main application.
//    if (!GetAppSettings().IsMainApplication()) return false;
//
//    std::unique_lock<std::mutex> lock(m_mtxInstallations);
//
//    // Check whether the installation already exists. If so, cancel the installation.
//    if (m_mapInstallations.find(ssInstallName) != m_mapInstallations.end())
//    {
//        throw sdv::installation::XDuplicateInstall();
//    }
//
//    // Rollback class - automatically reverses the loading.
//    class CRollback
//    {
//    public:
//        ~CRollback()
//        {
//            bool bSuccess = true;
//            for (sdv::core::TModuleID tModuleID : m_vecLoadedModules)
//			{
//				try
//				{
//					bSuccess &= GetModuleControl().ContextUnload(tModuleID, true);
//				}
//				catch (const sdv::XSysExcept&)
//				{
//				}
//				catch (const std::exception&)
//				{
//				}
//			}
//            if (!bSuccess)
//            {
//                SDV_LOG_ERROR("Failed to rollback \"", m_pathInstallRoot, "\".");
//                return;
//            }
//
//            if (!m_pathInstallRoot.empty())
//            {
//                try
//                {
//                    std::filesystem::remove_all(m_pathInstallRoot);
//                }
//                catch (const std::filesystem::filesystem_error& rexcept)
//                {
//                    SDV_LOG_ERROR("Failed to rollback \"", m_pathInstallRoot, "\": ", rexcept.what());
//                }
//            }
//        }
//        void Commit()
//        {
//            // Do not roll back.
//            m_pathInstallRoot.clear();
//            m_vecLoadedModules.clear();
//        }
//        void SetInstallPath(const std::filesystem::path& rPathInstallDir) { m_pathInstallRoot = rPathInstallDir; }
//        void AddLoadedModule(sdv::core::TModuleID tModuleID) { m_vecLoadedModules.push_back(tModuleID); }
//    private:
//        std::filesystem::path               m_pathInstallRoot;      ///< Installation root directory
//        std::vector<sdv::core::TModuleID>   m_vecLoadedModules;     ///< loaded modules
//    } rollback;
//
//    std::filesystem::path pathInstallation = GetAppSettings().GetInstallDir() / static_cast<std::string>(ssInstallName);
//    std::vector<std::filesystem::path> vecComponents;
//    try
//    {
//        // A sub-directory with the name must not already exist, if so, delete it.
//        if (std::filesystem::exists(pathInstallation))
//            std::filesystem::remove_all(pathInstallation);
//
//        // Create a directory for the installation
//        std::filesystem::create_directories(pathInstallation);
//        rollback.SetInstallPath(pathInstallation);
//
//        // Add each file
//        for (const sdv::installation::SFileDesc& rsFileDesc : seqFiles)
//        {
//            // File path
//            std::filesystem::path pathFileDir = pathInstallation;
//            //if (!rsFileDesc.ssRelativeDir.empty())
//            //{
//            //    std::filesystem::path pathTemp = static_cast<std::string>(rsFileDesc.ssRelativeDir);
//            //    if (pathTemp.is_absolute() || !pathTemp.is_relative())
//            //        throw sdv::installation::XIncorrectPath();
//            //    pathFileDir /= pathTemp;
//            //    if (!std::filesystem::exists(pathFileDir))
//            //        std::filesystem::create_directories(pathFileDir);
//            //}
//            std::filesystem::path pathFile = pathFileDir;
//            if (std::filesystem::exists(pathFile))
//                throw sdv::installation::XIncorrectPath();
//
//            // TODO EVE
//            //// Check the file CRC
//            //sdv::crcCRC32C crc;
//            //uint32_t uiCRC = crc.calc_checksum(rsFileDesc.ptrContent.get(), rsFileDesc.ptrContent.size());
//            //if (uiCRC != rsFileDesc.uiCRC32c)
//            //    throw sdv::installation::XIncorrectCRC();
//
//            // Create the file
//            std::ofstream fstream(pathFile, std::ios_base::out | std::ios_base::binary);
//            if (!fstream.is_open()) throw sdv::installation::XIncorrectPath();
//            if (rsFileDesc.ptrContent.size())
//                fstream.write(reinterpret_cast<const char*>(rsFileDesc.ptrContent.get()), rsFileDesc.ptrContent.size());
//            fstream.close();
//
//            // Set file times
//            // Note: use dedicated OS functions. C++11 filesystem library doesn't define a proper way to convert between UNIX time
//            // and the time defined by the C++11 functions. This deficit has been solved in C++20.
//
//#ifdef _WIN32
//            // Windows uses file access for this.
//            HANDLE hFile = CreateFile(pathFile.native().c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE,
//                NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
//            if (hFile == nullptr || hFile == INVALID_HANDLE_VALUE)
//            {
//                SDV_LOG_ERROR("Cannot create files or directories in \"", pathInstallation, "\"");
//                throw sdv::installation::XIncorrectPath();
//            }
//
//            // Set the filetime on the file.
//            // The filetime starts counting from 1st of January 1601. The resolution is 100ns.
//            // The UNIX time starts counting from 1st of January 1970. The resolution is 1s.
//            // Both times ignore leap seconds.
//            // Thus the UNIX time is running 11644473600 seconds behind the filetime and is a factor 10000000 larger.
//            ULARGE_INTEGER sCreateTime{}, sChangeTime{};
//            sCreateTime.QuadPart = (rsFileDesc.uiCreationDate + 11644473600ull) * 10000000ull;
//            FILETIME ftCreateTime{ sCreateTime.LowPart, sChangeTime.HighPart };
//            sChangeTime.QuadPart = (rsFileDesc.uiChangeDate + 11644473600ull) * 10000000ull;
//            FILETIME ftChangeTime{ sChangeTime.LowPart, sChangeTime.HighPart };
//            BOOL bRes = SetFileTime(hFile, &ftCreateTime, nullptr, &ftChangeTime);
//
//            // Close our handle.
//            CloseHandle(hFile);
//
//            if (!bRes)
//            {
//                SDV_LOG_ERROR("Cannot create files or directories in \"", pathInstallation, "\"");
//                throw sdv::installation::XIncorrectPath();
//            }
//#elif defined __unix__
//            // Note: POSIX doesn't define the creation time.
//            utimbuf sTimes{};
//            sTimes.actime = std::time(0);
//            sTimes.modtime = static_cast<time_t>(rsFileDesc.uiChangeDate);
//            if (utime(pathFile.native().c_str(), &sTimes))
//            {
//                SDV_LOG_ERROR("Cannot create files or directories in \"", pathInstallation, "\"");
//                throw sdv::installation::XIncorrectPath();
//            }
//#endif
//
//            // Set attributes
//            if (rsFileDesc.bAttrExecutable)
//                std::filesystem::permissions(pathFile, std::filesystem::perms::owner_exec | std::filesystem::perms::group_exec |
//                    std::filesystem::perms::others_exec, std::filesystem::perm_options::add);
//            if (rsFileDesc.bAttrReadonly)
//                std::filesystem::permissions(pathFile, std::filesystem::perms::owner_write | std::filesystem::perms::group_write |
//                    std::filesystem::perms::others_write, std::filesystem::perm_options::remove);
//
//            // Component paths are stored
//            //if (rsFileDesc.bAttrComponent) vecComponents.push_back(pathFile);
//        }
//    }
//    catch (const std::filesystem::filesystem_error& rexcept)
//    {
//        SDV_LOG_ERROR("Cannot create files or directories in \"", pathInstallation, "\": ", rexcept.what());
//        throw sdv::installation::XIncorrectPath();
//    }
//
//    // TODO: Restriction. Only complex services will be started dynamically and will be added to the configuration. Utilities will
//    // also be installed but will not be started. Devices and basic services will not be available this way.
//
//    // TODO:
//    // For each component, get the manifest using the manifest helper utility.
//    // Note: to get the manifest, the component module needs to be loaded and the GetManifest exported function is called. Loading
//    // a component module causes part of the component to start already and dependencies to be resolved. If this malfunctions, the
//    // component could crash or do other malicious things. OR if on purpose, an application or complex service component, that
//    // normally would run in an isolated environment, gets access to the memory and everything else within the process. If this
//    // module is not isolated from the core, it could access the core and do nasty things. THEREFORE, the manifest helper utility is
//    // started as a component in its own isolated environment and then loading the component. No component code is executed within
//    // the core process.
//    sdv::TObjectPtr ptrManifestUtil = sdv::core::CreateUtility("ManifestHelperUtility");
//    sdv::helper::IModuleManifestHelper* pManifestHelper = ptrManifestUtil.GetInterface<sdv::helper::IModuleManifestHelper>();
//    if (!pManifestHelper)
//    {
//        SDV_LOG_ERROR("Manifest helper utility cannot be loaded.");
//        throw sdv::installation::XComponentNotLoadable();
//    }
//
//    // Get the manifest of all components
//    std::map<std::filesystem::path, sdv::u8string> mapComponentManifests;
//    for (const std::filesystem::path& rpathComponent : vecComponents)
//    {
//        sdv::u8string ssManifest = pManifestHelper->ReadModuleManifest(rpathComponent.generic_u8string());
//        if (!ssManifest.empty())
//            mapComponentManifests[rpathComponent] = ssManifest;
//    }
//
//    // Check whether there ary any manifests
//    if (mapComponentManifests.empty())
//    {
//        SDV_LOG_ERROR("No component in installation", ssInstallName, ".");
//        throw sdv::installation::XNoManifest();
//    }
//
//    // Load the modules
//    bool bSuccess = true;
//    for (const auto& rvtModule : mapComponentManifests)
//    {
//        sdv::core::TModuleID tModuleID = GetModuleControl().ContextLoad(rvtModule.first, rvtModule.second);
//        if (!tModuleID)
//        {
//            bSuccess = false;
//            break;
//        }
//
//        // TODO: What about the dependent modules that were started as well. They are not rolled back...
//        rollback.AddLoadedModule(tModuleID);
//    }
//    if (!bSuccess)
//    {
//        SDV_LOG_ERROR("Manifest helper utility cannot be loaded.");
//        throw sdv::installation::XComponentNotLoadable();
//    }
//
//    // Add the installation to the installation map
//    //SInstallation sInstallation{ seqFiles };
//    //for (sdv::core::)
//    //m_mapInstallations[ssInstallName] = SInstallation{ seqFiles };
//
//    // Commit the changes - prevent rollback
//    rollback.Commit();

    return true;
}

#ifdef _MSC_VER
    #pragma warning(pop)
#endif

bool CAppConfig::Update(/*in*/ const sdv::u8string& ssInstallName, /*in*/ const sdv::sequence<sdv::installation::SFileDesc>& seqFiles)
{
    // TODO: as soon as the configuration is updatable as well, the update uses the same configuration and possibly updates the
    // configuration.
    // Until the config update feature is available, the update can stop and update and start the installation by uninstall and
    // install.

    bool bRet = Uninstall(ssInstallName);
    if (bRet) bRet = Install(ssInstallName, seqFiles);
    return bRet;
}

bool CAppConfig::Uninstall(/*in*/ const sdv::u8string& /*ssInstallName*/)
{
    // Installations can only be done in the main application.
    if (!GetAppSettings().IsMainApplication()) return false;


    return false;
}

sdv::sequence<sdv::u8string> CAppConfig::GetInstallations() const
{
    return sdv::sequence<sdv::u8string>();
}

sdv::sequence<sdv::installation::SFileDesc> CAppConfig::GetInstallationFiles(/*in*/ const sdv::u8string& /*ssInstallName*/) const
{
    return sdv::sequence<sdv::installation::SFileDesc>();
}

std::filesystem::path CAppConfig::FindInstalledModule(const std::filesystem::path& rpathRelModule)
{
    // Check for allowance
    bool bServerApp = false;
    switch (GetAppSettings().GetContextType())
    {
    case sdv::app::EAppContext::main:
    case sdv::app::EAppContext::isolated:
    case sdv::app::EAppContext::external:
    case sdv::app::EAppContext::maintenance:
        bServerApp = true;
    default:
        break;
    }

    // Search for the module in the following order:
    // - Core manifest
    // - Exe manifest
    // - User manifests

    std::filesystem::path pathModule = m_manifestCore.FindModule(rpathRelModule);
    if (pathModule.empty())
        pathModule = m_manifestExe.FindModule(rpathRelModule);
    if (bServerApp)
    {
        for (const CInstallManifest& rmanifest : m_vecUserManifests)
        {
            if (!pathModule.empty())
                break;
            pathModule = rmanifest.FindModule(rpathRelModule);
        }
    }
    return pathModule;
}

std::string CAppConfig::FindInstalledModuleManifest(const std::filesystem::path& rpathRelModule)
{
    // Check for allowance
    bool bServerApp = false;
    switch (GetAppSettings().GetContextType())
    {
    case sdv::app::EAppContext::main:
    case sdv::app::EAppContext::isolated:
    case sdv::app::EAppContext::external:
    case sdv::app::EAppContext::maintenance:
        bServerApp = true;
    default:
        break;
    }

    // Search for the module in the following order:
    // - Core manifest
    // - Exe manifest
    // - User manifests

    std::string ssManifest = m_manifestCore.FindModuleManifest(rpathRelModule);
    if (ssManifest.empty())
        ssManifest = m_manifestExe.FindModuleManifest(rpathRelModule);
    if (bServerApp)
    {
        for (const CInstallManifest& rmanifest : m_vecUserManifests)
        {
            if (!ssManifest.empty())
                break;
            ssManifest = rmanifest.FindModuleManifest(rpathRelModule);
        }
    }
    return ssManifest;
}

std::optional<sdv::SClassInfo> CAppConfig::FindInstalledComponent(const std::string& rssClass) const
{
    // Check for allowance
    bool bServerApp = false;
    switch (GetAppSettings().GetContextType())
    {
    case sdv::app::EAppContext::main:
    case sdv::app::EAppContext::isolated:
    case sdv::app::EAppContext::external:
    case sdv::app::EAppContext::maintenance:
        bServerApp = true;
    default:
        break;
    }

    // Search for the component in the following order:
    // - Core manifest
    // - Exe manifest
    // - User manifests
    auto optManifest = m_manifestCore.FindComponentByClass(rssClass);
    if (!optManifest) optManifest = m_manifestExe.FindComponentByClass(rssClass);
    if (bServerApp)
    {
        for (const CInstallManifest& rmanifest : m_vecUserManifests)
        {
            if (optManifest)
                break;
            optManifest = rmanifest.FindComponentByClass(rssClass);
        }
    }
    return optManifest;
}

bool CAppConfig::RemoveFromConfig(const CInstallManifest& /*rManifest*/)
{
    //// Check for allowance
    //bool bServerApp = false;
    //switch (GetAppSettings().GetContextType())
    //{
    //case sdv::app::EAppContext::main:
    //case sdv::app::EAppContext::isolated:
    //case sdv::app::EAppContext::external:
    //case sdv::app::EAppContext::maintenance:
    //    bServerApp = true;
    //default:
    //    break;
    //}

    // auto fnRemoveFromConfig = [&](const std::filesystem::path& /*rpathConfig*/, bool /*bIsSysConfig*/)
    //{
    //    // Iterate through the manifest and search for the component in the configuration.
    //};

    // Get the list of system configurations.
    //auto vecSysConfigs = GetAppSettings().GetSystemConfigPaths();
    return true;
}

std::filesystem::path CAppConfig::ComposeConfigPathStandaloneApp(const std::filesystem::path& rpathConfigFile) const
{
    try
    {
        // Get the search paths if the module path is relative
        std::list<std::filesystem::path> lstSearchPaths;
        if (rpathConfigFile.is_relative())
            lstSearchPaths = m_lstSearchPaths;

        // Add an empty path to allow the OS to search when our own search paths could not find the module.
        lstSearchPaths.push_back(std::filesystem::path());

        // Run through the search paths and try to find the config file.
        for (const std::filesystem::path& rpathDirectory : lstSearchPaths)
        {
            // Compose the path
            std::filesystem::path pathConfigTemp;
            if (rpathDirectory.is_absolute())
                pathConfigTemp = (rpathDirectory / rpathConfigFile).lexically_normal();
            else
            {
                if (rpathDirectory.empty())
                    pathConfigTemp = rpathConfigFile.lexically_normal();
                else
                    pathConfigTemp = (GetExecDirectory() / rpathDirectory / rpathConfigFile).lexically_normal();
            }
            if (std::filesystem::exists(pathConfigTemp))
            {
                return pathConfigTemp;
            }
        }
    }
    catch (const std::exception& re)
    {
        SDV_LOG_ERROR("Supplied path to config load is not valid \"", rpathConfigFile, "\": ", re.what());
    }
    return {};
}

void CAppConfig::AddCurrentPath()
{
    std::unique_lock<std::mutex> lock(m_mtxSearchPaths);

    // Do this only once.
    if (!m_lstSearchPaths.empty()) return;

    // Add the core directory
    std::filesystem::path pathCoreDir = GetAppSettings().GetFrameworkDir().lexically_normal();
    m_lstSearchPaths.push_back(pathCoreDir / "config/");

    // Add the exe dir
    std::filesystem::path pathExeDir = GetExecDirectory().lexically_normal();
    if (pathExeDir != pathCoreDir) m_lstSearchPaths.push_back(pathExeDir / "config/");
}

CAppConfig& CAppConfigService::GetAppConfig()
{
    return ::GetAppConfig();
}

bool CAppConfigService::EnableAppConfigAccess()
{
    return GetAppSettings().IsStandaloneApplication() ||
        GetAppSettings().IsEssentialApplication();
}

bool CAppConfigService::EnableAppInstallAccess()
{
    return GetAppSettings().IsMainApplication();
}
