//{{copyright}}

//{{version}}

//{{license}}

#pragma once

#include <FreeRTOSHeaders.h>
#include <string>

namespace sdi_uToolBox::generic::FreeRTOS
{
//--------------------------------------------------------------
class Task
{
  public:
  Task() = default;                                    // Constructor
  virtual ~Task();                                     // Destructor
  Task(const Task &other)                = delete;     // Copy constructor
  Task(Task &&other) noexcept            = delete;     // Move constructor
  Task &operator=(const Task &other)     = delete;     // Copy assignment
  Task &operator=(Task &&other) noexcept = delete;     // Move assignment

  bool                       startTask();               // Create and start a FreeRTOS task
  void                       stopTask();                // Stop and destroy the task
  [[nodiscard]] bool         isTaskRunning() const;     // Return true if the task is running
  [[nodiscard]] TaskHandle_t getTaskHandle() const;     // Return the task handle

  protected:
  TaskHandle_t m_taskHandle = nullptr;

  virtual void on_taskCreated();     // Function called after successful task creation (before execution)
  virtual void on_taskStarted();     // Function called after task starts
  virtual void on_taskStopped();     // Function called before task stops

  private:
  virtual void                      processTask() = 0;            // Task implementation
  [[nodiscard]] virtual std::string getTaskName() const;          // Return a name for the task
  [[nodiscard]] virtual size_t      getTaskStackSize() const;     // Return stack size in words, not bytes
  [[nodiscard]] virtual size_t      getTaskPriority() const;      // Return priority at which the task is created

  bool m_runningFlag = false;

  static void taskCode(void *pThis);     // Task implementation
};
//--------------------------------------------------------------

//--------------------------------------------------------------
/* Destructor */
inline Task::~Task()
{
  stopTask();
}
//--------------------------------------------------------------
/* Create and start a FreeRTOS task */
inline bool Task::startTask()
{
  if (isTaskRunning())
    return true;

  const auto xReturned = xTaskCreate(taskCode,                  // Function that implements the task
                                     getTaskName().c_str(),     // Text name for the task
                                     getTaskStackSize(),        // Stack size in words, not bytes
                                     (void *)this,              // Parameter passed into the task
                                     getTaskPriority(),         // Priority at which the task is created
                                     &m_taskHandle);            // Used to pass out the created task's handle

  m_runningFlag = (xReturned == pdPASS);

  if (m_runningFlag)
    on_taskCreated();

  return m_runningFlag;
}
//--------------------------------------------------------------
/* Stop and destroy the task */
inline void Task::stopTask()
{
  if (m_taskHandle)
    vTaskDelete(m_taskHandle);

  m_taskHandle  = nullptr;
  m_runningFlag = false;
}
//--------------------------------------------------------------
/* Return true if the task is running */
inline bool Task::isTaskRunning() const
{
  return m_runningFlag;
}
//--------------------------------------------------------------
/* Return the task handle */
inline TaskHandle_t Task::getTaskHandle() const
{
  return m_taskHandle;
}
//--------------------------------------------------------------
/* Function called after successful task creation (before execution) */
inline void Task::on_taskCreated()
{
  // Nothing to do...
}
//--------------------------------------------------------------
/* Function called after task starts */
inline void Task::on_taskStarted()
{
  // Nothing to do...
}
//--------------------------------------------------------------
/* Function called after before stops */
inline void Task::on_taskStopped()
{
  // Nothing to do...
}
//--------------------------------------------------------------
/* Return a name for the task */
inline std::string Task::getTaskName() const
{
  return "";
}
//--------------------------------------------------------------
/* Return stack size in words, not bytes */
inline size_t Task::getTaskStackSize() const
{
  return configMINIMAL_STACK_SIZE;
}
//--------------------------------------------------------------
/* Return priority at which the task is created */
inline size_t Task::getTaskPriority() const
{
  return tskIDLE_PRIORITY + 1;
}
//--------------------------------------------------------------
/* Task implementation */
inline void Task::taskCode(void *pThis)
{
  const auto task = static_cast<Task *>(pThis);

  // At task starts
  if (task->m_runningFlag)
    task->on_taskStarted();

  // Main task loop
  while (task->m_runningFlag)
    task->processTask();

  // At task stops
  task->on_taskStopped();

  task->stopTask();
}
//--------------------------------------------------------------
}     // namespace sdi_uToolBox::generic::FreeRTOS
