/* -LICENSE-START-
** Copyright (c) 2019 Blackmagic Design
**  
** Permission is hereby granted, free of charge, to any person or organization 
** obtaining a copy of the software and accompanying documentation (the 
** "Software") to use, reproduce, display, distribute, sub-license, execute, 
** and transmit the Software, and to prepare derivative works of the Software, 
** and to permit third-parties to whom the Software is furnished to do so, in 
** accordance with:
** 
** (1) if the Software is obtained from Blackmagic Design, the End User License 
** Agreement for the Software Development Kit (“EULA”) available at 
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
** 
** (2) if the Software is obtained from any third party, such licensing terms 
** as notified by that third party,
** 
** and all subject to the following:
** 
** (3) the copyright notices in the Software and this entire statement, 
** including the above license grant, this restriction and the following 
** disclaimer, must be included in all copies of the Software, in whole or in 
** part, and all derivative works of the Software, unless such copies or 
** derivative works are solely in the form of machine-executable object code 
** generated by a source language processor.
** 
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
** DEALINGS IN THE SOFTWARE.
** 
** A copy of the Software is available free of charge at 
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
** 
** -LICENSE-END-
*/

#pragma once

#include <condition_variable>
#include <memory>
#include <mutex>
#include <queue>


template<typename T>
class SampleQueue
{
public:
	SampleQueue();
	virtual ~SampleQueue();

	void						pushSample(const T& sample);
	void						pushSample(T&& sample);
	bool						popSample(T& sample);
	bool						waitForSample(T& sample);
	void						cancelWaiters(void);
	void						reset(void);

private:
	std::queue<T>				m_queue;
	std::condition_variable		m_queueCondition;
	std::mutex					m_mutex;
	bool						m_waitCancelled;
};

template<typename T>
SampleQueue<T>::SampleQueue() :
	m_waitCancelled(false)
{
}

template<typename T>
SampleQueue<T>::~SampleQueue()
{
	cancelWaiters();
}

template<typename T>
void SampleQueue<T>::pushSample(const T& sample)
{
	{
		std::lock_guard<std::mutex> lock(m_mutex);
		m_queue.push(sample);
	}
	m_queueCondition.notify_one();
}

template<typename T>
void SampleQueue<T>::pushSample(T&& sample)
{
	{
		std::lock_guard<std::mutex> lock(m_mutex);
		m_queue.push(std::move(sample));
	}
	m_queueCondition.notify_one();
}

template<typename T>
bool SampleQueue<T>::popSample(T& sample)
{
	// Non-blocking queue pop
	std::lock_guard<std::mutex> lock(m_mutex); 
	if (m_queue.empty())
		return false;

	sample = std::move(m_queue.front());
	m_queue.pop();

	return true;
}

template<typename T>
bool SampleQueue<T>::waitForSample(T& sample)
{
	// Blocking wait for sample
	std::unique_lock<std::mutex> lock(m_mutex);
	m_queueCondition.wait(lock, [&] { return !m_queue.empty() || m_waitCancelled; });

	if (m_waitCancelled)
		return false;	
	else if (!m_queue.empty())
	{
		sample = std::move(m_queue.front());
		m_queue.pop();
	}
	return true;
}

template<typename T>
void SampleQueue<T>::cancelWaiters()
{
	{
		// signal cancel flag to terminate wait condition
		std::lock_guard<std::mutex> lock(m_mutex);
		m_waitCancelled = true;
	}
	m_queueCondition.notify_all();
}

template<typename T>
void SampleQueue<T>::reset(void)
{
	std::lock_guard<std::mutex> lock(m_mutex);
	while (!m_queue.empty())
		m_queue.pop();
	m_waitCancelled = false;
}
