#pragma once
#include<mutex>
#include<condition_variable>
#include<list>
#include<iostream>

using namespace std;
 
template<class T> class SyncQueue
{
public:
	SyncQueue(int max_size);
	~SyncQueue();
	void put(const T& val);
	void take(T& val);
	void clear();
	bool isEmpty();
	bool isFull();
	int count();
public:
	string name;
private:
	mutex lock;
	condition_variable_any cv_full, cv_empty;
	list<T> q;
	int size;
	int max_size;
};

using namespace std;

template<class T>
SyncQueue<T>::SyncQueue(int max_size) :max_size(max_size)
{
}

template<class T>
SyncQueue<T>::~SyncQueue()
{
}

template<class T>
void SyncQueue<T>::put(const T& val)
{
	lock_guard<mutex> locker(lock);
	while (isFull())
	{
		//cout << "\nQueue " << name << " is full now, wait a minute!" << endl;
		cv_full.wait(lock);
	}
	q.emplace_back(val);
	cv_empty.notify_one();
	//cout << "   Put image success  " << endl;
}

template<class T>
void SyncQueue<T>::take(T& val)
{
	lock_guard<mutex> locker(lock);
	while (isEmpty())
	{
		//cout << "\nQueue "<< name << " is empty now, wait a minute!" << endl;
		cv_empty.wait(lock);
	}
	val = q.front();
	q.pop_front();
	cv_full.notify_one();
	//cout << "   Take image success  "<< endl;
}

template<class T>
void SyncQueue<T>::clear()
{
	lock_guard<mutex> locker(lock);
	q.clear();
}

template<class T>
bool SyncQueue<T>::isEmpty()
{
	return q.size() == 0;
}

template<class T>
bool SyncQueue<T>::isFull()
{
	return q.size() == max_size;
}

template<class T>
int SyncQueue<T>::count()
{
	lock_guard<mutex> locker(lock);
	return q.size();
}