/*
{{copyright}} 
*/

/*
{{version}} 
*/

/*
{{license}} 
*/

#pragma once

#include "../../defs.h"

#include <FreeRTOSHeaders.h>
#include <algorithm>
#include <span>

namespace sdi_uToolBox::generic::FreeRTOS
{
//--------------------------------------------------------------
/**
 * @class TaskInspector
 * @brief Utility class for inspecting FreeRTOS task statistics and runtime information.
 *
 * This class provides static methods to retrieve information about tasks,
 * such as their name, priority, runtime statistics, and stack usage.
 * It is designed for embedded systems and avoids dynamic memory allocation.
 *
 * @note Required FreeRTOSConfig.h options:
 *   - configUSE_TRACE_FACILITY must be set to 1
 *   - configUSE_RECURSIVE_MUTEXES must be set to 1
 */
class TaskInspector
{
  static constexpr size_t MAX_TASKS = 16;

  public:
  /**
   * @struct TaskInfo
   * @brief Structure holding detailed information about a FreeRTOS task.
   */
  struct TaskInfo
  {
    const char *taskName           = nullptr;      // Task name
    UBaseType_t currentPriority    = 0;            // Task current priority
    UBaseType_t basePriority       = 0;            // Task base priority
    uint32_t    runTimeCounter     = 0;            // Total run time counter
    eTaskState  currentState       = eInvalid;     // Task state
    UBaseType_t stackHighWaterMark = 0;            // Stack high watermark
  };

  /**
   * @struct RuntimeStats
   * @brief Structure holding global runtime statistics for all tasks.
   */
  struct RuntimeStats
  {
    uint32_t totalRunTime  = 0;     // Total run time of all tasks
    uint32_t numberOfTasks = 0;     // Number of tasks
  };

  /**
   * @struct TaskRuntimeStat
   * @brief Structure holding runtime statistics for a single task.
   */
  struct TaskRuntimeStat
  {
    const char *taskName       = nullptr;     // Task name
    uint32_t    runTimeCounter = 0;           // Total run time counter
    Real        cpuUsage       = 0.0f;        // CPU usage percentage
  };

  public:
  TaskInspector()                                        = delete;      // Default constructor
  ~TaskInspector()                                       = default;     // Default destructor
  TaskInspector(const TaskInspector &obj)                = delete;      // Copy constructor
  TaskInspector(TaskInspector &&obj) noexcept            = delete;      // Move constructor
  TaskInspector &operator=(const TaskInspector &obj)     = delete;      // Copy assignment operator
  TaskInspector &operator=(TaskInspector &&obj) noexcept = delete;      // Move assignment operator

  /**
   * @brief Retrieves information about a specific task or the current task if no handle is provided.
   * @param taskHandle The handle of the task to inspect (optional).
   * @return TaskInfo structure with the task's information.
   */
  [[nodiscard]] static TaskInfo getTaskInfo(TaskHandle_t taskHandle = nullptr);

  /**
   * @brief Retrieves global runtime statistics for all tasks.
   * @return RuntimeStats structure with the global runtime statistics.
   */
  [[nodiscard]] static RuntimeStats GetRuntimeStats();

  /**
   * @brief Retrieves the number of tasks currently running.
   * @return The number of tasks.
   */
  [[nodiscard]] static size_t GetTaskCount();

  /**
   * @brief Retrieves runtime statistics for all tasks.
   * @param statsArray A span to an array where task runtime statistics will be stored.
   * @return The number of tasks for which statistics were retrieved.
   */
  [[nodiscard]] static size_t GetTasksRuntimeStats(std::span<TaskRuntimeStat> statsArray);
};
//--------------------------------------------------------------
/* Retrieves information about the current task */
inline TaskInspector::TaskInfo TaskInspector::getTaskInfo(TaskHandle_t taskHandle)
{
#if (configUSE_RECURSIVE_MUTEXES != 1)
#  error "configUSE_RECURSIVE_MUTEXES must be set to 1 in FreeRTOSConfig.h"
#endif
#if (configUSE_TRACE_FACILITY != 1)
#  error "configUSE_TRACE_FACILITY must be set to 1 in FreeRTOSConfig.h"
#endif

  TaskInfo taskInfo;
  if (!taskHandle)
  {
    taskHandle = xTaskGetCurrentTaskHandle();
    if (!taskHandle)
      return taskInfo;
  }

  // Get task info
  TaskStatus_t taskStatus;
  vTaskGetInfo(taskHandle, &taskStatus, pdTRUE, eInvalid);

  // Fill task info structure
  taskInfo.taskName           = taskStatus.pcTaskName;
  taskInfo.currentPriority    = taskStatus.uxCurrentPriority;
  taskInfo.basePriority       = taskStatus.uxBasePriority;
  taskInfo.runTimeCounter     = taskStatus.ulRunTimeCounter;
  taskInfo.currentState       = taskStatus.eCurrentState;
  taskInfo.stackHighWaterMark = taskStatus.usStackHighWaterMark;

  return taskInfo;
}
//--------------------------------------------------------------
/* Retrieves global runtime statistics for all tasks */
inline TaskInspector::RuntimeStats TaskInspector::GetRuntimeStats()
{
#if (configUSE_TRACE_FACILITY != 1)
#  error "configUSE_TRACE_FACILITY must be set to 1 in FreeRTOSConfig.h"
#endif

  RuntimeStats stats;
  stats.numberOfTasks = uxTaskGetNumberOfTasks();
  uxTaskGetSystemState(nullptr, 0, &stats.totalRunTime);
  return stats;
}
//--------------------------------------------------------------
/* Retrieves the number of tasks */
inline size_t TaskInspector::GetTaskCount()
{
  return uxTaskGetNumberOfTasks();
}
//--------------------------------------------------------------
/* Retrieves runtime statistics for all tasks */
inline size_t TaskInspector::GetTasksRuntimeStats(std::span<TaskRuntimeStat> statsArray)
{
#if (configUSE_TRACE_FACILITY != 1)
#  error "configUSE_TRACE_FACILITY must be set to 1 in FreeRTOSConfig.h"
#endif

  size_t numTasks = uxTaskGetNumberOfTasks();
  numTasks        = std::min({ numTasks, statsArray.size(), MAX_TASKS });

  TaskStatus_t statusArray[MAX_TASKS];
  uint32_t     totalRunTime = 0;
  uxTaskGetSystemState(statusArray, numTasks, &totalRunTime);

  for (size_t i = 0; i < numTasks; i++)
  {
    auto &stat          = statsArray[i];
    stat.taskName       = statusArray[i].pcTaskName;
    stat.runTimeCounter = statusArray[i].ulRunTimeCounter;
    stat.cpuUsage       = (totalRunTime > 0) ? (100.0f * static_cast<Real>(stat.runTimeCounter) / static_cast<Real>(totalRunTime)) : 0.0f;
  }
  return numTasks;
}
//--------------------------------------------------------------
}     // namespace sdi_uToolBox::generic::FreeRTOS
