/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2022, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */

#pragma once

#include <type_traits>

namespace mrpt
{
/** Auxiliary helper structure for mrpt::lockHelper()
 * \ingroup mrpt_core_grp
 */
template <class T>
class LockHelper
{
	using Tnc = std::remove_const_t<T>;

   public:
	LockHelper(const Tnc* l) : l_{const_cast<Tnc*>(l)} { l_->lock(); }
	~LockHelper()
	{
		if (l_) l_->unlock();
	}

	LockHelper(const LockHelper& o) = delete;
	LockHelper& operator=(const LockHelper& o) = delete;

	LockHelper(LockHelper&& o) : l_{o.l} { o.l = nullptr; }
	LockHelper& operator=(LockHelper&& o)
	{
		l_ = o.l;
		o.l = nullptr;
		return *this;
	}

	/** Can be used to unlock the mutex before this object dtor
	 * \note [New in MRPT 2.1.5]
	 */
	void unlock()
	{
		if (!l_) return;
		l_->unlock();
		l_ = nullptr;
	}

   private:
	Tnc* l_{nullptr};
};

/** Syntactic sugar to easily create a locker to any kind of std::mutex
 * \ingroup mrpt_core_grp
 */
template <class T>
LockHelper<T> lockHelper(T& t)
{
	return LockHelper<T>(&t);
}
}  // namespace mrpt
