#ifndef RT_THREADING_HELPERS_HPP
#define RT_THREADING_HELPERS_HPP

// C++ wrappers around threading functions/types

#ifndef __cplusplus
#error This file must only be used from C++ code
#endif

#include "threading.h"

template <typename _LockType, void (*_AcquireFunc)(_LockType *), void (*_ReleaseFunc)(_LockType *)>
class rt_generic_auto_lock {
  public:
    explicit rt_generic_auto_lock(_LockType *lock) : m_lock(lock) {
        _AcquireFunc(lock);
    }

    ~rt_generic_auto_lock() {
        _ReleaseFunc(m_lock);
    }

    rt_generic_auto_lock(const rt_generic_auto_lock &)                  = delete;
    const rt_generic_auto_lock &operator=(const rt_generic_auto_lock &) = delete;

  private:
    _LockType *m_lock;
};

namespace __rt_auto_lock_mutex_wrappers {

RT_INLINE void Lock(rt_mutex *mtx) {
    RT_ASSERT_ALWAYS_EVAL(rtLockMutex(mtx), "Lock mutex failed!");
}

RT_INLINE void Unlock(rt_mutex *mtx) {
    RT_ASSERT_ALWAYS_EVAL(rtUnlockMutex(mtx), "Unlock mutex failed!");
}

} // namespace __rt_auto_lock_mutex_wrappers

using rt_mutex_auto_lock = rt_generic_auto_lock<rt_mutex,
                                          __rt_auto_lock_mutex_wrappers::Lock,
                                          __rt_auto_lock_mutex_wrappers::Unlock>;

using rt_read_auto_lock = rt_generic_auto_lock<rt_rwlock, rtLockRead, rtUnlockRead>;
using rt_write_auto_lock = rt_generic_auto_lock<rt_rwlock, rtLockWrite, rtUnlockWrite>;

RT_INLINE rt_mutex_auto_lock rtAutoLock(rt_mutex *mutex) {
    return rt_mutex_auto_lock(mutex);
}

RT_INLINE rt_read_auto_lock rtAutoLock(rt_rwlock *rw) {
    return rt_read_auto_lock(rw);
}

RT_INLINE rt_write_auto_lock rtAutoWriteLock(rt_rwlock *rw) {
    return rt_write_auto_lock(rw);
}

#endif
