#include "PMDCamCubeSourcePlugin.h"

#include "ritkManager.h"
#include "ritkDebugManager.h"
#include "ritkRImageIOManager.h"

#include <ctime>


PMDCamCubeSourcePlugin::PMDCamCubeSourcePlugin()
{
	// Create reader
	m_CameraSource = SourceType::New();

	// Initialize timer
	m_Timer = new QTimer();		
	m_Timer->stop();
	connect(m_Timer, SIGNAL(timeout()), SLOT(RequestFrame()));

	m_PlayerWidget = 0;

	// Default state: not connected
	SetState(DisconnectedState);

	// Create widget and connect signals
	m_PlayerWidget = new PMDCamCubeSourcePluginWidget(0);
	connect(m_PlayerWidget,		SIGNAL(ConnectEvent()),							this,				SLOT(Connect())									);
	connect(m_PlayerWidget,		SIGNAL(DisconnectEvent()),						this,				SLOT(Disconnect())								);
	connect(m_PlayerWidget,		SIGNAL(PauseEvent()),							this,				SLOT(PauseLive())								);
	connect(m_PlayerWidget,		SIGNAL(StopEvent()),							this,				SLOT(StopLive())								);
	connect(m_PlayerWidget,		SIGNAL(PlayEvent()),							this,				SLOT(PlayLive())								);
	connect(m_PlayerWidget,		SIGNAL(StepForwardEvent()),						this,				SLOT(StepForwardLive())							);
	connect(m_PlayerWidget,		SIGNAL(IntegrationTimeEvent(int)),				this,				SLOT(SetIntegrationTime(int))					);
	connect(m_PlayerWidget,		SIGNAL(SourceDataAveragingEvent(bool)),			this,				SLOT(AverageSourceData(bool))					);
	connect(m_PlayerWidget,		SIGNAL(NumFramesToAverageSourceDataEvent(int)),	this,				SLOT(SetNumberOfFramesToAverageSourceData(int))	);
	connect(this,				SIGNAL(UpdateGUI()),							m_PlayerWidget,		SLOT(UpdateGUI())								);

	// Register the PMDRImage
	try
	{
		LOG_DEB("Registering PMDCamCubeRImageIO...");
		m_PMDCamCubeRImageIO = PMDCamCubeRImageIOF2::New();
		ritk::RImageIOManager<ritk::RImageF2>::GetInstance().RegisterIOObject(m_PMDCamCubeRImageIO);
	}
	catch ( itk::ExceptionObject &e )
	{
		LOG_DEB("Could not register PMDCamCubeRImageIO for the following reason: " << e);
	}
}


PMDCamCubeSourcePlugin::~PMDCamCubeSourcePlugin()
{
	// Disconnect reader
	if ( GetState() == ConnectedState )
		m_CameraSource->Disconnect();
	
	// Delete the timer
	if ( m_Timer )
		delete m_Timer;

	// Unregister the PMDRImage
	LOG_DEB("Unregistering PMDCamCubeRImageIO...");
	ritk::RImageIOManager<ritk::RImageF2>::GetInstance().UnRegisterIOObject(m_PMDCamCubeRImageIO);
	m_PMDCamCubeRImageIO = NULL;

	delete m_PlayerWidget;
}


//----------------------------------------------------------------------------
QWidget*
PMDCamCubeSourcePlugin::GetPluginGUI()
{
	return m_PlayerWidget;
}


//----------------------------------------------------------------------------
PMDCamCubeSourcePlugin::SourcePlugin::SourcePointer
PMDCamCubeSourcePlugin::GetSource()
{
	return (SourcePlugin::SourceType*)m_CameraSource;
}


//----------------------------------------------------------------------------
void
PMDCamCubeSourcePlugin::SetState(CamInterfaceStateType State)
{
	// Lock
	{
		QMutexLocker locker(&m_StateMutex);
		m_State = State;

		// Update widget
		if ( m_PlayerWidget )
		{
			switch ( m_State )
			{
			case DisconnectedState:
				m_PlayerWidget->SetState(PMDCamCubeSourcePluginWidget::DisconnectedState);
				break;
			case ConnectingState:
				m_PlayerWidget->SetState(PMDCamCubeSourcePluginWidget::ConnectingState);
				break;
			case ConnectedState:
				m_PlayerWidget->SetState(PMDCamCubeSourcePluginWidget::ConnectedState);
				break;
			case PlayLiveState:
				m_PlayerWidget->SetState(PMDCamCubeSourcePluginWidget::PlayLiveState);
				break;
			}
			emit UpdateGUI();
		}
	}
}


//----------------------------------------------------------------------------
PMDCamCubeSourcePlugin::CamInterfaceStateType 
PMDCamCubeSourcePlugin::GetState()
{
	// Lock
	{
		QMutexLocker locker(&m_StateMutex);
		return m_State;
	}
}


//----------------------------------------------------------------------------
void
PMDCamCubeSourcePlugin::Connect()
{
	SetState(ConnectingState);

	//QMessageBox::information(m_PlayerWidget,tr(""),tr("Connecting to camera..."));


	// Create the job and connect to slot
	ConnectToCamJob *job = new ConnectToCamJob(m_CameraSource);
	connect(
		job, SIGNAL(Finished(bool,QString)),
		this,SLOT(ConnectionResult(bool,QString)));

	// Add
	ritk::Manager::GetInstance().AddThreadedJob(job);

	m_ConnectMessageBox = new QMessageBox(m_PlayerWidget);
	m_ConnectMessageBox->setText("Connecting to camera...");
	m_ConnectMessageBox->setStandardButtons(QMessageBox::NoButton);
	//ConnectMessage->setWindowFlags(Qt::Popup);
	m_ConnectMessageBox->setIcon(QMessageBox::Information);
	m_ConnectMessageBox->exec();

	SetIntegrationTime(250);
}


//----------------------------------------------------------------------------
void
PMDCamCubeSourcePlugin::Disconnect()
{
	// Disconnect
	bool Result = true;
	try
	{
		m_CameraSource->Disconnect();
	}
	catch ( itk::ExceptionObject &e )
	{
		LOG_DEB("Disconnect failed: " << e.what());
		Result = false;
	}

	// Update state
	if ( Result )
		SetState(DisconnectedState);
}


//----------------------------------------------------------------------------
void
PMDCamCubeSourcePlugin::SetIntegrationTime(int value)
{
	if ( value <= 50 )
	{
		LOG_DEB("Invalid value set...");
		return;
	}

	LockPlugin();
	m_AcquisitionPipeline->Lock();
	m_CameraSource->SetIntegrationTime(value);
	m_AcquisitionPipeline->Unlock();
	UnlockPlugin();
}


//----------------------------------------------------------------------------
void
PMDCamCubeSourcePlugin::AverageSourceData(bool b)
{
	m_AcquisitionPipeline->Lock();
	m_CameraSource->AverageSourceData(b);
	m_AcquisitionPipeline->Unlock();
}


//----------------------------------------------------------------------------
void
PMDCamCubeSourcePlugin::SetNumberOfFramesToAverageSourceData(int val)
{
	m_AcquisitionPipeline->Lock();
	m_CameraSource->SetNumberOfFramesToAverageSourceData(val);
	m_AcquisitionPipeline->Unlock();
}


//----------------------------------------------------------------------------
void
PMDCamCubeSourcePlugin::ConnectionResult(bool Result, QString Message)
{
	LOG_DEB("Connection result: " << Result << " (" << Message.toStdString() << ")");

	// Update the state of the player
	switch ( GetState() )
	{
	case ConnectingState:
		if ( Result )
			SetState(ConnectedState);
		else
		{
			SetState(DisconnectedState);
			delete m_ConnectMessageBox;
			m_ConnectMessageBox = new QMessageBox(m_PlayerWidget);
			m_ConnectMessageBox->setIcon(QMessageBox::Critical);
			m_ConnectMessageBox->setText("Connection failed!");
			m_ConnectMessageBox->setInformativeText(Message);
			m_ConnectMessageBox->setStandardButtons(QMessageBox::Ok);
			m_ConnectMessageBox->exec();
		}
		break;
	}
	delete m_ConnectMessageBox;
}


//----------------------------------------------------------------------------
void
PMDCamCubeSourcePlugin::RequestFrame()
{
	// Lock the player as some members may be changed from outside (especially when the player is stopped)
	LockPlugin();

	// If we're not started (propably stopped from outside) hide the scene dialog and stop time (if running). Otherwise execute the pipeline
	if ( !Started() )
	{
		StopLive();
	}
	else
	{
		m_AcquisitionPipeline->Lock();

		// We need to modify the source
		m_Source->Modified();

		m_AcquisitionPipeline->Unlock();

		// Trigger pipeline execution (other thread)
		m_AcquisitionPipeline->ExecuteOnce();
	}

	// Done here, unlock
	UnlockPlugin();	
}


//----------------------------------------------------------------------------
void
PMDCamCubeSourcePlugin::NewFrame()
{
	emit UpdateGUI();
}


//----------------------------------------------------------------------------
void
PMDCamCubeSourcePlugin::StepForwardLive()
{
	RequestFrame();

	return;
}


//----------------------------------------------------------------------------
void
PMDCamCubeSourcePlugin::PlayLive()
{
	SetState(PlayLiveState);
	m_Timer->start(10);

	return;
}


//----------------------------------------------------------------------------
void 
PMDCamCubeSourcePlugin::PauseLive()
{
	m_Timer->stop();
	SetState(ConnectedState);

	return;
}


//----------------------------------------------------------------------------
void 
PMDCamCubeSourcePlugin::StopLive()
{
	m_Timer->stop();
	SetState(ConnectedState);

	return;
}



//----------------------------------------------------------------------------
// CONNECT TO CAMERA 
//----------------------------------------------------------------------------

ConnectToCamJob::ConnectToCamJob(PMDCamCubeSourcePlugin::SourcePointer Source)
{
	m_Source = Source;
}


ConnectToCamJob::~ConnectToCamJob()
{
}


//----------------------------------------------------------------------------
void
ConnectToCamJob::Execute()
{
	if(	m_Source->Connect())
	{
		emit Finished(true);
	}
	else
	{
		emit Finished(false, "Error occurred while connecting. Check the log.");
	}
}






