/********************************************************************************
 * 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_file.h"
#include <support/toml.h>
#include "app_settings.h"
#include "../../global/ipc_named_mutex.h"
#include "toml_parser/parser_toml.h"
#include <support/toml.h>
#include "installation_manifest.h"
#include <set>

CAppConfigFile::CAppConfigFile(const std::filesystem::path& rpathConfigFile)
{
    switch (GetAppSettings().GetContextType())
    {
    case sdv::app::EAppContext::main:
    case sdv::app::EAppContext::isolated:
    case sdv::app::EAppContext::maintenance:
        m_pathConfigFile = GetAppSettings().GetInstallDir() / rpathConfigFile;
        break;
    default:
        m_pathConfigFile = rpathConfigFile;
        break;
    }

    if (!LoadConfigFile())
    {
        SDV_LOG_ERROR("ERROR: failed to load the configuration file: ", rpathConfigFile.generic_u8string());

    }
}

void CAppConfigFile::Clear()
{
    m_pathConfigFile.clear();
    m_bLoaded = false;
    m_vecComponentList.clear();
    m_vecClassList.clear();
    m_vecModuleList.clear();
}

bool CAppConfigFile::IsLoaded() const
{
    return m_bLoaded;
}

const std::filesystem::path& CAppConfigFile::ConfigPath() const
{
    return m_pathConfigFile;
}

bool CAppConfigFile::LoadConfigFile()
{
    if (!m_pathConfigFile.has_filename() || !std::filesystem::exists(m_pathConfigFile.parent_path()))
        return false;
    if (!std::filesystem::exists(m_pathConfigFile))
    {
        m_bLoaded = true;
        return true;
    }

    std::ifstream fstream(m_pathConfigFile);
    std::string ssConfig((std::istreambuf_iterator<char>(fstream)), std::istreambuf_iterator<char>());
    fstream.close();

    return LoadConfigFromString(ssConfig);
}

bool CAppConfigFile::LoadConfigFromString(const std::string& rssConfig)
{
    // Determine whether running in main, isolation or maintenance mode.
    bool bServerApp = false;
    switch (GetAppSettings().GetContextType())
    {
    case sdv::app::EAppContext::main:
    case sdv::app::EAppContext::isolated:
    case sdv::app::EAppContext::maintenance:
        bServerApp = true;
        break;
    default:
        break;
    }

    try
    {
        // Read the configuration
        toml_parser::CParser parserConfig(rssConfig);

        // Check for the version
        sdv::toml::CNodeCollection nodeConfig(&parserConfig.Root());
        uint32_t uiVersion = nodeConfig.GetDirect("Configuration.Version").GetValue();
        if (uiVersion != SDVFrameworkInterfaceVersion)
        {
            SDV_LOG_ERROR("Invalid version of application settings file (expected version ",
                SDVFrameworkInterfaceVersion,
                ", but available version ",
                uiVersion,
                ")");
            return false;
        }

        // Iterate through the module list (if not running as server application).
        if (!bServerApp)
        {
            sdv::toml::CNodeCollection nodeModules = nodeConfig.GetDirect("Module");
            for (size_t nIndex = 0; nIndex < nodeModules.GetCount(); nIndex++)
            {
                sdv::toml::CNodeCollection nodeModule = nodeModules.Get(nIndex);
                SModule sModule;
                sModule.pathModule = nodeModule.GetDirect("Path").GetValueAsPath();
                auto itModule = std::find_if(m_vecModuleList.begin(), m_vecModuleList.end(),
                    [&](const SModule& rsModuleStored) { return rsModuleStored.pathModule == sModule.pathModule; });
                if (itModule == m_vecModuleList.end())
                    m_vecModuleList.push_back(std::move(sModule));
            }
        }

        // Iterate through the class list
        sdv::toml::CNodeCollection arrayClasses = nodeConfig.GetDirect("Class");
        for (size_t nIndex = 0; nIndex < arrayClasses.GetCount(); nIndex++)
        {
            sdv::toml::CNodeCollection tableClass = arrayClasses.Get(nIndex);
            sdv::SClassInfo sClass = TOML2ClassInfo(parserConfig.Root().Cast<toml_parser::CNodeCollection>(), nIndex);
            if (sClass.ssName.empty() || sClass.eType == sdv::EObjectType::undefined) continue;
            auto itClass = std::find_if(m_vecClassList.begin(), m_vecClassList.end(),
                [&](const sdv::SClassInfo& rsClassStored) { return rsClassStored.ssName == sClass.ssName; });
            if (itClass == m_vecClassList.end())
                m_vecClassList.push_back(std::move(sClass));
        }

        // Iterate through the component list
        sdv::toml::CNodeCollection nodeComponents = nodeConfig.GetDirect("Component");
        for (size_t nIndex = 0; nIndex < nodeComponents.GetCount(); nIndex++)
        {
            sdv::toml::CNodeCollection tableComponent = nodeComponents.Get(nIndex);
            SComponent sComponent;
            if (!bServerApp)
                sComponent.pathModule = tableComponent.GetDirect("Path").GetValue().get<std::string>();
            sComponent.ssClassName = tableComponent.GetDirect("Class").GetValue().get<std::string>();
            if (sComponent.ssClassName.empty())
            {
                SDV_LOG_ERROR("Missing class name for the class definition in the configuration file '", m_pathConfigFile, "'.");
                return false;
            }
            sComponent.ssInstanceName = tableComponent.GetDirect("Name").GetValue().get<std::string>();
            // NOTE: The name could be empty. The system will automatically select a name.
            sdv::toml::CNodeCollection tableParams = tableComponent.GetDirect("Parameters");
            if (tableParams.IsValid() && tableParams.GetCount())
                sComponent.ssParameterTOML = tableParams.GetTOML();
            auto itComponent = std::find_if(m_vecComponentList.begin(), m_vecComponentList.end(),
                [&](const SComponent& rsComponentStored) { return rsComponentStored.ssClassName == sComponent.ssClassName &&
                rsComponentStored.ssInstanceName == sComponent.ssInstanceName; });
            if (itComponent == m_vecComponentList.end())
                m_vecComponentList.push_back(std::move(sComponent));
        }
    }
    catch (const sdv::toml::XTOMLParseException& rexcept)
    {
        SDV_LOG_ERROR("Failed to parse application configuration '", m_pathConfigFile.generic_u8string(), "': ", rexcept.what());
        return false;
    }

    m_bLoaded = true;

    return true;
}

bool CAppConfigFile::SaveConfigFile(const std::filesystem::path& rpathTarget /*= std::filesystem::path()*/,
    bool bSafeParamsOnly /*= false*/) const
{
    // Protect against multiple write actions at the same time.
    std::string ssMutexName = "LockSdvConfigFile_" + std::to_string(GetAppSettings().GetInstanceID()) + "_" +
        rpathTarget.filename().generic_u8string();
    ipc::named_mutex mtx(ssMutexName);
    // Warning of cppcheck for locking a local mutex, which doesn't have any effect. Since this is a named mutex between
    // applciations, the warning is not correct. Suppress warning.
    // cppcheck-suppress localMutex
    std::unique_lock<ipc::named_mutex> lock(mtx);

    std::filesystem::path pathTargetCopy = rpathTarget;
    if (pathTargetCopy.empty()) pathTargetCopy = m_pathConfigFile;
    if (pathTargetCopy.empty()) return false;
    if (!std::filesystem::exists(pathTargetCopy.parent_path())) return false;

    // If the configuration is not existing, create a default configuration file...
    std::string ssConfig;
    if (std::filesystem::exists(pathTargetCopy))
    {
        // Open the existing settings file
        std::ifstream fstream(m_pathConfigFile);
        if (!fstream.is_open())
        {
            if (!GetAppSettings().IsConsoleSilent())
                std::cerr << "ERROR: Cannot open the application configuration file." << std::endl;
            return false;
        }

        // Read the config file
        ssConfig = std::string((std::istreambuf_iterator<char>(fstream)), std::istreambuf_iterator<char>());
        if (ssConfig.empty())
        {
            if (!GetAppSettings().IsConsoleSilent())
                std::cerr << "ERROR: Cannot read the application settings file; will use default." << std::endl;
        }
    }
    bool bChanged = false;
    if (!UpdateConfigString(ssConfig, bChanged, bSafeParamsOnly)) return false;
    if (!bChanged) return true; // No change needed

    std::ofstream fstream(pathTargetCopy, std::ios::trunc);
    if (!fstream.is_open())
    {
        SDV_LOG_ERROR("Cannot write the application settings file '", m_pathConfigFile.generic_u8string(), "'.");
        return false;
    }
    fstream << ssConfig;

    return true;
}

bool CAppConfigFile::UpdateConfigString(std::string& rssContent, bool& rbChanged, bool /*bSaveParamsOnly*/ /*= false*/) const
{
    // Reset change flag
    rbChanged = false;

    // Determine whether running in main, isolation or maintenance mode.
    bool bServerApp = false;
    switch (GetAppSettings().GetContextType())
    {
    case sdv::app::EAppContext::main:
    case sdv::app::EAppContext::isolated:
    case sdv::app::EAppContext::maintenance:
        bServerApp = true;
        break;
    default:
        break;
    }

    // If the configuration is not existing, create a default configuration file...
    std::string ssConfig = rssContent;
    if (ssConfig.empty())
    {
        ssConfig = R"toml(# Configuration file
[Configuration]
Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml(

# The configuration consists of several sections:
#  - Modules          Modules can contain component classes, which are only made accessible, but not automatically loaded. This
#                     section applies to standalone applications only. Server applications use installations to provide accessible
#                     component classes.
#  - Classes          The classes that are present in a module and contain specific class information.
#  - Components       The components that are started when loading the configuration.
#
#
# The modules specify the location of the modules relative to the executable or with an absolute path. They are only available for
# standalone applications and are ignored by server based application, who install the modules rather than provide them in a
# configuration. A module has the following attribute:
#  - Path             [Compulsory] Identifying the module path of components which should be made accessible. The path can be
#                     relative to the application executable (or relocation path) or can be an absolute path to the module in the
#                     system.
#
# Example:
#    [[Module]]
#    Path = "my_module.sdv"
#
#
# Classes are used to specify default parameter for a component instance. These parameter can be overridden by the component instance
# when specifying the parameter with identical name in the component section. The class has the following attributes:
#  - Path             [Optional] Identifying the module path of the component. Only used for standalone applications. If not
#                     specified the component class must be known in the system (e.g. through the module section or the component
#                     section).
#  - Class            [Compulsory] Name of the class that has the default pateters.
#  - Parameters       [Optional] Table containing component instance specific parameters which are default parameters of all
#                     instances of the components with of this class.
# Example:
#    [[Class]]
#    Path = "my_module.sdv"
#    Class = "MyComponent"
#    [Class.Parameters]
#    AttributeA = "default_blep"                     
#    AttributeB = 1234
#    [Component.Parameters.GroupC]
#    AttributeC1 = 4567.7890
#    AttributeC2 = "This is a string"
#
#
# Components are class instances that are instantiated during the loading of the configuration. They are stored as table array
# and contain as a minimum a class name. They can have instance specific parameters, which are stored as a table within the
# component. Components are instantiated in the order of appearance. A component can have the following attributes:
#  - Path             [Optional] Identifying the module path of the component. Only used for standalone applications. If not
#                     specified the component class must be known in the system (e.g. through the module section or the class
#                     section).
#  - Class            [Compulsory] Name of the class that has to be instantiated.
#  - Name             [Optional] Name of the component instance. If not available, the class name determines the component name.
#  - Parameters       [Optional] Table containing the component instance specific parameters. They override default component class
#                     parameters.
#
# Example:
#    [[Component]]
#    Path = "my_module.sdv"
#    Class = "MyComponent"
#    Name = "MyPersonalComponent"
#    [Component.Parameters]
#    AttributeA = "blep"                     
#    AttributeB = 123
#    [Component.Parameters.GroupC]
#    AttributeC1 = 456.789
#    AttributeC2 = "This is a text"
#

)toml";
        rbChanged = true;
    }

    try
    {
        // Read the configuration
        toml_parser::CParser parserConfig(ssConfig);

        // Check for the version
        sdv::toml::CNodeCollection nodeConfig(&parserConfig.Root());
        uint32_t uiVersion = nodeConfig.GetDirect("Configuration.Version").GetValue();
        if (uiVersion != SDVFrameworkInterfaceVersion)
        {
            SDV_LOG_ERROR("Invalid version of application settings file (expected version ", SDVFrameworkInterfaceVersion,
                ", but available version ", uiVersion, ")");
            return false;
        }

        // Iterate and update through the module list (if not running as server application).
        if (!bServerApp)
        {
            // Remove modules not in the list any more.
            // Modules both in the vector and in the list are removed from the vector
            // Modules leftover in the vector are added to the list
            auto vecModuleListCopy = m_vecModuleList;
            sdv::toml::CNodeCollection nodeModules = nodeConfig.GetDirect("Module");
            for (int64_t iIndex = static_cast<int64_t>(nodeModules.GetCount() - 1); iIndex >= 0; --iIndex)
            {
                sdv::toml::CNodeCollection tableModule = nodeModules.Get(static_cast<size_t>(iIndex));
                std::filesystem::path pathModule = tableModule.GetDirect("Path").GetValue().get<std::string>();
                auto itModule = std::find_if(vecModuleListCopy.begin(), vecModuleListCopy.end(),
                    [&](const SModule& rsModule) { return pathModule == rsModule.pathModule; });
                if (itModule == vecModuleListCopy.end())
                {
                    tableModule.Delete();
                    rbChanged = true;
                }
                else
                    vecModuleListCopy.erase(itModule);
            }
            for (const SModule& rsModule : vecModuleListCopy)
            {
                sdv::toml::CNodeCollection tableModule = nodeConfig.AddTableArray("Module");
                tableModule.AddValue("Path", rsModule.pathModule);
                rbChanged = true;
            }
        }

        // Iterate through and update the class list
        // Remove classes not in the list any more.
        // Classes both in the vector and in the list are removed from the vector
        // Classes leftover in the vector are added to the list
        auto vecClassListCopy = m_vecClassList;
        sdv::toml::CNodeCollection arrayClasses = nodeConfig.GetDirect("Class");
        for (int64_t iIndex = static_cast<int64_t>(arrayClasses.GetCount() - 1); iIndex >= 0; --iIndex)
        {
            sdv::toml::CNodeCollection tableClass = arrayClasses.Get(static_cast<size_t>(iIndex));
            std::filesystem::path pathModule;
            if (!bServerApp)
                pathModule = tableClass.GetDirect("Path").GetValue().get<std::string>();
            std::string ssClassName = tableClass.GetDirect("Class").GetValue().get<std::string>();
            auto itClass = std::find_if(vecClassListCopy.begin(),
                vecClassListCopy.end(),
                [&](const sdv::SClassInfo& rsClass) { return ssClassName == rsClass.ssName; });
            if (itClass == vecClassListCopy.end())
            {
                tableClass.Delete();
                rbChanged = true;
            }
            else
            {
                // Update of path
                if (!bServerApp)
                {
                    if (pathModule.generic_u8string() != itClass->ssModulePath)
                    {
                        pathModule = std::filesystem::u8path(static_cast<std::string>(itClass->ssModulePath));
                        sdv::toml::CNode nodePath = tableClass.GetDirect("Path");
                        if (!pathModule.empty())
                        {
                            if (nodePath)
                                nodePath.SetValue(pathModule);
                            else
                                tableClass.AddValue("Path", pathModule);
                        }
                        else if (nodePath)
                            nodePath.Delete();
                        rbChanged = true;
                    }
                }

                // Update the parameters - class parameters are default parameters provided by the object. Overwrite existing
                // default parameters.
                sdv::toml::CNodeCollection tableParams = tableClass.GetDirect("Parameters");
                sdv::u8string ssExistingTOML;
                if (tableParams) ssExistingTOML = tableParams.GetTOML();
                if (ssExistingTOML != itClass->ssDefaultConfig)
                {
                    if (tableParams) tableParams.Delete();
                    if (!itClass->ssDefaultConfig.empty()) tableClass.InsertTOML("", itClass->ssDefaultConfig);
                    rbChanged = true;
                }

                vecClassListCopy.erase(itClass);
            }
        }
        for (const sdv::SClassInfo& rsClass : vecClassListCopy)
        {
            sdv::toml::CNodeCollection tableClass = nodeConfig.AddTableArray("Class");
            tableClass.AddValue("Class", rsClass.ssName);
            if (!bServerApp)
                tableClass.AddValue("Path", std::filesystem::u8path(static_cast<std::string>(rsClass.ssModulePath)));
            if (!rsClass.ssDefaultConfig.empty()) tableClass.InsertTOML("", rsClass.ssDefaultConfig);
            rbChanged = true;
        }

        // Iterate through and update the component list
        // Remove components not in the list any more.
        // Components both in the vector and in the list are removed from the vector
        // Components leftover in the vector are added to the list
        auto vecComponentListCopy = m_vecComponentList;
        sdv::toml::CNodeCollection nodeComponents = nodeConfig.GetDirect("Component");
        for (int64_t iIndex = static_cast<int64_t>(nodeComponents.GetCount() - 1); iIndex >= 0; --iIndex)
        {
            sdv::toml::CNodeCollection tableComponent = nodeComponents.Get(static_cast<size_t>(iIndex));
            std::filesystem::path pathModule;
            if (!bServerApp)
                pathModule = tableComponent.GetDirect("Path").GetValue().get<std::string>();
            std::string ssClassName = tableComponent.GetDirect("Class").GetValue().get<std::string>();
            std::string ssInstanceName = tableComponent.GetDirect("Name").GetValue().get<std::string>();

            auto itComponent = std::find_if(vecComponentListCopy.begin(), vecComponentListCopy.end(),
                [&](const SComponent& rsComponent)
                {
                    return ssInstanceName.empty() ? (ssClassName == rsComponent.ssClassName && rsComponent.ssInstanceName.empty()) :
                        ssInstanceName == rsComponent.ssInstanceName;
                });
            if (itComponent == vecComponentListCopy.end())
            {
                // The class and the instance names must be identical
                tableComponent.Delete();
                rbChanged = true;
            }
            else
            {
                // Update of path
                if (!bServerApp)
                {
                    if (pathModule != itComponent->pathModule)
                    {
                        pathModule = itComponent->pathModule;
                        sdv::toml::CNode nodePath = tableComponent.GetDirect("Path");
                        if (!pathModule.empty())
                        {
                            if (nodePath)
                                nodePath.SetValue(pathModule);
                            else
                                tableComponent.AddValue("Path", pathModule);
                        }
                        else if (nodePath)
                            nodePath.Delete();
                        rbChanged = true;
                    }
                }

                // Update the parameters - component parameters should be updated, not simply overwritten. Use the combine function.
                sdv::toml::CNodeCollection tableParams = tableComponent.GetDirect("Parameters");
                sdv::u8string ssExistingTOML;
                if (tableParams) ssExistingTOML = tableParams.GetTOML();
                if (!itComponent->ssParameterTOML.empty())
                {
                    // Update needed?
                    if (tableParams && ssExistingTOML != itComponent->ssParameterTOML)
                    {
                        // Get the underlying node collection
                        toml_parser::CTable* pComponent =
                            dynamic_cast<toml_parser::CTable*>(static_cast<sdv::IInterfaceAccess*>(tableParams.GetInterface()));
                        if (!pComponent)
                        {
                            SDV_LOG_ERROR("Failure to get access to an internal interface within the TOML parser.");
                            return false;
                        }
                        try
                        {
                            toml_parser::CParser parserParams(itComponent->ssParameterTOML);
                            pComponent->Combine(parserParams.Root().Cast<toml_parser::CNodeCollection>());
                        }
                        catch (const toml_parser::XTOMLParseException& /*rexcept*/)
                        {
                            SDV_LOG_WARNING("The parameter TOML cannot be interpreted; no saving possible.");
                        }
                        rbChanged = true;
                    }
                    else if (!tableParams)
                    {
                        // Simply add the parameters
                        tableParams = tableComponent.InsertTable("", "Parameters");
                        tableParams.InsertTOML("", itComponent->ssParameterTOML);
                        rbChanged = true;
                    }
                }
                vecComponentListCopy.erase(itComponent);
            }
        }
        for (const SComponent& rsComponent : vecComponentListCopy)
        {
            sdv::toml::CNodeCollection tableComponent = nodeConfig.AddTableArray("Component");
            if (!bServerApp && !rsComponent.pathModule.empty())
                tableComponent.AddValue("Path", rsComponent.pathModule);
            tableComponent.AddValue("Class", rsComponent.ssClassName);
            if (!rsComponent.ssInstanceName.empty())
                tableComponent.AddValue("Name", rsComponent.ssInstanceName);
            if (!rsComponent.ssParameterTOML.empty())
            {
                sdv::toml::CNodeCollection tableParams = tableComponent.InsertTable("", "Parameters");
                tableParams.InsertTOML("", rsComponent.ssParameterTOML);
            }
            rbChanged = true;
        }

        // Save the configuration file if needed
        if (rbChanged)
        {
            rssContent = parserConfig.GenerateTOML();
            rbChanged  = true;
        }
    }
    catch (const sdv::toml::XTOMLParseException& rexcept)
    {
        SDV_LOG_ERROR("Failed to parse configuration: ", rexcept.what());
        return false;
    }
    return true;
}

const std::vector<CAppConfigFile::SComponent>& CAppConfigFile::GetComponentList() const
{
    return m_vecComponentList;
}

const std::vector<sdv::SClassInfo>& CAppConfigFile::GetClassList() const
{
    return m_vecClassList;
}

const std::vector<CAppConfigFile::SModule>& CAppConfigFile::GetModuleList() const
{
    return m_vecModuleList;
}

bool CAppConfigFile::InsertComponent(size_t nIndex, const std::filesystem::path& rpathModule, const std::string& rssClassName,
    const std::string& rssInstanceName, const TParameterVector& rvecParameters)
{
    // Valid parameter?
    if (rssClassName.empty()) return false;

    // Check for a duplicate instance name. If there is no instance name, check for a component with the same class name without
    // the instance name. This is not an error!
    if (!rssInstanceName.empty())
    {
        if (std::find_if(m_vecComponentList.begin(), m_vecComponentList.end(), [&](const SComponent& rsComponent)
            { return rsComponent.ssInstanceName == rssInstanceName; }) != m_vecComponentList.end())
            return true;
    }
    else
    {
        if (std::find_if(m_vecComponentList.begin(), m_vecComponentList.end(), [&](const SComponent& rsComponent)
            { return rsComponent.ssInstanceName.empty() && rsComponent.ssClassName == rssClassName; }) != m_vecComponentList.end())
            return true;
    }

    // Check for the module to already be added to the module list. Remove it from the list if it is.
    if (!rpathModule.empty())
    {
        auto itModule = std::find_if(m_vecModuleList.begin(), m_vecModuleList.end(), [&](const SModule& rsModule)
            { return rsModule.pathModule == rpathModule; });
        if (itModule != m_vecModuleList.end()) m_vecModuleList.erase(itModule);
    }

    // Create a parameter TOML
    std::string ssParameterTOML;
    try
    {
        // Create a TOML string
        toml_parser::CParser parser("");
        sdv::toml::CNodeCollection root(&parser.Root());
        
        // Split the key in a group and a parameter name
        auto fnSplit = [](const std::string& rssKey) -> std::pair<std::string, std::string>
        {
            size_t nSeparator = rssKey.find_last_of('.');
            if (nSeparator == std::string::npos) return std::make_pair("", rssKey);
            return std::make_pair(rssKey.substr(0, nSeparator), rssKey.substr(nSeparator + 1));
        };

        // Iterate through the parameters
        std::string ssGroup;
        sdv::toml::CNodeCollection group(root);
        for (const auto& prParameter : rvecParameters)
        {
            // Split the key in a group and parameter name.
            auto prKey = fnSplit(prParameter.first);

            // Need to add a group?
            if (prKey.first != ssGroup)
            {
                group = root.InsertTable("", prKey.first);
                ssGroup = prKey.first;
            }

            // Add the parameter
            group.InsertValue("", prKey.second, prParameter.second);
        }
        ssParameterTOML = parser.GenerateTOML();
    }
    catch (const toml_parser::XTOMLParseException& /*rexcept*/)
    {
        return false;
    }

    // Add the instance
    SComponent sComponent;
    sComponent.pathModule = rpathModule;
    sComponent.ssClassName = rssClassName;
    sComponent.ssInstanceName = rssInstanceName;
    sComponent.ssParameterTOML = std::move(ssParameterTOML);
    m_vecComponentList.insert((nIndex < m_vecComponentList.size() ? m_vecComponentList.begin() + nIndex : m_vecComponentList.end()),
        std::move(sComponent));

    return true;
}

void CAppConfigFile::RemoveComponent(const std::string& rssInstanceName)
{
    // Find and destroy
    auto itComponent = std::find_if(m_vecComponentList.begin(),
        m_vecComponentList.end(),
        [&](const SComponent& rsComponent) { return rsComponent.ssInstanceName == rssInstanceName; });
    if (itComponent != m_vecComponentList.end())
        m_vecComponentList.erase(itComponent);
}

bool CAppConfigFile::InsertModule(size_t nIndex, const std::filesystem::path& rpathModule)
{
    // Check for the proper context; only allowed when running as standalone (not main, isolated or maintenace).
    switch (GetAppSettings().GetContextType())
    {
    case sdv::app::EAppContext::main:
    case sdv::app::EAppContext::isolated:
    case sdv::app::EAppContext::maintenance:
        return true;
    default:
        break;
    }

    if (rpathModule.empty())
        return false;

    // Check for the module to exist already in either the component or in the module list
    if (std::find_if(m_vecComponentList.begin(),
            m_vecComponentList.end(),
            [&](const SComponent& rsComponent) { return rsComponent.pathModule == rpathModule; })
        != m_vecComponentList.end())
        return true;
    if (std::find_if(m_vecModuleList.begin(),
            m_vecModuleList.end(),
            [&](const SModule& rsModule) { return rsModule.pathModule == rpathModule; })
        != m_vecModuleList.end())
        return true;

    // Add the module to the module list
    SModule sModule;
    sModule.pathModule = rpathModule;
    m_vecModuleList.insert(
        (nIndex < m_vecModuleList.size() ? m_vecModuleList.begin() + nIndex : m_vecModuleList.end()), std::move(sModule));

    return true;
}

void CAppConfigFile::RemoveModule(const std::filesystem::path& rpathModule)
{
    // Check for the proper context; only allowed when running as standalone (not main, isolated or maintenace).
    switch (GetAppSettings().GetContextType())
    {
    case sdv::app::EAppContext::main:
    case sdv::app::EAppContext::isolated:
    case sdv::app::EAppContext::maintenance:
        return;
    default:
        break;
    }

    // Find and destroy (any module and component with the provided path).
    auto itModule = std::find_if(m_vecModuleList.begin(),
        m_vecModuleList.end(),
        [&](const SModule& rsModule) { return rsModule.pathModule == rpathModule; });
    if (itModule != m_vecModuleList.end())
        m_vecModuleList.erase(itModule);
    while (true)
    {
        auto itComponent = std::find_if(m_vecComponentList.begin(),
            m_vecComponentList.end(),
            [&](const SComponent& rsComponent) { return rsComponent.pathModule == rpathModule; });
        if (itComponent == m_vecComponentList.end())
            break;
        m_vecComponentList.erase(itComponent);
    }
}

CAppConfigFile::EMergeResult CAppConfigFile::MergeConfigFile(const std::filesystem::path& rpathConfigFile)
{
    CAppConfigFile configNew(rpathConfigFile);
    if (!configNew.LoadConfigFile()) return EMergeResult::not_successful;

    size_t nSucceeded = 0;
    size_t nFailed = 0;

    // Add modules
    auto vecNewModules = configNew.GetModuleList();
    for (const auto& sNewModule : vecNewModules)
    {
        if (std::find_if(m_vecModuleList.begin(), m_vecModuleList.end(), [&sNewModule](const SModule& rsExistingModule)
            { return sNewModule.pathModule == rsExistingModule.pathModule; }) == m_vecModuleList.end())
            m_vecModuleList.push_back(sNewModule);
        ++nSucceeded;
    }

    // Add/update classes
    auto vecNewClasses = configNew.GetClassList();
    for (const auto& sNewClass : vecNewClasses)
    {
        auto itClass = std::find_if(m_vecClassList.begin(), m_vecClassList.end(),
            [&sNewClass](const sdv::SClassInfo& rsExistingClass)
            { return sNewClass.ssName == rsExistingClass.ssName; });
        if (itClass == m_vecClassList.end())
            m_vecClassList.push_back(sNewClass);
        else
            *itClass = sNewClass;
        ++nSucceeded;
    }

    // Merge components
    auto vecNewComponents = configNew.GetComponentList();
    for (const auto& sNewComponent : vecNewComponents)
    {
        auto itComponent = std::find_if(m_vecComponentList.begin(), m_vecComponentList.end(),
            [&sNewComponent](const SComponent& rsExistingComponent)
            {
                if (sNewComponent.ssInstanceName.empty())
                {
                    if (!rsExistingComponent.ssInstanceName.empty()) return false;
                    return sNewComponent.ssClassName == rsExistingComponent.ssClassName;
                }
                else
                    return sNewComponent.ssInstanceName == rsExistingComponent.ssInstanceName;
            });
        if (itComponent == m_vecComponentList.end())
            m_vecComponentList.push_back(sNewComponent);
        else
        {
            // Only update component information if the components are of identical type
            if (itComponent->ssClassName != sNewComponent.ssClassName)
            {
                ++nFailed;
                continue;
            }
            
            // TODO: Add additional possibilty to replace the parameters
            
            // Merge the parameters of the existing component.
            if (!sNewComponent.ssParameterTOML.empty())
            {
                if (itComponent->ssParameterTOML.empty())
                    itComponent->ssParameterTOML = sNewComponent.ssParameterTOML;
                else
                    try
                    {
                        toml_parser::CParser parserNew(itComponent->ssParameterTOML), parserExisting(sNewComponent.ssParameterTOML);
                        parserExisting.Root().Combine(parserNew.Root().Cast<toml_parser::CNodeCollection>());
                        itComponent->ssParameterTOML = parserExisting.GenerateTOML();
                    }
                    catch (const toml_parser::XTOMLParseException&)
                    {
                        ++nFailed;
                        continue;
                    }
            }
            ++nSucceeded;
        }
    }
    return nFailed ? (nSucceeded ? EMergeResult::partly_successfull : EMergeResult::not_successful) : EMergeResult::successful;
}
