/*
{{copyright}} 
*/

/*
{{version}} 
*/

/*
{{license}} 
*/

#pragma once

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

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

  void lock() const;                                                      // Locks the mutex, blocks if the mutex is not available
  void unlock() const;                                                    // Unlocks the mutex
  bool try_lock() const;                                                  // Tries to lock the mutex, returns if the mutex is not available
  bool try_lock_for(const std::chrono::milliseconds &duration) const;     // Attempts to lock the mutex, returns if the mutex is not available

  protected:
  SemaphoreHandle_t m_xSemaphore = nullptr;
};
//--------------------------------------------------------------

//--------------------------------------------------------------
/* Constructor */
inline Mutex::Mutex()
{
  m_xSemaphore = xSemaphoreCreateMutex();
  configASSERT(m_xSemaphore);
}
//--------------------------------------------------------------
/* Destructor */
inline Mutex::~Mutex()
{
  if (!m_xSemaphore)
    vSemaphoreDelete(m_xSemaphore);
}
//--------------------------------------------------------------
/* Locks the mutex, blocks if the mutex is not available */
inline void Mutex::lock() const
{
  if (xSemaphoreTake(m_xSemaphore, portMAX_DELAY) != pdTRUE)
    configASSERT(0);
}
//--------------------------------------------------------------
/* Unlocks the mutex */
inline void Mutex::unlock() const
{
  xSemaphoreGive(m_xSemaphore);
}
//--------------------------------------------------------------
/* Tries to lock the mutex, returns if the mutex is not available */
inline bool Mutex::try_lock() const
{
  return try_lock_for(std::chrono::milliseconds(0));
}
//--------------------------------------------------------------
/* Attempts to lock the mutex, returns if the mutex has been unavailable for the specified time duration */
inline bool Mutex::try_lock_for(const std::chrono::milliseconds &duration) const
{
  const auto waitDelay = pdMS_TO_TICKS(duration.count());

  if (xSemaphoreTakeRecursive(m_xSemaphore, waitDelay) == pdTRUE)
    return true;
  return false;
}
//--------------------------------------------------------------

/* --- */

//--------------------------------------------------------------
class RecursiveMutex
{
  public:
  RecursiveMutex();                                                    // Constructor
  ~RecursiveMutex();                                                   // Destructor
  RecursiveMutex(const RecursiveMutex &other)            = delete;     // Copy constructor
  RecursiveMutex(RecursiveMutex &&other)                 = delete;     // Move constructor
  RecursiveMutex &operator=(const RecursiveMutex &other) = delete;     // Copy assignment
  RecursiveMutex &operator=(RecursiveMutex &&other)      = delete;     // Move assignment

  void lock() const;                                                      // Locks the mutex, blocks if the mutex is not available
  void unlock() const;                                                    // Unlocks the mutex
  bool try_lock() const;                                                  // Tries to lock the mutex, returns if the mutex is not available
  bool try_lock_for(const std::chrono::milliseconds &duration) const;     // Attempts to lock the mutex, returns if the mutex is not available

  protected:
  SemaphoreHandle_t m_xSemaphore = nullptr;
};
//--------------------------------------------------------------

//--------------------------------------------------------------
/* Constructor */
inline RecursiveMutex::RecursiveMutex()
{
  m_xSemaphore = xSemaphoreCreateRecursiveMutex();
  configASSERT(m_xSemaphore);
}
//--------------------------------------------------------------
/* Destructor */
inline RecursiveMutex::~RecursiveMutex()
{
  if (!m_xSemaphore)
    vSemaphoreDelete(m_xSemaphore);
}
//--------------------------------------------------------------
/* Locks the mutex, blocks if the mutex is not available */
inline void RecursiveMutex::lock() const
{
  if (xSemaphoreTakeRecursive(m_xSemaphore, portMAX_DELAY) != pdTRUE)
    configASSERT(0);
}
//--------------------------------------------------------------
/* Unlocks the mutex */
inline void RecursiveMutex::unlock() const
{
  xSemaphoreGiveRecursive(m_xSemaphore);
}
//--------------------------------------------------------------
/* Tries to lock the mutex, returns if the mutex is not available */
inline bool RecursiveMutex::try_lock() const
{
  return try_lock_for(std::chrono::milliseconds(0));
}
//--------------------------------------------------------------
/* Attempts to lock the mutex, returns if the mutex has been unavailable for the specified time duration */
inline bool RecursiveMutex::try_lock_for(const std::chrono::milliseconds &duration) const
{
  const auto waitDelay = pdMS_TO_TICKS(duration.count());

  if (xSemaphoreTakeRecursive(m_xSemaphore, waitDelay) == pdTRUE)
    return true;
  return false;
}
//--------------------------------------------------------------
}     // namespace sdi_uToolBox::generic::FreeRTOS
