/*
 * SPDX-FileCopyrightText: 2025 Copyright contributors to the cppTango project
 *
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */

#include <tango/server/DServer.h>

#include <tango/internal/parse.h>

#include <tango/server/Util.h>

#ifdef TANGO_USE_TELEMETRY

  #include <tango/common/telemetry/Configuration.h>

namespace
{

std::vector<Tango::DeviceImpl *> get_device_list(std::string &pattern, std::vector<std::string> &notFound)
{
    std::transform(pattern.begin(), pattern.end(), pattern.begin(), ::tolower);
    std::vector<Tango::DeviceImpl *> deviceList = Tango::Util::instance()->get_device_list(pattern);
    if(deviceList.empty())
    {
        notFound.push_back(pattern);
    }
    return deviceList;
}

std::string device_not_found_error(const std::vector<std::string> &notFound)
{
    TangoSys_OMemStream o;
    o << "Error: No device name matching patterns:";
    for(const auto &pattern : notFound)
    {
        o << " <" << pattern << ">";
    }
    return o.str();
}

std::string device_locked_error(const std::vector<std::string> &lockedList)
{
    TangoSys_OMemStream o;
    o << "Error: The following devices were locked:";
    for(const auto &device : lockedList)
    {
        o << " <" << device << ">";
    }
    return o.str();
}

bool get_errors(std::string &error,
                std::string &errMsg,
                const std::vector<std::string> &notFound,
                const std::vector<std::string> &lockedList = {})
{
    if(!notFound.empty())
    {
        errMsg = device_not_found_error(notFound);
        error = Tango::API_DeviceNotFound;
    }
    if(!lockedList.empty())
    {
        if(errMsg != "")
        {
            errMsg += "\n" + device_locked_error(lockedList);
            error = "API_TelemetryConfig";
        }
        else
        {
            errMsg = device_locked_error(lockedList);
            error = Tango::API_DeviceLocked;
        }
    }
    return !notFound.empty() || !lockedList.empty();
}

} // namespace

namespace Tango
{

void DServer::start_telemetry(const Tango::DevVarStringArray *argin)
{
    try
    {
        std::string pattern;
        std::vector<DeviceImpl *> deviceList;
        std::vector<std::string> notFound;
        std::vector<std::string> lockedList;

        for(unsigned int i = 0; i < argin->length(); ++i)
        {
            pattern = (*argin)[i];
            deviceList = get_device_list(pattern, notFound);
            DServer *adm_dev = Util::instance()->get_dserver_device();
            for(const auto &device : deviceList)
            {
                try
                {
                    adm_dev->check_lock_owner(device, "start_telemetry", (device->get_name()).c_str());
                    device->enable_telemetry();
                }
                catch(Tango::DevFailed &e)
                {
                    if(::strcmp(e.errors[0].reason, Tango::API_DeviceLocked) == 0)
                    {
                        lockedList.emplace_back((device->get_name()).c_str());
                    }
                    else
                    {
                        TANGO_RETHROW_EXCEPTION(e, "API_TelemetryError", "Unexpected telemetry error!");
                    }
                }
            }
        }
        // Error reporting
        std::string error, errMsg;
        if(get_errors(error, errMsg, notFound, lockedList))
        {
            TANGO_THROW_EXCEPTION(error.c_str(), errMsg.c_str());
        }
    }
    catch(std::exception &e)
    {
        TangoSys_OMemStream o;
        o << "std::exception caught [" << e.what() << "]" << std::ends;
        TANGO_THROW_EXCEPTION(API_InternalError, o.str());
    }
}

void DServer::stop_telemetry(const Tango::DevVarStringArray *argin)
{
    try
    {
        std::string pattern;
        std::vector<DeviceImpl *> deviceList;
        std::vector<std::string> notFound;
        std::vector<std::string> lockedList;

        for(unsigned int i = 0; i < argin->length(); ++i)
        {
            pattern = (*argin)[i];
            deviceList = get_device_list(pattern, notFound);
            DServer *adm_dev = Util::instance()->get_dserver_device();
            for(const auto &device : deviceList)
            {
                try
                {
                    adm_dev->check_lock_owner(device, "stop_telemetry", (device->get_name()).c_str());
                    device->disable_telemetry();
                }
                catch(Tango::DevFailed &e)
                {
                    if(::strcmp(e.errors[0].reason, Tango::API_DeviceLocked) == 0)
                    {
                        lockedList.emplace_back((device->get_name()).c_str());
                    }
                    else
                    {
                        TANGO_RETHROW_EXCEPTION(e, "API_TelemetryError", "Unexpected telemetry error!");
                    }
                }
            }
        }
        // Error reporting
        std::string error, errMsg;
        if(get_errors(error, errMsg, notFound, lockedList))
        {
            TANGO_THROW_EXCEPTION(error.c_str(), errMsg.c_str());
        }
    }
    catch(std::exception &e)
    {
        TangoSys_OMemStream o;
        o << "std::exception caught [" << e.what() << "]" << std::ends;
        TANGO_THROW_EXCEPTION(API_InternalError, o.str());
    }
}

DevVarLongStringArray *DServer::is_telemetry_enabled_ds(const Tango::DevVarStringArray *argin)
{
    DevVarLongStringArray *result = nullptr;
    try
    {
        result = new DevVarLongStringArray;
        unsigned int N_devices = 0;

        std::string pattern;
        std::vector<DeviceImpl *> deviceList;
        std::vector<std::string> notFound;
        for(unsigned int i = 0; i < argin->length(); ++i)
        {
            pattern = (*argin)[i];
            deviceList = get_device_list(pattern, notFound);
            for(const auto &device : deviceList)
            {
                ++N_devices;
                result->lvalue.length(N_devices);
                result->svalue.length(N_devices);
                result->lvalue[N_devices - 1] = device->is_telemetry_enabled() ? 1 : 0;
                result->svalue[N_devices - 1] = Tango::string_dup(device->get_name().c_str());
            }
        }
        // Error reporting
        std::string error, errMsg;
        if(get_errors(error, errMsg, notFound))
        {
            if(result != nullptr)
            {
                delete result;
                result = nullptr;
            }
            TANGO_THROW_EXCEPTION(error.c_str(), errMsg.c_str());
        }
    }
    catch(std::exception &e)
    {
        if(result != nullptr)
        {
            delete result;
            result = nullptr;
        }
        TangoSys_OMemStream o;
        o << "std::exception caught [" << e.what() << "]" << std::ends;
        TANGO_THROW_EXCEPTION(API_InternalError, o.str());
    }
    return result;
}

void DServer::set_telemetry_topics_ds(const Tango::DevVarStringArray *argin)
{
    try
    {
        std::string pattern;
        std::string n_str;
        std::vector<DeviceImpl *> deviceList;
        std::vector<std::string> notFound;
        std::vector<std::string> lockedList;

        unsigned int argin_index = 0;
        // Validate arguments
        while(argin_index < argin->length())
        {
            if(argin->length() - argin_index < 2)
            {
                TANGO_THROW_EXCEPTION(API_InvalidArgs, "You must provide a device pattern and number of topics");
            }
            pattern = (*argin)[argin_index++];
            n_str = (*argin)[argin_index++];

            unsigned int n_topics = detail::parse_as<unsigned int>(n_str);
            if(argin->length() - argin_index < n_topics)
            {
                TangoSys_OMemStream o;
                o << "The number of topics entries for the pattern <" << pattern << ">";
                o << " does not match the expected number (" << n_topics << ")";
                TANGO_THROW_EXCEPTION(API_InvalidArgs, o.str());
            }
            // Validate topics
            for(unsigned int j = 0; j < n_topics; ++j)
            {
                telemetry::Configuration::to_topic(std::string((*argin)[argin_index++]));
            }
        }

        argin_index = 0;
        while(argin_index < argin->length())
        {
            if(argin->length() - argin_index < 2)
            {
                TANGO_THROW_EXCEPTION(API_InvalidArgs, "You must provide a device pattern and number of topics");
            }
            pattern = (*argin)[argin_index++];
            n_str = (*argin)[argin_index++];
            unsigned int n_topics = detail::parse_as<unsigned int>(n_str);

            std::vector<std::string> topics;
            for(unsigned int j = 0; j < n_topics; ++j)
            {
                std::string topic{(*argin)[argin_index++]};
                topics.push_back(topic);
            }
            deviceList = get_device_list(pattern, notFound);
            DServer *adm_dev = Util::instance()->get_dserver_device();
            for(const auto &device : deviceList)
            {
                try
                {
                    adm_dev->check_lock_owner(device, "set_telemetry_topics", (device->get_name()).c_str());
                    device->set_telemetry_topics(topics);
                }
                catch(Tango::DevFailed &e)
                {
                    if(::strcmp(e.errors[0].reason, Tango::API_DeviceLocked) == 0)
                    {
                        lockedList.emplace_back((device->get_name()).c_str());
                    }
                    else
                    {
                        TANGO_RETHROW_EXCEPTION(e, "API_TelemetryError", "Unexpected telemetry error!");
                    }
                }
            }
        }
        // Error reporting
        std::string error, errMsg;
        if(get_errors(error, errMsg, notFound, lockedList))
        {
            TANGO_THROW_EXCEPTION(error.c_str(), errMsg.c_str());
        }
    }
    catch(std::exception &e)
    {
        TangoSys_OMemStream o;
        o << "std::exception caught [" << e.what() << "]" << std::ends;
        TANGO_THROW_EXCEPTION(API_InternalError, o.str());
    }
}

DevVarStringArray *DServer::get_telemetry_topics_ds(const Tango::DevVarStringArray *argin)
{
    DevVarStringArray *result = nullptr;
    try
    {
        result = new DevVarStringArray;

        std::string pattern;
        std::vector<DeviceImpl *> deviceList;
        std::vector<std::string> notFound;
        for(unsigned int i = 0; i < argin->length(); ++i)
        {
            pattern = (*argin)[i];
            deviceList = get_device_list(pattern, notFound);
            for(const auto &device : deviceList)
            {
                unsigned int result_size = result->length();
                auto topics = device->get_telemetry_topics();
                result->length(result_size + 2 + topics.size());
                (*result)[result_size++] = Tango::string_dup(device->get_name().c_str());
                (*result)[result_size++] = Tango::string_dup(std::to_string(topics.size()).c_str());
                for(const auto &topic : topics)
                {
                    (*result)[result_size++] = Tango::string_dup(topic.c_str());
                }
            }
        }
        // Error reporting
        std::string error, errMsg;
        if(get_errors(error, errMsg, notFound))
        {
            if(result != nullptr)
            {
                delete result;
                result = nullptr;
            }
            TANGO_THROW_EXCEPTION(error.c_str(), errMsg.c_str());
        }
    }
    catch(std::exception &e)
    {
        if(result != nullptr)
        {
            delete result;
            result = nullptr;
        }
        TangoSys_OMemStream o;
        o << "std::exception caught [" << e.what() << "]" << std::ends;
        TANGO_THROW_EXCEPTION(API_InternalError, o.str());
    }
    return result;
}

void DServer::set_telemetry_tracing_ds(const Tango::DevVarLongStringArray *argin)
{
    if(argin->svalue.length() != argin->lvalue.length())
    {
        TANGO_THROW_EXCEPTION(Tango::API_InvalidArgs, "Input long array and string array must have the same length.");
    }
    try
    {
        std::string pattern;
        bool do_enable;
        std::vector<DeviceImpl *> deviceList;
        std::vector<std::string> notFound;
        std::vector<std::string> lockedList;

        for(unsigned int i = 0; i < argin->svalue.length(); ++i)
        {
            pattern = argin->svalue[i];
            do_enable = argin->lvalue[i] != 0;
            deviceList = get_device_list(pattern, notFound);
            DServer *adm_dev = Util::instance()->get_dserver_device();
            for(const auto &device : deviceList)
            {
                try
                {
                    adm_dev->check_lock_owner(device, "set_telemetry_tracing", (device->get_name()).c_str());
                    device->set_telemetry_tracing(do_enable);
                }
                catch(Tango::DevFailed &e)
                {
                    if(::strcmp(e.errors[0].reason, Tango::API_DeviceLocked) == 0)
                    {
                        lockedList.emplace_back((device->get_name()).c_str());
                    }
                    else
                    {
                        TANGO_RETHROW_EXCEPTION(e, "API_TelemetryError", "Unexpected telemetry error!");
                    }
                }
            }
        }
        // Error reporting
        std::string error, errMsg;
        if(get_errors(error, errMsg, notFound, lockedList))
        {
            TANGO_THROW_EXCEPTION(error.c_str(), errMsg.c_str());
        }
    }
    catch(std::exception &e)
    {
        TangoSys_OMemStream o;
        o << "std::exception caught [" << e.what() << "]" << std::ends;
        TANGO_THROW_EXCEPTION(API_InternalError, o.str());
    }
}

DevVarLongStringArray *DServer::get_telemetry_tracing(const DevVarStringArray *argin)
{
    DevVarLongStringArray *result = nullptr;
    try
    {
        result = new DevVarLongStringArray;
        unsigned int N_devices = 0;

        std::string pattern;
        std::vector<DeviceImpl *> deviceList;
        std::vector<std::string> notFound;
        for(unsigned int i = 0; i < argin->length(); ++i)
        {
            pattern = (*argin)[i];
            deviceList = get_device_list(pattern, notFound);
            for(const auto &device : deviceList)
            {
                ++N_devices;
                result->lvalue.length(N_devices);
                result->svalue.length(N_devices);
                result->lvalue[N_devices - 1] = device->is_telemetry_tracing_enabled() ? 1 : 0;
                result->svalue[N_devices - 1] = Tango::string_dup(device->get_name().c_str());
            }
        }
        // Error reporting
        std::string error, errMsg;
        if(get_errors(error, errMsg, notFound))
        {
            if(result != nullptr)
            {
                delete result;
                result = nullptr;
            }
            TANGO_THROW_EXCEPTION(error.c_str(), errMsg.c_str());
        }
    }
    catch(std::exception &e)
    {
        if(result != nullptr)
        {
            delete result;
            result = nullptr;
        }
        TangoSys_OMemStream o;
        o << "std::exception caught [" << e.what() << "]" << std::ends;
        TANGO_THROW_EXCEPTION(API_InternalError, o.str());
    }
    return result;
}

void DServer::set_telemetry_tracing_endpoints_ds(const DevVarStringArray *argin)
{
    try
    {
        std::string pattern;
        std::string n_str;
        std::vector<DeviceImpl *> deviceList;
        std::vector<std::string> notFound;
        std::vector<std::string> lockedList;

        unsigned int argin_index = 0;
        // Validate arguments
        while(argin_index < argin->length())
        {
            if(argin->length() - argin_index < 2)
            {
                TANGO_THROW_EXCEPTION(API_InvalidArgs, "You must provide a device pattern and number of endpoints");
            }
            pattern = (*argin)[argin_index++];
            n_str = (*argin)[argin_index++];
            unsigned int n_endpoints = detail::parse_as<unsigned int>(n_str);
            if(argin->length() - argin_index < n_endpoints * 2)
            {
                TangoSys_OMemStream o;
                o << "The number of exporter/endpoint entries for the pattern <" << pattern << ">";
                o << " does not match the expected number (" << n_endpoints << ")";
                TANGO_THROW_EXCEPTION(API_InvalidArgs, o.str());
            }
            // Validate endpoints
            for(unsigned int j = 0; j < n_endpoints; ++j)
            {
                std::string exporter{(*argin)[argin_index++]};
                std::string endpoint{(*argin)[argin_index++]};
                telemetry::Configuration::create_collector_from_endpoint(exporter, endpoint);
            }
        }

        // Set the endpoints
        argin_index = 0;
        while(argin_index < argin->length())
        {
            pattern = (*argin)[argin_index++];
            n_str = (*argin)[argin_index++];
            unsigned int n_endpoints = detail::parse_as<unsigned int>(n_str);

            std::vector<telemetry::Configuration::Collector> collectors;
            for(unsigned int j = 0; j < n_endpoints; ++j)
            {
                std::string exporter{(*argin)[argin_index++]};
                std::string endpoint{(*argin)[argin_index++]};
                collectors.push_back(telemetry::Configuration::create_collector_from_endpoint(exporter, endpoint));
            }
            deviceList = get_device_list(pattern, notFound);
            DServer *adm_dev = Util::instance()->get_dserver_device();
            for(const auto &device : deviceList)
            {
                try
                {
                    adm_dev->check_lock_owner(device, "set_telemetry_tracing_endpoints", (device->get_name()).c_str());
                    device->set_telemetry_tracing_endpoints(collectors);
                }
                catch(Tango::DevFailed &e)
                {
                    if(::strcmp(e.errors[0].reason, Tango::API_DeviceLocked) == 0)
                    {
                        lockedList.emplace_back((device->get_name()).c_str());
                    }
                    else
                    {
                        TANGO_RETHROW_EXCEPTION(e, "API_TelemetryError", "Unexpected telemetry error!");
                    }
                }
            }
        }
        // Error reporting
        std::string error, errMsg;
        if(get_errors(error, errMsg, notFound, lockedList))
        {
            TANGO_THROW_EXCEPTION(error.c_str(), errMsg.c_str());
        }
    }
    catch(std::exception &e)
    {
        TangoSys_OMemStream o;
        o << "std::exception caught [" << e.what() << "]" << std::ends;
        TANGO_THROW_EXCEPTION(API_InternalError, o.str());
    }
}

DevVarStringArray *DServer::get_telemetry_tracing_endpoints_ds(const DevVarStringArray *argin)
{
    DevVarStringArray *result = nullptr;
    try
    {
        result = new DevVarStringArray;

        std::string pattern;
        std::vector<DeviceImpl *> deviceList;
        std::vector<std::string> notFound;
        for(unsigned int i = 0; i < argin->length(); ++i)
        {
            pattern = (*argin)[i];
            deviceList = get_device_list(pattern, notFound);
            for(const auto &device : deviceList)
            {
                unsigned int result_size = result->length();
                auto collectors = device->get_telemetry_tracing_endpoints();
                result->length(result_size + 2 + (2 * collectors.size()));
                (*result)[result_size++] = Tango::string_dup(device->get_name().c_str());
                (*result)[result_size++] = Tango::string_dup(std::to_string(collectors.size()).c_str());
                for(const auto &collector : collectors)
                {
                    (*result)[result_size++] = Tango::string_dup(to_string(collector.exporter_type).c_str());
                    (*result)[result_size++] = Tango::string_dup(collector.endpoint);
                }
            }
        }
        // Error reporting
        std::string error, errMsg;
        if(get_errors(error, errMsg, notFound))
        {
            if(result != nullptr)
            {
                delete result;
                result = nullptr;
            }
            TANGO_THROW_EXCEPTION(error.c_str(), errMsg.c_str());
        }
    }
    catch(std::exception &e)
    {
        if(result != nullptr)
        {
            delete result;
            result = nullptr;
        }
        TangoSys_OMemStream o;
        o << "std::exception caught [" << e.what() << "]" << std::ends;
        TANGO_THROW_EXCEPTION(API_InternalError, o.str());
    }
    return result;
}

void DServer::add_telemetry_tracing_endpoints(const DevVarStringArray *argin)
{
    try
    {
        std::string pattern;
        std::vector<DeviceImpl *> deviceList;
        std::vector<std::string> notFound;
        std::vector<std::string> lockedList;

        unsigned int argin_index = 0;
        // Validate arguments
        if(argin->length() % 3 != 0)
        {
            TANGO_THROW_EXCEPTION(API_InvalidArgs,
                                  "There must be 3 arguments per entry: device pattern, exporter, and endpoint.");
        }
        while(argin_index < argin->length())
        {
            pattern = (*argin)[argin_index++];
            // Validate endpoints
            std::string exporter{(*argin)[argin_index++]};
            std::string endpoint{(*argin)[argin_index++]};
            telemetry::Configuration::create_collector_from_endpoint(exporter, endpoint);
        }

        // Add the endpoints
        argin_index = 0;
        while(argin_index < argin->length())
        {
            pattern = (*argin)[argin_index++];
            std::string exporter{(*argin)[argin_index++]};
            std::string endpoint{(*argin)[argin_index++]};
            auto collector = telemetry::Configuration::create_collector_from_endpoint(exporter, endpoint);

            deviceList = get_device_list(pattern, notFound);
            DServer *adm_dev = Util::instance()->get_dserver_device();
            for(const auto &device : deviceList)
            {
                try
                {
                    adm_dev->check_lock_owner(device, "add_telemetry_tracing_endpoints", (device->get_name()).c_str());
                    device->add_telemetry_tracing_endpoint(collector);
                }
                catch(Tango::DevFailed &e)
                {
                    if(::strcmp(e.errors[0].reason, Tango::API_DeviceLocked) == 0)
                    {
                        lockedList.emplace_back((device->get_name()).c_str());
                    }
                    else
                    {
                        TANGO_RETHROW_EXCEPTION(e, "API_TelemetryError", "Unexpected telemetry error!");
                    }
                }
            }
        }
        // Error reporting
        std::string error, errMsg;
        if(get_errors(error, errMsg, notFound, lockedList))
        {
            TANGO_THROW_EXCEPTION(error.c_str(), errMsg.c_str());
        }
    }
    catch(std::exception &e)
    {
        TangoSys_OMemStream o;
        o << "std::exception caught [" << e.what() << "]" << std::ends;
        TANGO_THROW_EXCEPTION(API_InternalError, o.str());
    }
}

void DServer::remove_telemetry_tracing_endpoints(const DevVarStringArray *argin)
{
    try
    {
        std::string pattern;
        std::vector<DeviceImpl *> deviceList;
        std::vector<std::string> notFound;
        std::vector<std::string> lockedList;

        unsigned int argin_index = 0;
        // Validate arguments
        if(argin->length() % 3 != 0)
        {
            TANGO_THROW_EXCEPTION(API_InvalidArgs,
                                  "There must be 3 arguments per entry: device pattern, exporter, and endpoint.");
        }
        while(argin_index < argin->length())
        {
            pattern = (*argin)[argin_index++];
            // Validate endpoints
            std::string exporter{(*argin)[argin_index++]};
            std::string endpoint{(*argin)[argin_index++]};
            telemetry::Configuration::create_collector_from_endpoint(exporter, endpoint);
        }

        // Remove the endpoints
        argin_index = 0;
        while(argin_index < argin->length())
        {
            pattern = (*argin)[argin_index++];
            std::string exporter{(*argin)[argin_index++]};
            std::string endpoint{(*argin)[argin_index++]};
            auto collector = telemetry::Configuration::create_collector_from_endpoint(exporter, endpoint);

            deviceList = get_device_list(pattern, notFound);
            DServer *adm_dev = Util::instance()->get_dserver_device();
            for(const auto &device : deviceList)
            {
                try
                {
                    adm_dev->check_lock_owner(
                        device, "remove_telemetry_tracing_endpoints", (device->get_name()).c_str());
                    device->remove_telemetry_tracing_endpoint(collector);
                }
                catch(Tango::DevFailed &e)
                {
                    if(::strcmp(e.errors[0].reason, Tango::API_DeviceLocked) == 0)
                    {
                        lockedList.emplace_back((device->get_name()).c_str());
                    }
                    else
                    {
                        TANGO_RETHROW_EXCEPTION(e, "API_TelemetryError", "Unexpected telemetry error!");
                    }
                }
            }
        }
        // Error reporting
        std::string error, errMsg;
        if(get_errors(error, errMsg, notFound, lockedList))
        {
            TANGO_THROW_EXCEPTION(error.c_str(), errMsg.c_str());
        }
    }
    catch(std::exception &e)
    {
        TangoSys_OMemStream o;
        o << "std::exception caught [" << e.what() << "]" << std::ends;
        TANGO_THROW_EXCEPTION(API_InternalError, o.str());
    }
}

void DServer::set_telemetry_logging_ds(const Tango::DevVarLongStringArray *argin)
{
    if(argin->svalue.length() != argin->lvalue.length())
    {
        TANGO_THROW_EXCEPTION(Tango::API_InvalidArgs, "Input long array and string array must have the same length.");
    }
    try
    {
        std::string pattern;
        bool do_enable;
        std::vector<DeviceImpl *> deviceList;
        std::vector<std::string> notFound;
        std::vector<std::string> lockedList;

        for(unsigned int i = 0; i < argin->svalue.length(); ++i)
        {
            pattern = argin->svalue[i];
            do_enable = argin->lvalue[i] != 0;
            deviceList = get_device_list(pattern, notFound);
            DServer *adm_dev = Util::instance()->get_dserver_device();
            for(const auto &device : deviceList)
            {
                try
                {
                    adm_dev->check_lock_owner(device, "set_telemetry_logging", (device->get_name()).c_str());
                    device->set_telemetry_logging(do_enable);
                }
                catch(Tango::DevFailed &e)
                {
                    if(::strcmp(e.errors[0].reason, Tango::API_DeviceLocked) == 0)
                    {
                        lockedList.emplace_back((device->get_name()).c_str());
                    }
                    else
                    {
                        TANGO_RETHROW_EXCEPTION(e, "API_TelemetryError", "Unexpected telemetry error!");
                    }
                }
            }
        }
        // Error reporting
        std::string error, errMsg;
        if(get_errors(error, errMsg, notFound, lockedList))
        {
            TANGO_THROW_EXCEPTION(error.c_str(), errMsg.c_str());
        }
    }
    catch(std::exception &e)
    {
        TangoSys_OMemStream o;
        o << "std::exception caught [" << e.what() << "]" << std::ends;
        TANGO_THROW_EXCEPTION(API_InternalError, o.str());
    }
}

DevVarLongStringArray *DServer::get_telemetry_logging(const DevVarStringArray *argin)
{
    DevVarLongStringArray *result = nullptr;
    try
    {
        result = new DevVarLongStringArray;
        unsigned int N_devices = 0;

        std::string pattern;
        std::vector<DeviceImpl *> deviceList;
        std::vector<std::string> notFound;
        for(unsigned int i = 0; i < argin->length(); ++i)
        {
            pattern = (*argin)[i];
            deviceList = get_device_list(pattern, notFound);
            for(const auto &device : deviceList)
            {
                ++N_devices;
                result->lvalue.length(N_devices);
                result->svalue.length(N_devices);
                result->lvalue[N_devices - 1] = device->is_telemetry_logging_enabled() ? 1 : 0;
                result->svalue[N_devices - 1] = Tango::string_dup(device->get_name().c_str());
            }
        }
        // Error reporting
        std::string error, errMsg;
        if(get_errors(error, errMsg, notFound))
        {
            if(result != nullptr)
            {
                delete result;
                result = nullptr;
            }
            TANGO_THROW_EXCEPTION(error.c_str(), errMsg.c_str());
        }
    }
    catch(std::exception &e)
    {
        if(result != nullptr)
        {
            delete result;
            result = nullptr;
        }
        TangoSys_OMemStream o;
        o << "std::exception caught [" << e.what() << "]" << std::ends;
        TANGO_THROW_EXCEPTION(API_InternalError, o.str());
    }
    return result;
}

void DServer::set_telemetry_logging_endpoints_ds(const DevVarStringArray *argin)
{
    try
    {
        std::string pattern;
        std::string n_str;
        std::vector<DeviceImpl *> deviceList;
        std::vector<std::string> notFound;
        std::vector<std::string> lockedList;

        unsigned int argin_index = 0;
        // Validate arguments
        while(argin_index < argin->length())
        {
            if(argin->length() - argin_index < 2)
            {
                TANGO_THROW_EXCEPTION(API_InvalidArgs, "You must provide a device pattern and number of endpoints");
            }
            pattern = (*argin)[argin_index++];
            n_str = (*argin)[argin_index++];
            unsigned int n_endpoints = detail::parse_as<unsigned int>(n_str);
            if(argin->length() - argin_index < n_endpoints * 2)
            {
                TangoSys_OMemStream o;
                o << "The number of exporter/endpoint entries for the pattern <" << pattern << ">";
                o << " does not match the expected number (" << n_endpoints << ")";
                TANGO_THROW_EXCEPTION(API_InvalidArgs, o.str());
            }
            // Validate endpoints
            for(unsigned int j = 0; j < n_endpoints; ++j)
            {
                std::string exporter{(*argin)[argin_index++]};
                std::string endpoint{(*argin)[argin_index++]};
                telemetry::Configuration::create_collector_from_endpoint(exporter, endpoint);
            }
        }

        // Set the endpoints
        argin_index = 0;
        while(argin_index < argin->length())
        {
            pattern = (*argin)[argin_index++];
            n_str = (*argin)[argin_index++];
            unsigned int n_endpoints = detail::parse_as<unsigned int>(n_str);

            std::vector<telemetry::Configuration::Collector> collectors;
            for(unsigned int j = 0; j < n_endpoints; ++j)
            {
                std::string exporter{(*argin)[argin_index++]};
                std::string endpoint{(*argin)[argin_index++]};
                collectors.push_back(telemetry::Configuration::create_collector_from_endpoint(exporter, endpoint));
            }
            deviceList = get_device_list(pattern, notFound);
            DServer *adm_dev = Util::instance()->get_dserver_device();
            for(const auto &device : deviceList)
            {
                try
                {
                    adm_dev->check_lock_owner(device, "set_telemetry_logging_endpoints", (device->get_name()).c_str());
                    device->set_telemetry_logging_endpoints(collectors);
                }
                catch(Tango::DevFailed &e)
                {
                    if(::strcmp(e.errors[0].reason, Tango::API_DeviceLocked) == 0)
                    {
                        lockedList.emplace_back((device->get_name()).c_str());
                    }
                    else
                    {
                        TANGO_RETHROW_EXCEPTION(e, "API_TelemetryError", "Unexpected telemetry error!");
                    }
                }
            }
        }
        // Error reporting
        std::string error, errMsg;
        if(get_errors(error, errMsg, notFound, lockedList))
        {
            TANGO_THROW_EXCEPTION(error.c_str(), errMsg.c_str());
        }
    }
    catch(std::exception &e)
    {
        TangoSys_OMemStream o;
        o << "std::exception caught [" << e.what() << "]" << std::ends;
        TANGO_THROW_EXCEPTION(API_InternalError, o.str());
    }
}

DevVarStringArray *DServer::get_telemetry_logging_endpoints_ds(const DevVarStringArray *argin)
{
    DevVarStringArray *result = nullptr;
    try
    {
        result = new DevVarStringArray;

        std::string pattern;
        std::vector<DeviceImpl *> deviceList;
        std::vector<std::string> notFound;
        for(unsigned int i = 0; i < argin->length(); ++i)
        {
            pattern = (*argin)[i];
            deviceList = get_device_list(pattern, notFound);
            for(const auto &device : deviceList)
            {
                unsigned int result_size = result->length();
                auto collectors = device->get_telemetry_logging_endpoints();
                result->length(result_size + 2 + (2 * collectors.size()));
                (*result)[result_size++] = Tango::string_dup(device->get_name().c_str());
                (*result)[result_size++] = Tango::string_dup(std::to_string(collectors.size()).c_str());
                for(const auto &collector : collectors)
                {
                    (*result)[result_size++] = Tango::string_dup(to_string(collector.exporter_type).c_str());
                    (*result)[result_size++] = Tango::string_dup(collector.endpoint);
                }
            }
        }
        // Error reporting
        std::string error, errMsg;
        if(get_errors(error, errMsg, notFound))
        {
            if(result != nullptr)
            {
                delete result;
                result = nullptr;
            }
            TANGO_THROW_EXCEPTION(error.c_str(), errMsg.c_str());
        }
    }
    catch(std::exception &e)
    {
        if(result != nullptr)
        {
            delete result;
            result = nullptr;
        }
        TangoSys_OMemStream o;
        o << "std::exception caught [" << e.what() << "]" << std::ends;
        TANGO_THROW_EXCEPTION(API_InternalError, o.str());
    }
    return result;
}

void DServer::add_telemetry_logging_endpoints(const DevVarStringArray *argin)
{
    try
    {
        std::string pattern;
        std::vector<DeviceImpl *> deviceList;
        std::vector<std::string> notFound;
        std::vector<std::string> lockedList;

        unsigned int argin_index = 0;
        // Validate arguments
        if(argin->length() % 3 != 0)
        {
            TANGO_THROW_EXCEPTION(API_InvalidArgs,
                                  "There must be 3 arguments per entry: device pattern, exporter, and endpoint.");
        }
        while(argin_index < argin->length())
        {
            pattern = (*argin)[argin_index++];
            // Validate endpoints
            std::string exporter{(*argin)[argin_index++]};
            std::string endpoint{(*argin)[argin_index++]};
            telemetry::Configuration::create_collector_from_endpoint(exporter, endpoint);
        }

        // Add the endpoints
        argin_index = 0;
        while(argin_index < argin->length())
        {
            pattern = (*argin)[argin_index++];
            std::string exporter{(*argin)[argin_index++]};
            std::string endpoint{(*argin)[argin_index++]};
            auto collector = telemetry::Configuration::create_collector_from_endpoint(exporter, endpoint);

            deviceList = get_device_list(pattern, notFound);
            DServer *adm_dev = Util::instance()->get_dserver_device();
            for(const auto &device : deviceList)
            {
                try
                {
                    adm_dev->check_lock_owner(device, "add_telemetry_logging_endpoints", (device->get_name()).c_str());
                    device->add_telemetry_logging_endpoint(collector);
                }
                catch(Tango::DevFailed &e)
                {
                    if(::strcmp(e.errors[0].reason, Tango::API_DeviceLocked) == 0)
                    {
                        lockedList.emplace_back((device->get_name()).c_str());
                    }
                    else
                    {
                        TANGO_RETHROW_EXCEPTION(e, "API_TelemetryError", "Unexpected telemetry error!");
                    }
                }
            }
        }
        // Error reporting
        std::string error, errMsg;
        if(get_errors(error, errMsg, notFound, lockedList))
        {
            TANGO_THROW_EXCEPTION(error.c_str(), errMsg.c_str());
        }
    }
    catch(std::exception &e)
    {
        TangoSys_OMemStream o;
        o << "std::exception caught [" << e.what() << "]" << std::ends;
        TANGO_THROW_EXCEPTION(API_InternalError, o.str());
    }
}

void DServer::remove_telemetry_logging_endpoints(const DevVarStringArray *argin)
{
    try
    {
        std::string pattern;
        std::vector<DeviceImpl *> deviceList;
        std::vector<std::string> notFound;
        std::vector<std::string> lockedList;

        unsigned int argin_index = 0;
        // Validate arguments
        if(argin->length() % 3 != 0)
        {
            TANGO_THROW_EXCEPTION(API_InvalidArgs,
                                  "There must be 3 arguments per entry: device pattern, exporter, and endpoint.");
        }
        while(argin_index < argin->length())
        {
            pattern = (*argin)[argin_index++];
            // Validate endpoints
            std::string exporter{(*argin)[argin_index++]};
            std::string endpoint{(*argin)[argin_index++]};
            telemetry::Configuration::create_collector_from_endpoint(exporter, endpoint);
        }

        // Remove the endpoints
        argin_index = 0;
        while(argin_index < argin->length())
        {
            pattern = (*argin)[argin_index++];
            std::string exporter{(*argin)[argin_index++]};
            std::string endpoint{(*argin)[argin_index++]};
            auto collector = telemetry::Configuration::create_collector_from_endpoint(exporter, endpoint);

            deviceList = get_device_list(pattern, notFound);
            DServer *adm_dev = Util::instance()->get_dserver_device();
            for(const auto &device : deviceList)
            {
                try
                {
                    adm_dev->check_lock_owner(
                        device, "remote_telemetry_logging_endpoints", (device->get_name()).c_str());
                    device->remove_telemetry_logging_endpoint(collector);
                }
                catch(Tango::DevFailed &e)
                {
                    if(::strcmp(e.errors[0].reason, Tango::API_DeviceLocked) == 0)
                    {
                        lockedList.emplace_back((device->get_name()).c_str());
                    }
                    else
                    {
                        TANGO_RETHROW_EXCEPTION(e, "API_TelemetryError", "Unexpected telemetry error!");
                    }
                }
            }
        }
        // Error reporting
        std::string error, errMsg;
        if(get_errors(error, errMsg, notFound, lockedList))
        {
            TANGO_THROW_EXCEPTION(error.c_str(), errMsg.c_str());
        }
    }
    catch(std::exception &e)
    {
        TangoSys_OMemStream o;
        o << "std::exception caught [" << e.what() << "]" << std::ends;
        TANGO_THROW_EXCEPTION(API_InternalError, o.str());
    }
}

} // namespace Tango

#endif // #if defined(TANGO_USE_TELEMETRY)
