#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <iostream>
#include <queue>
 
class Logger // singleton class
{
public:
	Logger() : mThread{}, mCV{}, mMutex{}, mQueue{}, mStop{ false }
	{
		mThread = std::thread(&Logger::Run, this);
	}
 
	~Logger()
	{
		if (mThread.joinable())
		{
			mThread.join();
		}
	}
 
	static Logger& getInstance() {
		static Logger logger;
		return logger;
	}
 
	void Stop()
	{
		{
		    std::lock_guard<std::mutex> lk(mMutex);
		    mStop = true;
		}
		mCV.notify_one();
	}
 
	void WriteMessage(const std::string& msg)
	{
		{
		    std::lock_guard<std::mutex> lk(mMutex);
		    mQueue.push(msg);
		}
		mCV.notify_one();
	}
 
private:
	void Run()
	{
		while (true)
		{
			std::unique_lock<std::mutex> lock(mMutex);
			mCV.wait(lock, [&]() { return mStop || !mQueue.empty(); });
 
			//Stop if needed
			if (mStop)
			{
				std::cout << "--- Stopping ---" << std::endl;
				break;
			}
 
			std::string msg = std::move(mQueue.front());
			mQueue.pop();
			lock.unlock();
 
			std::cout << msg << std::endl;
		}
	}
 
private:
	std::thread mThread;
	std::condition_variable mCV;
	std::mutex mMutex;
	std::queue<std::string> mQueue;
	bool mStop;
};
 
 
int main()
{
    Logger& logger = Logger::getInstance();
 
    std::thread t1([&logger, count = 10]() mutable {
        while (count > 0)
        {
            logger.WriteMessage("Hello from thread1");
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            --count;
        }
    });
 
    std::thread t2([&logger, count = 20]() mutable {
        while (count > 0)
        {
            logger.WriteMessage("        Hello from thread2");
            std::this_thread::sleep_for(std::chrono::milliseconds(250));
            --count;
        }
    });
 
    t1.join();
    t2.join();
    logger.Stop();
 
}