/* Timer.cpp, (Created on 30.03.2024) */

/* Copyright (C) 2011-2024 Michael Lugmair
 *
 * This file is part of Sayonara Player
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.

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

#include "Utils/Logger/Logger.h"

#include <chrono>
#include <thread>

namespace Utils
{
	struct Timer::Private
	{
		std::atomic_bool mayRun {false};
		std::atomic_bool isRunning {false};
		std::thread thread;
	};

	Timer::Timer() :
		m {Pimpl::make<Private>()} {}

	Timer::~Timer() noexcept
	{
		stop();
	}

	void Timer::start(const std::chrono::milliseconds interval, const int iterations, Callback&& callback)
	{
		if(isActive())
		{
			stop();
		}

		m->mayRun = true;

		// you cannot detach the thread because it accesses members of Timer
		m->thread = std::thread([this, callback = std::move(callback), interval, iterations]() {
			m->isRunning = true;

			for(auto i = 0; (i < iterations) && m->mayRun; i++)
			{
				std::this_thread::sleep_for(interval);
				const auto mayLive = callback();
				if(!mayLive)
				{
					break;
				}
			}

			m->mayRun = false;
			m->isRunning = false;
			spLog(Log::Verbose, "Thread") << "Thread finished";
		});
	}

	void Timer::stop()
	{
		m->mayRun = false;
		spLog(Log::Verbose, this) << "Stopping...";

		try
		{
			if(m->thread.joinable())
			{
				m->thread.join();
			}
		}
		catch(std::exception& e)
		{
			spLog(Log::Warning, this) << e.what();
		}
	}

	bool Timer::isActive() const { return m->isRunning; }
} // Utils