//! \~\file piprotectedvariable.h
//! \~\ingroup Thread
//! \~\brief
//! \~english Thread-safe variable
//! \~russian Потокобезопасная переменная
/*
    PIP - Platform Independent Primitives
    Thread-safe variable
    Ivan Pelipenko peri4ko@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PIPROTECTEDVARIABLE_H
#define PIPROTECTEDVARIABLE_H

#include "pimutex.h"

//! \~\ingroup Thread
//! \~\brief
//! \~english Thread-safe variable template class
//! \~russian Шаблонный класс потокобезопасной переменной
template<typename T>
class PIP_EXPORT PIProtectedVariable {
public:
	//! \~english Constructs %PIProtectedVariable and initialize variable by value `v`.
	//! \~russian Создает %PIProtectedVariable и инициализирует переменную значением `v`.
	PIProtectedVariable(T v = T()): var(std::move(v)) {}

	//! \~\brief
	//! \~english Pointer-like wrapper returned by \a getRef() while the protected value remains locked.
	//! \~russian Указателеподобная обертка, возвращаемая \a getRef(), пока защищенное значение остается заблокированным.
	class PIP_EXPORT Pointer {
		friend class PIProtectedVariable<T>;
		NO_COPY_CLASS(Pointer);
		Pointer & operator=(Pointer && other) = delete;

	public:
		//! \~english Move constructor - transfers ownership of the lock.
		//! \~russian Конструктор перемещения - передает владение блокировкой.
		Pointer(Pointer && other): pv(other.pv) { other.can_unlock = false; };

		//! \~english Destroys wrapper and releases the mutex.
		//! \~russian Уничтожает обертку и освобождает мьютекс.
		~Pointer() {
			if (can_unlock) {
				pv.mutex.unlock();
			}
		}

		//! \~english Returns pointer access to the protected value.
		//! \~russian Возвращает указательный доступ к защищенному значению.
		T * operator->() { return &pv.var; }

		//! \~english Returns reference access to the protected value.
		//! \~russian Возвращает ссылочный доступ к защищенному значению.
		T & operator*() { return pv.var; }

	private:
		explicit Pointer() = delete;
		explicit Pointer(PIProtectedVariable<T> & v): pv(v) { pv.mutex.lock(); }

		PIProtectedVariable<T> & pv;
		bool can_unlock = true;
	};

	//! \~english Replaces the protected value with \a v.
	//! \~russian Заменяет защищенное значение на \a v.
	void set(T v) {
		PIMutexLocker _ml(mutex);
		var = std::move(v);
	}

	//! \~english Locks the value and returns wrapper-based access to it.
	//! \~russian Блокирует значение и возвращает обертку для доступа к нему.
	Pointer getRef() { return Pointer(*this); }

	//! \~english Returns a copy of the protected value.
	//! \~russian Возвращает копию защищенного значения.
	T get() const {
		PIMutexLocker _ml(mutex);
		return var;
	}

	//! \~english Replaces the protected value with \a v.
	//! \~russian Заменяет защищенное значение на \a v.
	PIProtectedVariable<T> & operator=(T v) {
		set(std::move(v));
		return *this;
	}


private:
	mutable PIMutex mutex;
	T var = {};
};


#endif // PIPROTECTEDVARIABLE_H
