/*! \file pitimer.h
 * \ingroup Thread
 * \~\brief
 * \~english Timer object backed by an internal thread
 * \~russian Объект таймера, работающий на внутреннем потоке
 *
 * \~\details
 * \~english
 * %PITimer uses an internal %PIThread to generate best-effort periodic ticks.
 * When queued delivery is enabled, the timer also drains events addressed to it
 * as performer through \a maybeCallQueuedEvents() on the main tick.
 * \~russian
 * %PITimer использует внутренний %PIThread для генерации периодических тиков
 * с практической, но не строго гарантированной точностью. Когда включена
 * отложенная доставка, таймер также обрабатывает события, адресованные ему как
 * исполнителю, через \a maybeCallQueuedEvents() на основном тике.
 */
/*
    PIP - Platform Independent Primitives
    Timer
    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 PITIMER_H
#define PITIMER_H

#include "piconditionvar.h"
#include "piobject.h"
#include "pisystemtime.h"


class PIThread;

//! \~\ingroup Thread
//! \~\brief
//! \~english Periodic timer that emits ticks from an internal worker thread.
//! \~russian Периодический таймер, который выдает тики из внутреннего рабочего потока.
//! \~\details
//! \~english
//! The main tick uses delimiter value 1. Additional frequency delimiters can
//! request extra callbacks every \a n ticks. This class does not promise exact
//! wake-up timing or scheduler fairness.
//! \~russian
//! Основной тик использует значение делителя 1. Дополнительные делители частоты
//! позволяют запрашивать дополнительные вызовы каждые \a n тиков. Этот класс не
//! обещает точное время пробуждения или справедливость планировщика.
class PIP_EXPORT PITimer: public PIObject {
	PIOBJECT_SUBCLASS(PITimer, PIObject);

public:
	NO_COPY_CLASS(PITimer);

	//! \~english Constructs a timer without a tick callback.
	//! \~russian Создает таймер без обратного вызова тика.
	explicit PITimer();

	//! \~english Constructs a timer with a callback that receives the delimiter value.
	//! \~russian Создает таймер с обратным вызовом, принимающим значение делителя.
	explicit PITimer(std::function<void(int)> func);

	//! \~english Constructs a timer with a callback that ignores the delimiter value.
	//! \~russian Создает таймер с обратным вызовом, игнорирующим значение делителя.
	explicit PITimer(std::function<void()> func);

	//! \~english Destroys the timer and stops its internal worker thread.
	//! \~russian Уничтожает таймер и останавливает его внутренний рабочий поток.
	virtual ~PITimer();

	//! \~english Returns the configured base interval between main ticks.
	//! \~russian Возвращает настроенный базовый интервал между основными тиками.
	PISystemTime interval() const;

	//! \~english Sets the base interval. If the timer is running, restarts it with the new value.
	//! \~russian Устанавливает базовый интервал. Если таймер запущен, перезапускает его с новым значением.
	void setInterval(PISystemTime interval);

	//! \~english Returns whether the timer thread is currently running.
	//! \~russian Возвращает, работает ли сейчас поток таймера.
	bool isRunning() const;

	//! \~english Returns whether stop has been requested and shutdown is still in progress.
	//! \~russian Возвращает, был ли запрошен останов и идет ли еще завершение.
	bool isStopping() const;

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

	//! \~english Sets the base interval and starts the timer.
	//! \~russian Устанавливает базовый интервал и запускает таймер.
	bool start(PISystemTime interval);

	//! \~english Sets the base interval and tick callback, then starts the timer.
	//! \~russian Устанавливает базовый интервал и обратный вызов тика, затем запускает таймер.
	bool start(PISystemTime interval, std::function<void()> func);

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

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

	//! \~english Requests stop and waits for the worker thread to finish.
	//! \~russian Запрашивает остановку и ожидает завершения рабочего потока.
	void stopAndWait(PISystemTime timeout = {});

	//! \~english Sets a tick callback that ignores the delimiter value.
	//! \~russian Устанавливает обратный вызов тика, игнорирующий значение делителя.
	void setSlot(std::function<void()> func);

	//! \~english Sets a tick callback that receives the current delimiter value.
	//! \~russian Устанавливает обратный вызов тика, принимающий текущее значение делителя.
	void setSlot(std::function<void(int)> func);

	//! \~english Enables locking of the internal mutex around tick processing.
	//! \~russian Включает блокировку внутреннего мьютекса вокруг обработки тиков.
	void needLockRun(bool need) { lockRun = need; }

	//! \~english Returns whether the timer drains queued delivery for itself as performer on each main tick. By default \b true.
	//! \~russian Возвращает, должен ли таймер обрабатывать отложенную доставку для себя как исполнителя на каждом основном тике. По
	//! умолчанию \b true.
	bool isCallQueuedEvents() const { return callEvents; }

	//! \~english Enables or disables queued-delivery draining through \a maybeCallQueuedEvents() on each main tick.
	//! \~russian Включает или отключает обработку отложенной доставки через \a maybeCallQueuedEvents() на каждом основном тике.
	void setCallQueuedEvents(bool yes) { callEvents = yes; }

	//! \~english Adds a frequency delimiter that invokes \a func every \a delim main ticks.
	//! \~russian Добавляет делитель частоты, который вызывает \a func каждые \a delim основных тиков.
	void addDelimiter(int delim, std::function<void(int)> func = nullptr);

	//! \~english Adds a delimiter with a callback that ignores the delimiter value.
	//! \~russian Добавляет делитель с обратным вызовом, игнорирующим значение делителя.
	void addDelimiter(int delim, std::function<void()> func);

	//! \~english Adds a delimiter with a pointer-based callback signature.
	//! \~russian Добавляет делитель с сигнатурой обратного вызова, принимающей указатель.
	void addDelimiter(int delim, std::function<void(void *)> slot);

	//! \~english Removes all delimiters with value \a delim.
	//! \~russian Удаляет все делители со значением \a delim.
	void removeDelimiter(int delim);


	//! \handlers
	//! \{

	//! \fn bool start()
	//! \brief
	//! \~english Starts the timer with the current \a interval().
	//! \~russian Запускает таймер с текущим значением \a interval().
	EVENT_HANDLER0(bool, start);

	//! \fn bool restart()
	//! \brief
	//! \~english Stops the timer, then starts it again with the current \a interval().
	//! \~russian Останавливает таймер, затем снова запускает его с текущим значением \a interval().
	EVENT_HANDLER0(bool, restart);

	//! \fn bool stop()
	//! \brief
	//! \~english Requests stop and wakes the timer thread, but does not wait for completion.
	//! \~russian Запрашивает остановку и пробуждает поток таймера, но не ожидает завершения.
	EVENT_HANDLER0(void, stop);

	//! \fn void clearDelimiters()
	//! \brief
	//! \~english Remove all frequency delimiters
	//! \~russian Удаляет все делители частоты
	EVENT_HANDLER0(void, clearDelimiters) { delims.clear(); }

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

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

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

	//! \fn void tickEvent(int delimiter)
	//! \brief
	//! \~english Raised on each timer tick and on configured delimiter ticks.
	//! \~russian Вызывается на каждом тике таймера и на настроенных тиках делителей.
	//! \~\details
	//! \~english
	//! "delimiter" is the frequency delimiter, 1 for the main loop.
	//! \~russian
	//! "delimiter" - делитель частоты, 1 для основного цикла.
	EVENT1(tickEvent, int, delimiter);

	//! \}

protected:
	struct PIP_EXPORT Delimiter {
		Delimiter(std::function<void(int)> func_ = nullptr, int delim_ = 1) {
			func  = std::move(func_);
			delim = delim_;
		}
		std::function<void(int)> func;
		int delim = 0;
		int tick  = 0;
	};

	void initFirst();
	void init() const;

	void threadFunc();
	void adjustTimes();
	void execTick();

	//! \~english Virtual tick method. The main loop passes delimiter 1, additional delimiters pass their own value.
	//! \~russian Виртуальный метод тика. Основной цикл передает делитель 1, дополнительные делители передают свое значение.
	virtual void tick(int delimiter) {}

	PIThread * thread        = nullptr;
	std::atomic_bool lockRun = {false}, callEvents = {true};
	PIMutex mutex_;
	PISystemTime m_interval, m_interval_x5;
	PISystemTime m_time_next;
	std::function<void()> ret_func          = nullptr;
	std::function<void(int)> ret_func_delim = nullptr;
	PIVector<Delimiter> delims;
	PIConditionVariable event;
};


#endif // PITIMER_H
