#include <atomic>
#include <chrono>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>

std::mutex gMutex;
std::queue<int> gQueue;
std::atomic<bool> gRunThread(true);
std::thread gWorkerThread;

void workerThread();

void driver();

void start();

void addData(int i);

void end();

int main()
{
	std::thread driverThread(&driver);
	driverThread.join();
	
	return 0;
}

void driver()
{
	std::cout << "Starting ...\n";
	std::this_thread::sleep_for(std::chrono::seconds(1));
	start();
	
	std::this_thread::sleep_for(std::chrono::seconds(1));
	for (auto i = 0; i < 5; ++i)
	{
		std::this_thread::sleep_for(std::chrono::seconds(1));
		addData(i);
	}
	
	std::cout << "Ending ...\n";
	std::this_thread::sleep_for(std::chrono::seconds(1));
	end();
}

void workerThread()
{
	while (gRunThread)
	{
		bool isEmpty;
		
		{
			std::lock_guard<std::mutex> lock(gMutex);
			isEmpty = gQueue.empty();
		}
		
		if (isEmpty)
		{
			std::cout << "Waiting for the queue to fill ...\n";
			std::this_thread::sleep_for(std::chrono::seconds(2));
		}
		else
		{
			std::lock_guard<std::mutex> lock(gMutex);
			
			int value = gQueue.front();
			gQueue.pop();
			
			std::cout << "Dequeued: " << value << "\n";
		}
	}
}

void start()
{
	gWorkerThread = std::thread(&workerThread);
}

void addData(int i)
{
	{
		std::lock_guard<std::mutex> lock(gMutex);
		gQueue.push(i);
	}
	
	std::cout << "Queued: " << i << "\n";
}

void end()
{
	gRunThread = false;
	gWorkerThread.join();
}
