/*======================================================================================

	Program:		Range Imaging Toolkit (RITK)
	Module:			ritkThreadedEventProcessor.cxx
	Version:		$Revision: 2602 $
	Date:			$Date: 2012-02-20 00:09:18 +0100 (Mo, 20 Feb 2012) $

	Copyright (c) 2010-2011
	Pattern Recognition Lab, 
    University Erlangen-Nuremberg, Germany
    All rights reserved.

	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 RITKCopyright.txt for more information.

	http://www5.informatik.uni-erlangen.de/research/software/ritk

========================================================================================*/

#include "ritkThreadedEventProcessor.h"

#include "ritkDebugManager.h"

namespace ritk
{


//----------------------------------------------------------------------------
ThreadedEventProcessor::ThreadedEventProcessor()
{
	m_ShutdownFlag = true;
	m_SingleEventInstanceMode = true;
}


//----------------------------------------------------------------------------
ThreadedEventProcessor::~ThreadedEventProcessor()
{
	//std::cout << "Destructor called for ThreadedEventProcessor@" << this << "..." << std::endl;
	QMutexLocker locker(&m_ShutdownFlagMutex);

	// If the processor was not shutdown do it now. However, this won't propagate to subclasses
	if ( !m_ShutdownFlag )
	{
		std::stringstream ss;
		ss << "Warning: ThreadedEventProcessor@" << this << " not shutdown properly. Use the Shutdown() method!";
		LOG_DEB(ss.str().c_str());
		std::cout << ss.str() << std::endl;

		m_ShutdownFlag = true;

		// Enqueue the stop event. This will cause the thread's endloess loop to terminate
		EnqueueEvent(StopEvent::Pointer(new StopEvent()));
	}

	// Wait for actual thread termination
    wait();
}


//----------------------------------------------------------------------------
void 
ThreadedEventProcessor::SetSingleEventInstanceMode(bool b)
{
	// Critical section
    QMutexLocker locker(&m_Mutex);
	m_SingleEventInstanceMode = b;
}


//----------------------------------------------------------------------------
bool 
ThreadedEventProcessor::EnqueueEvent(Event::Pointer Event)
{
	// Critical section
    QMutexLocker locker(&m_Mutex);
	
	// Check for multiple event instances
	if ( m_SingleEventInstanceMode )
	{
		for ( int i = 0; i < m_Events.size(); i++ )
		{
			if ( m_Events[i]->type() == Event->type() )
			{
				return false;
			}
		}
	}

	// Enqueue the event, wake the processing thread and return success
    m_Events.enqueue(Event);
    m_EventAdded.wakeOne();
	return true;
}


//----------------------------------------------------------------------------
void
ThreadedEventProcessor::Shutdown()
{
	QMutexLocker locker(&m_ShutdownFlagMutex);

	// Enqueue the stop event. This will cause the thread's endloess loop to terminate
	EnqueueEvent(StopEvent::Pointer(new StopEvent()));

	m_ShutdownFlag = true;

	wait();
}


//----------------------------------------------------------------------------
void 
ThreadedEventProcessor::run()
{
	// No proper shutdown by default
	m_ShutdownFlag = false;

	// Initialize the thread handle
	m_ThreadHandle = QThread::currentThreadId();

	// The current event to process
	Event::Pointer e;

	LOG_DEB("ThreadedEventProcessor started. ThreadHandle=" << m_ThreadHandle);

	// Loop
    while ( true )
	{
        {
			// Wait for new event
            QMutexLocker locker(&m_Mutex);
            if ( m_Events.isEmpty() )
			{
                m_EventAdded.wait(&m_Mutex);
			}
            e = m_Events.dequeue();


			// Termination criteria
			if ( e->type() == StopEvent::EventType )
			{
				m_Events.clear();
				if ( !m_ShutdownFlag )
					ProcessEvent(e);
                break;
			}
        }

		// Process the event in subclass
		ProcessEvent(e);

		// Reassign smartpointer. This is a mandatory step in order to free ressources immediately after execution.
		e = Event::Pointer();
    }
	//std::cout << "Shutting down processor@" << this << std::endl;
}


//----------------------------------------------------------------------------
Qt::HANDLE
ThreadedEventProcessor::GetThreadHandle()
{
	return m_ThreadHandle;
}


} // end namespace ritk
