//! \~\file pithread.h
//! \~\ingroup Thread
//! \~\brief
//! \~english Runtime thread object with optional loop execution
//! \~russian Объект потока выполнения с необязательным циклом
//!
//! \~\details
//! \~english
//! %PIThread runs \a begin(), \a run() and \a end() on a dedicated system
//! thread. In loop mode it also drains queued delivery addressed to the thread
//! object as performer through \a maybeCallQueuedEvents() before each iteration.
//! \~russian
//! %PIThread выполняет \a begin(), \a run() и \a end() в отдельном системном
//! потоке. В циклическом режиме он также обрабатывает отложенную доставку,
//! адресованную объекту потока как исполнителю, через
//! \a maybeCallQueuedEvents() перед каждой итерацией.
/*
    PIP - Platform Independent Primitives
    Thread
    Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@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 PITHREAD_H
#define PITHREAD_H

#include "piinit.h"
#include "pimutex.h"
#include "piobject.h"
#include "pithreadnotifier.h"

class PIThread;

#ifndef MICRO_PIP
class PIIntrospectionThreads;

class PIP_EXPORT __PIThreadCollection: public PIObject {
	PIOBJECT(__PIThreadCollection)

public:
	static __PIThreadCollection * instance();
	void registerThread(PIThread * t);
	void unregisterThread(PIThread * t);
	PIVector<PIThread *> threads() const;
	void lock() { mutex.lock(); }
	void unlock() { mutex.unlock(); }
	void startedAuto(PIThread * t);
	EVENT_HANDLER(void, stoppedAuto);

private:
	PIVector<PIThread *> threads_, auto_threads_;
	mutable PIMutex mutex, auto_mutex;
};


class PIP_EXPORT __PIThreadCollection_Initializer__ {
public:
	__PIThreadCollection_Initializer__();
	~__PIThreadCollection_Initializer__();
	static int count_;
	static __PIThreadCollection * __instance__;
};

static __PIThreadCollection_Initializer__ __PIThreadCollection_initializer__;
#endif // MICRO_PIP

//! \~english Callback executed by %PIThread with the current \a data() pointer.
//! \~russian Обратный вызов, который %PIThread выполняет с текущим указателем \a data().
typedef std::function<void(void *)> ThreadFunc;


//! \~\ingroup Thread
//! \~\brief
//! \~english Thread object that executes work on a dedicated system thread.
//! \~russian Объект потока, выполняющий работу в отдельном системном потоке.
//! \~\details
//! \~english
//! The default loop calls \a begin(), then repeats queued-delivery draining for
//! this performer object plus \a run(), and finally calls \a end() when
//! stopping. Use \a startOnce() when only one pass is needed and no repeated
//! queued draining loop is required.
//! \~russian
//! Стандартный цикл вызывает \a begin(), затем повторяет обработку отложенной
//! доставки для этого объекта-исполнителя вместе с \a run(), а при остановке
//! вызывает \a end(). Используйте \a startOnce(), когда нужен только один
//! проход без повторяющегося цикла обработки очереди.
class PIP_EXPORT PIThread: public PIObject {
	PIOBJECT_SUBCLASS(PIThread, PIObject);
#ifndef MICRO_PIP
	friend class PIIntrospectionThreads;
#endif

public:
	NO_COPY_CLASS(PIThread);

	//! \~english Constructs a thread with user data, callback and optional immediate start.
	//! \~russian Создает поток с пользовательскими данными, обратным вызовом и необязательным немедленным запуском.
	PIThread(void * data, ThreadFunc func, bool startNow = false, PISystemTime loop_delay = {});

	//! \~english Constructs a thread with a callback without custom data.
	//! \~russian Создает поток с обратным вызовом без пользовательских данных.
	PIThread(std::function<void()> func, bool startNow = false, PISystemTime loop_delay = {});

	//! \~english Constructs a subclass-oriented thread with an optional loop delay.
	//! \~russian Создает поток для наследования с необязательной задержкой цикла.
	PIThread(bool startNow = false, PISystemTime loop_delay = {});

	//! \~english Destroys the thread object. If it is still running, destruction forces termination.
	//! \~russian Уничтожает объект потока. Если поток еще работает, при уничтожении выполняется принудительное завершение.
	virtual ~PIThread();

	//! \~english Thread priority hint.
	//! \~russian Подсказка приоритета потока.
	enum Priority {
		piLowerst /** \~english Lowest \~russian Низший */,
		piLow /** \~english Low \~russian Низкий */,
		piNormal /** \~english Normal, this is default priority of threads and timers \~russian Нормальный, это приоритет по умолчанию для
		            потоков и таймеров */
			,
		piHigh /** \~english High \~russian Высокий */,
		piHighest /** \~english Highest \~russian Высший */
	};


	//! \~english Starts the thread with the stored callback and loop delay.
	//! \~russian Запускает поток с сохраненными обратным вызовом и задержкой цикла.
	bool start();

	//! \~english Stores a new loop delay and starts the thread.
	//! \~russian Сохраняет новую задержку цикла и запускает поток.
	bool start(PISystemTime loop_delay);

	//! \~english Stores a callback and starts the thread.
	//! \~russian Сохраняет обратный вызов и запускает поток.
	bool start(ThreadFunc func);

	//! \~english Stores a callback and loop delay, then starts the thread.
	//! \~russian Сохраняет обратный вызов и задержку цикла, затем запускает поток.
	bool start(ThreadFunc func, PISystemTime loop_delay);

	//! \~english Stores a lambda callback and starts the thread.
	//! \~russian Сохраняет лямбда-обратный вызов и запускает поток.
	bool start(std::function<void()> func);

	//! \~english Stores a lambda callback and loop delay, then starts the thread.
	//! \~russian Сохраняет лямбда-обратный вызов и задержку цикла, затем запускает поток.
	bool start(std::function<void()> func, PISystemTime loop_delay);

	//! \~english Starts a one-shot thread without the repeating loop.
	//! \~russian Запускает одноразовый поток без повторяющегося цикла.
	bool startOnce();

	//! \~english Stores a callback and starts one-shot execution.
	//! \~russian Сохраняет обратный вызов и запускает одноразовое выполнение.
	bool startOnce(ThreadFunc func);

	//! \~english Stores a lambda callback and starts one-shot execution.
	//! \~russian Сохраняет лямбда-обратный вызов и запускает одноразовое выполнение.
	bool startOnce(std::function<void()> func);

	//! \~english Deprecated overload of \a stopAndWait() that accepts milliseconds.
	//! \~russian Устаревшая перегрузка \a stopAndWait(), принимающая миллисекунды.
	bool stopAndWait(int timeout_ms) DEPRECATEDM("use stopAndWait(PISystemTime)") {
		return stopAndWait(PISystemTime::fromMilliseconds(timeout_ms));
	}

	//! \~english Requests stop and waits for thread completion. Returns \b false if the timeout expires.
	//! \~russian Запрашивает остановку и ожидает завершения потока. Возвращает \b false, если таймаут истек.
	bool stopAndWait(PISystemTime timeout = {});

	//! \~english Sets the data pointer passed to \a ThreadFunc callbacks.
	//! \~russian Устанавливает указатель данных, передаваемый в обратные вызовы \a ThreadFunc.
	void setData(void * d) { data_ = d; }

	//! \~english Sets the callback executed after each \a run() pass.
	//! \~russian Устанавливает обратный вызов, выполняемый после каждого прохода \a run().
	void setSlot(ThreadFunc func) { ret_func = func; }

	//! \~english Sets a lambda callback executed after each \a run() pass.
	//! \~russian Устанавливает лямбда-обратный вызов, выполняемый после каждого прохода \a run().
	void setSlot(std::function<void()> func) {
		ret_func = [func](void *) { func(); };
	}

	//! \~english Sets the priority hint. If the thread is already running, applies it immediately.
	//! \~russian Устанавливает подсказку приоритета. Если поток уже работает, применяет ее немедленно.
	void setPriority(PIThread::Priority prior);

	//! \~english Returns the data pointer passed to \a ThreadFunc callbacks.
	//! \~russian Возвращает указатель данных, передаваемый в обратные вызовы \a ThreadFunc.
	void * data() const { return data_; }

	//! \~english Returns the configured priority hint.
	//! \~russian Возвращает настроенную подсказку приоритета.
	PIThread::Priority priority() const { return priority_; }

	//! \~english Returns whether the thread is currently running.
	//! \~russian Возвращает, выполняется ли поток в данный момент.
	bool isRunning() const { return running_; }

	//! \~english Returns whether stop has been requested and the thread is still finishing.
	//! \~russian Возвращает, был ли запрошен останов и поток еще завершает работу.
	bool isStopping() const { return running_ && terminating; }

	//! \~english Waits until the thread starts. Returns \b false if the timeout expires first.
	//! \~russian Ожидает запуска потока. Возвращает \b false, если таймаут истек раньше.
	bool waitForStart(PISystemTime timeout = {});

	//! \~english Deprecated overload of \a waitForStart() that accepts milliseconds.
	//! \~russian Устаревшая перегрузка \a waitForStart(), принимающая миллисекунды.
	bool waitForStart(int timeout_msecs) DEPRECATEDM("use waitForStart(PISystemTime)") {
		return waitForStart(PISystemTime::fromMilliseconds(timeout_msecs));
	}

	//! \~english Waits for thread completion. Returns \b false if the timeout expires first.
	//! \~russian Ожидает завершения потока. Возвращает \b false, если таймаут истек раньше.
	bool waitForFinish(PISystemTime timeout = {});

	//! \~english Deprecated overload of \a waitForFinish() that accepts milliseconds.
	//! \~russian Устаревшая перегрузка \a waitForFinish(), принимающая миллисекунды.
	bool waitForFinish(int timeout_msecs) DEPRECATEDM("use waitForFinish(PISystemTime)") {
		return waitForFinish(PISystemTime::fromMilliseconds(timeout_msecs));
	}

	//! \~english Enables locking of the internal mutex around \a begin(), \a run(), callbacks and \a end().
	//! \~russian Включает блокировку внутреннего мьютекса вокруг \a begin(), \a run(), обратных вызовов и \a end().
	void needLockRun(bool need) { lockRun = need; }

	//! \~english Returns the internal mutex used by \a lock(), \a unlock() and \a needLockRun().
	//! \~russian Возвращает внутренний мьютекс, используемый \a lock(), \a unlock() и \a needLockRun().
	PIMutex & mutex() const { return thread_mutex; }

	//! \~english Returns the system thread identifier, or -1 when the thread is not running.
	//! \~russian Возвращает системный идентификатор потока, либо -1 когда поток не запущен.
	llong tid() const { return tid_; }

	void __thread_func__();
	void __thread_func_once__();


	//! \~english Creates a temporary thread and invokes handler \a handler of object \a object on it.
	//! \~russian Создает временный поток и вызывает в нем обработчик \a handler объекта \a object.
	static void runOnce(PIObject * object, const char * handler, const PIString & name = PIString());

	//! \~english Creates a temporary thread and runs [lambda expression](https://en.cppreference.com/w/cpp/language/lambda) \a func on it.
	//! \~russian Создает временный поток и выполняет в нем [лямбда-выражение](https://ru.cppreference.com/w/cpp/language/lambda) \a func.
	static void runOnce(std::function<void()> func, const PIString & name = PIString());

	//! \handlers
	//! \{

	//! \fn void stop()
	//! \brief
	//! \~english Requests graceful thread shutdown.
	//! \~russian Запрашивает корректное завершение потока.
	EVENT_HANDLER0(void, stop);

	//! \fn void terminate()
	//! \brief
	//! \~english Forces thread termination. Use only as a last resort.
	//! \~russian Принудительно прерывает поток. Используйте только как крайнюю меру.
	EVENT_HANDLER0(void, terminate);

	//! \fn void lock()
	//! \brief
	//! \~english Locks the internal mutex.
	//! \~russian Блокирует внутренний мьютекс.
	EVENT_HANDLER0(void, lock) const { thread_mutex.lock(); }

	//! \fn void unlock()
	//! \brief
	//! \~english Unlocks the internal mutex.
	//! \~russian Разблокирует внутренний мьютекс.
	EVENT_HANDLER0(void, unlock) const { thread_mutex.unlock(); }

	//! \}
	//! \events
	//! \{

	//! \fn void started()
	//! \brief
	//! \~english Raised after the thread has started.
	//! \~russian Вызывается после запуска потока.
	EVENT(started);

	//! \fn void stopped()
	//! \brief
	//! \~english Raised when thread shutdown begins.
	//! \~russian Вызывается при начале завершения потока.
	EVENT(stopped);

	//! \}

protected:
	static int priority2System(PIThread::Priority p);

	//! \~english Virtual method executed once after the system thread starts and before \a started().
	//! \~russian Виртуальный метод, выполняемый один раз после запуска системного потока и до \a started().
	virtual void begin() { ; }

	//! \~english Virtual method executed on each loop iteration until stop is requested.
	//! \~russian Виртуальный метод, выполняемый на каждой итерации цикла, пока не запрошен останов.
	virtual void run() { ; }

	//! \~english Virtual method executed once during thread shutdown after \a stopped().
	//! \~russian Виртуальный метод, выполняемый один раз при завершении потока после \a stopped().
	virtual void end() { ; }

	std::atomic_bool terminating, running_, lockRun;
	PISystemTime delay_;
	llong tid_   = -1;
	void * data_ = nullptr;
	mutable PIMutex thread_mutex;
	PIThread::Priority priority_ = piNormal;
	ThreadFunc ret_func          = nullptr;
	PIThreadNotifier state_notifier;
	PRIVATE_DECLARATION(PIP_EXPORT)

private:
	bool _startThread(void * func);
	void _beginThread();
	void _runThread();
	void _endThread();
	bool _waitForFinish(PISystemTime max_tm);

	PIByteArray createThreadName(int size = 16) const;
	void setThreadName();
};


#endif // PITHREAD_H
