/*
{{copyright}} 
*/

/*
{{version}} 
*/

/*
{{license}} 
*/

#pragma once

#include <FreeRTOSHeaders.h>
#include <chrono>

namespace sdi_uToolBox::generic::FreeRTOS
{
//--------------------------------------------------------------
class Semaphore
{
  using Timeout = std::chrono::milliseconds;

  public:
  Semaphore() = delete;                                      // Default constructor
  ~Semaphore();                                              // Default destructor
  Semaphore(const Semaphore &other)            = delete;     // Copy constructor
  Semaphore(Semaphore &&other)                 = delete;     // Move constructor
  Semaphore &operator=(const Semaphore &other) = delete;     // Copy assignment
  Semaphore &operator=(Semaphore &&other)      = delete;     // Move assignment

  explicit Semaphore(size_t maxCount = 1, size_t initialCount = 0);     // Constructor

  bool give() const;                    // Give/release the semaphore
  bool giveFromISR() const;             // Give/release the semaphore from ISR
  bool take(Timeout timeout) const;     // Take/acquire the semaphore with timeout
  bool take() const;                    // Take/acquire the semaphore without timeout (blocking)

  protected:
  SemaphoreHandle_t m_xSemaphore = nullptr;
};
//--------------------------------------------------------------
/* Constructor */
inline Semaphore::Semaphore(const size_t maxCount, const size_t initialCount)
{
  if (maxCount == 1)
  {
    // Binary semaphore
    m_xSemaphore = xSemaphoreCreateBinary();
    if (m_xSemaphore && initialCount > 0)
      xSemaphoreGive(m_xSemaphore);
  }
  else
  {
    // Counting semaphore
    m_xSemaphore = xSemaphoreCreateCounting(maxCount, initialCount);
  }
  configASSERT(m_xSemaphore);
}
//--------------------------------------------------------------
/* Default destructor */
inline Semaphore::~Semaphore()
{
  if (m_xSemaphore)
    vSemaphoreDelete(m_xSemaphore);
}

//--------------------------------------------------------------
/* Give/release the semaphore */
inline bool Semaphore::give() const
{
  return xSemaphoreGive(m_xSemaphore) == pdTRUE;
}
//--------------------------------------------------------------
/* Give/release the semaphore from ISR */
inline bool Semaphore::giveFromISR() const
{
  BaseType_t xHigherPriorityTaskWoken = pdFALSE;

  const bool result = xSemaphoreGiveFromISR(m_xSemaphore, &xHigherPriorityTaskWoken) == pdTRUE;
  portYIELD_FROM_ISR(xHigherPriorityTaskWoken);     // Request a context switch if needed
  return result;
}
//--------------------------------------------------------------
/* Take/acquire the semaphore with timeout */
inline bool Semaphore::take(const Timeout timeout) const
{
  const TickType_t xTicksToWait = (timeout.count() == 0) ? 0 : pdMS_TO_TICKS(timeout.count());
  return xSemaphoreTake(m_xSemaphore, xTicksToWait) == pdTRUE;
}
//--------------------------------------------------------------
/* Take/acquire the semaphore without timeout (blocking) */
inline bool Semaphore::take() const
{
  return xSemaphoreTake(m_xSemaphore, portMAX_DELAY) == pdTRUE;
}
//--------------------------------------------------------------

/* --- */

//--------------------------------------------------------------
/* Binary Semaphore helper class */
class BinarySemaphore : public Semaphore
{
  public:
  explicit BinarySemaphore(const bool initialState = false)
    : Semaphore(1, initialState ? 1 : 0)
  {
  }
};
//--------------------------------------------------------------

/* --- */

//--------------------------------------------------------------
class CountingSemaphore : public Semaphore
{
  /* Counting Semaphore helper class */
  public:
  explicit CountingSemaphore(const uint32_t maxCount, const uint32_t initialCount = 0)
    : Semaphore(maxCount, initialCount)
  {
  }
};
//--------------------------------------------------------------
}     // namespace sdi_uToolBox::generic::FreeRTOS
