/*
 * SPDX-FileCopyrightText: 2024 Copyright contributors to the cppTango project
 *
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */
#ifndef TANGO_INTERNAL_TELEMETRY_TELEMETRY_KERNEL_MACROS_H
#define TANGO_INTERNAL_TELEMETRY_TELEMETRY_KERNEL_MACROS_H

#include <tango/common/defines.h>

#ifdef TANGO_USE_TELEMETRY

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

  #include <string>

    //-----------------------------------------------------------------------------------------------------------------
    // TANGO_TELEMETRY_KERNEL_CLIENT_SPAN & TANGO_TELEMETRY_KERNEL_CLIENT_SPAN  (FOR CPP KERNEL ONLY)
    //
    // Start a new "client" span.
    //
    // For Tango kernel internal usage only. This is used by the Tango::DeviceProxy to initiate a client RPC.
    //
    // Usage:
    //
    //    auto span = TANGO_TELEMETRY_KERNEL_CLIENT_SPAN();
    //    TANGO_TELEMETRY_KERNEL_CLIENT_SPAN(some_arg_name);
    //-----------------------------------------------------------------------------------------------------------------
  #define TANGO_TELEMETRY_KERNEL_CLIENT_SPAN(ATTRS)           \
      Tango::telemetry::Interface::get_current()->start_span( \
          TELEMETRY_CURRENT_FUNCTION, ATTRS, Tango::telemetry::Span::Kind::kClient)

    //-----------------------------------------------------------------------------------------------------------------
    // TANGO_TELEMETRY_KERNEL_SERVER_SPAN (FOR CPP KERNEL ONLY)
    //
    // Start a new "server" span.
    //
    // For Tango kernel internal usage only. This is used by the several flavors of Tango::DeviceImpl to initiate a
    // reply to a client RPC.
    //
    // Usage:
    //    TANGO_TELEMETRY_KERNEL_SERVER_SPAN;
    //-------------------------------------------------------------------------------
  #define TANGO_TELEMETRY_KERNEL_SERVER_SPAN(...) telemetry()->set_trace_context(__VA_ARGS__)

    //-------------------------------------------------------------------------------
    // TANGO_TELEMETRY_TRY (FOR CPP KERNEL ONLY)
    //-------------------------------------------------------------------------------
  #define TANGO_TELEMETRY_TRY \
      try                     \
      {
  //---------------------------------------------------------------------------------
  // TANGO_TELEMETRY_TRY (FOR CPP KERNEL ONLY)
  //---------------------------------------------------------------------------------
  // see otel. specs for details
  // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/exceptions/exceptions-spans.md#semantic-conventions-for-exceptions-on-spans
  #define TANGO_TELEMETRY_CATCH                                                                                    \
      }                                                                                                            \
      catch(...)                                                                                                   \
      {                                                                                                            \
          std::string type;                                                                                        \
          std::string msg;                                                                                         \
          Tango::telemetry::Interface::extract_exception_info(type, msg);                                          \
          auto current_span = Tango::telemetry::Interface::get_current()->get_current_span();                      \
          if(current_span)                                                                                         \
          {                                                                                                        \
              current_span->add_event("exception caught", {{"exception.type", type}, {"exception.message", msg}}); \
          }                                                                                                        \
          TANGO_TELEMETRY_SET_ERROR_STATUS("exception caught (see associated event)");                             \
          throw;                                                                                                   \
      }

    /// Helper macros for tango RPC starting points
    /// ATTRS is an initializer list enclosed in `()`, pass `({})` for no additional attributes
  #define TANGO_TELEMETRY_TRACE_BEGIN(ATTRS)                                              \
      auto span = TANGO_TELEMETRY_KERNEL_CLIENT_SPAN(Tango::telemetry::Attributes ATTRS); \
      auto scope = TANGO_TELEMETRY_SCOPE(span);                                           \
      TANGO_TELEMETRY_TRY

    /// Helper macros for tango RPC starting points inside the kernel part of the device server
    /// ATTRS is an initializer list enclosed in `()`, pass `({})` for no additional attributes
  #define TANGO_TELEMETRY_KERNEL_TRACE_BEGIN(ATTRS)                                          \
      auto telemetry_interface_scope = TANGO_TELEMETRY_ACTIVE_INTERFACE(telemetry());        \
      auto cl_addr = get_client_ident();                                                     \
      const char *client_ip;                                                                 \
      if(cl_addr != nullptr)                                                                 \
      {                                                                                      \
          client_ip = cl_addr->client_ip;                                                    \
      }                                                                                      \
      else                                                                                   \
      {                                                                                      \
          client_ip = "";                                                                    \
      }                                                                                      \
      auto scope = TANGO_TELEMETRY_KERNEL_SERVER_SPAN(                                       \
          TELEMETRY_CURRENT_FUNCTION, Tango::telemetry::Attributes ATTRS, cl_id, client_ip); \
      TANGO_TELEMETRY_TRY

  #define TANGO_TELEMETRY_TRACE_END() TANGO_TELEMETRY_CATCH

  #define TANGO_TELEMETRY_TRACK_BAD_ATTR(ATTR) bad_attributes.emplace_back(ATTR);

#else

  #define TANGO_TELEMETRY_TRACE_BEGIN(ATTRS) static_cast<void>(0)
  #define TANGO_TELEMETRY_KERNEL_TRACE_BEGIN(ATTRS) static_cast<void>(0)
  #define TANGO_TELEMETRY_TRACE_END() static_cast<void>(0)

  #define TANGO_TELEMETRY_TRACK_BAD_ATTR(ATTR) static_cast<void>(0)

#endif // TANGO_USE_TELEMETRY

#endif // TANGO_INTERNAL_TELEMETRY_TELEMETRY_KERNEL_MACROS_H
