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

	Program:		Range Imaging Toolkit (RITK)
	Module:			ritkRImageFilePlayer.cxx
	Version:		$Revision: 2918 $
	Date:			$Date: 2012-04-04 17:12:47 +0200 (Mi, 04 Apr 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 "ritkRImageFilePlayer.h"
#include "ritkDebugManager.h"

namespace ritk
{

//----------------------------------------------------------------------------
RImageFilePlayer::RImageFilePlayer()
{
	// Initialize members
	m_Loop = true;
	m_RImageFileSource = RImageFileSourceType::New();
	m_CurrentFrameID = 0;
	m_PlayerWidget = 0;

	// Default state
	SetState(ClosedState);

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

	// Create the widget
	m_PlayerWidget = new RImageFilePlayerWidget(0);

	// Connect signals from widget
	connect(m_PlayerWidget,		SIGNAL(PauseEvent()),						this,			SLOT(Pause())					);
	connect(m_PlayerWidget,		SIGNAL(StopEvent()),						this,			SLOT(Stop())					);
	connect(m_PlayerWidget,		SIGNAL(PlayBackwardEvent()),				this,			SLOT(PlayBackward())			);
	connect(m_PlayerWidget,		SIGNAL(PlayForwardEvent()),					this,			SLOT(PlayForward())				);
	connect(m_PlayerWidget,		SIGNAL(StepBackwardEvent()),				this,			SLOT(StepBackward())			);
	connect(m_PlayerWidget,		SIGNAL(StepForwardEvent()),					this,			SLOT(StepForward())				);
	connect(m_PlayerWidget,		SIGNAL(GoToFrameEvent(unsigned long)),		this,			SLOT(GoToFrame(unsigned long))	);
	connect(m_PlayerWidget,		SIGNAL(ToggleLoopEvent(bool)),				this,			SLOT(ToggleLoop(bool))			);
	connect(m_PlayerWidget,		SIGNAL(OpenFileEvent(QString)),				this,			SLOT(SetFileName(QString))		);
	connect(m_PlayerWidget,		SIGNAL(ToggleTimestampDeliveryEvent(bool)),	this,			SLOT(ToggleTimestampDelivery(bool)));
	connect(m_PlayerWidget,		SIGNAL(FrameIntervalEvent(int)),			this,			SLOT(SetFrameInterval(int)));

	// Connect signals from us
	connect(this,				SIGNAL(UpdateGUI()),						m_PlayerWidget,	SLOT(UpdateGUI())				);
	connect(this,				SIGNAL(OpenFileEvent(QString)),				this,			SLOT(SetFileName(QString))		);

	// Use timestamps by default
	m_TimestampDeliveryFlag = true;
	// Set default frame interval
	m_FrameInterval = 1;
	m_PlayerWidget->m_LineEditInterval->setText(QString::number(m_FrameInterval));

	m_ValidityFlag = true;
}


//----------------------------------------------------------------------------
RImageFilePlayer::~RImageFilePlayer()
{
	///TBD Close the reader

	{
		QMutexLocker locker(&m_ValidityFlagMutex);
		m_ValidityFlag = false;
		if ( m_Timer )
		{
			m_Timer->stop();
			delete m_Timer;
		}
	}
}


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


//----------------------------------------------------------------------------
RImageFilePlayer::SourcePlugin::SourcePointer
RImageFilePlayer::GetSource()
{
	return (SourceType*) m_RImageFileSource;
}



//----------------------------------------------------------------------------
void
RImageFilePlayer::SetState(FileInterfaceStateType State)
{
	// Lock
	{
		QMutexLocker locker(&m_StateMutex);

		//LOG_DEB(<<"Switching state from " << m_State << " to " << State);
		m_State = State;

		// Update the widget (nothing to do for stepping forward or backward)
		if ( m_PlayerWidget )
		{
			switch(m_State)
			{
			case ClosedState:
				m_PlayerWidget->SetState(RImageFilePlayerWidget::ClosedState);
				break;
			case OpenedState:
				m_PlayerWidget->SetFileName(m_FileName);				
				m_PlayerWidget->SetNumberOfFrames(m_RImageFileSource->GetNumberOfFrames());				
				unsigned int SX;
				unsigned int SY;
				m_RImageFileSource->GetResolution(&SX,&SY);
				m_PlayerWidget->SetImageResolution(SX,SY);
				m_PlayerWidget->SetState(RImageFilePlayerWidget::OpenedState);
				break;
			case PlayForwardState:
				m_PlayerWidget->SetState(RImageFilePlayerWidget::PlayForwardState);
				break;
			case PlayBackwardState:
				m_PlayerWidget->SetState(RImageFilePlayerWidget::PlayForwardState);
				break;
			};
			emit UpdateGUI();
		}
	}
}


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


//----------------------------------------------------------------------------
void
RImageFilePlayer::SetFileName(QString FileName)
{
	// Stop the player
	if ( GetState() != ClosedState )
		Stop();
	
	m_FileName = FileName;

	// Update the file reader
	m_AcquisitionPipeline->Lock();
	bool Result = true;
	try
	{
		m_RImageFileSource->SetFileName(m_FileName.toStdString().c_str());
		m_RImageFileSource->Update();
	}
	catch ( itk::ExceptionObject &e )
	{
		Result = false;
		LOG_DEB("Could not open file: " << e.what());
	}
	m_AcquisitionPipeline->Unlock();

	// Update state
	if ( Result )
		SetState(OpenedState);
	else 
		SetState(ClosedState);

	// Reset the pipeline as the region probably changed
	m_AcquisitionPipeline->ResetPipeline();

	// Reset the frame index and request the first frame
	m_CurrentFrameID = 0;
	m_PlayerWidget->SetFrameID(m_CurrentFrameID);
	RequestFrame();

	return;
}


//----------------------------------------------------------------------------
QString 
RImageFilePlayer::GetFileName()
{
	return m_FileName;
}


//----------------------------------------------------------------------------
unsigned long
RImageFilePlayer::GetCurrentFrameID() const
{
	return m_CurrentFrameID;
}


//----------------------------------------------------------------------------
bool 
RImageFilePlayer::ValidFileName()
{
	if(m_FileName.isEmpty())
	{
		return false;
	}
	else
	{
		return true;
	}	
}


//----------------------------------------------------------------------------
void 
RImageFilePlayer::ToggleLoop(bool Enable)
{
	if ( m_Loop == Enable )
		return;

	m_Loop = Enable;

	return;
}


//----------------------------------------------------------------------------
void 
RImageFilePlayer::ToggleTimestampDelivery(bool TimestampDeliveryFlag)
{
	LockPlugin();

	// Nothing changed => return
	if ( m_TimestampDeliveryFlag == TimestampDeliveryFlag )
	{
		UnlockPlugin();
		return;
	}

	// Update frame requests
	/*if ( m_TimestampDeliveryFlag )
	{
		m_Timer->stop();
		disconnect(m_Timer, SIGNAL(timeout()), this, SLOT(PlayEvent()));
		connect(m_AcquisitionPipeline, SIGNAL(NewFrame()), this, SLOT(PlayEvent()) );
	}
	else
	{
		disconnect(m_AcquisitionPipeline, SIGNAL(NewFrame()), this, SLOT(PlayEvent()));
		connect(m_Timer, SIGNAL(timeout()), this, SLOT(PlayEvent()));
	}*/

	// Update our flag
	m_TimestampDeliveryFlag = TimestampDeliveryFlag;


	UnlockPlugin();

	// Trigger execution
	//PlayEvent();

	return;
}


//----------------------------------------------------------------------------
void 
RImageFilePlayer::Pause()
{
	LockPlugin();

	m_Timer->stop();
	SetState(OpenedState);

	UnlockPlugin();

	return;
}


//----------------------------------------------------------------------------
void 
RImageFilePlayer::PlayBackward()
{
	LockPlugin();

	SetState(PlayBackwardState);

	UnlockPlugin();

	// Trigger execution
	PlayEvent();
}


//----------------------------------------------------------------------------
void 
RImageFilePlayer::PlayForward()
{
	LockPlugin();

	SetState(PlayForwardState);

	UnlockPlugin();

	// Trigger execution
	PlayEvent();

	return;
}


//----------------------------------------------------------------------------
void 
RImageFilePlayer::StepBackward()
{
	// Check for the beginning of the sequence
	if ( m_CurrentFrameID == 0 )
	{
		if ( !m_Loop )
		{
			m_Timer->stop();
			SetState(OpenedState);
			return;
		}
		m_CurrentFrameID = m_RImageFileSource->GetNumberOfFrames()-1;
	}
	else
		m_CurrentFrameID--;
	RequestFrame();
}


//----------------------------------------------------------------------------
void 
RImageFilePlayer::StepForward()
{
	// Check for the end of the sequence
	if ( m_CurrentFrameID == m_RImageFileSource->GetNumberOfFrames()-1 )
	{
		if ( !m_Loop )
		{
			m_Timer->stop();
			SetState(OpenedState);
			return;
		}
		m_CurrentFrameID = 0;
	}
	else
		m_CurrentFrameID++;
	RequestFrame();
}


//----------------------------------------------------------------------------
void 
RImageFilePlayer::Stop()
{
	m_Timer->stop();
	m_CurrentFrameID = 0;
	m_PlayerWidget->SetFrameID(m_CurrentFrameID);
	SetState(OpenedState);

	return;
}


//----------------------------------------------------------------------------
void
RImageFilePlayer::GoToFrame(unsigned long ID)
{
	m_Timer->stop();
	m_CurrentFrameID = ID;

	RequestFrame();

	// TODO: Check for invalid frame
	// Atm the player will restart with index 0
	// if an invalid ID is encountered


	return;
}


//----------------------------------------------------------------------------
void
RImageFilePlayer::PlayEvent()
{
	// Stop the timer
	m_Timer->stop();

	// Measure the time for one step
	QTime Timer;
	Timer.start();

	// Get the timestamp for the current frame
	typedef RImageFileSourceType::RImageType::TimeStampType TimeStampType;
	TimeStampType CurrentTimeStamp = 1000*m_RImageFileSource->GetTimeStamps()[m_CurrentFrameID];
	TimeStampType NextTimeStamp = CurrentTimeStamp+1;

	// The number of frames in the current sequence
	unsigned int NumFrames = m_RImageFileSource->GetTimeStamps().size();

	// Depending on the state we step forward or backward and have to set the timer correctly
	if ( GetState() == PlayForwardState )
	{
		// Compute next timestamp
		if ( m_CurrentFrameID == NumFrames-1 )
			NextTimeStamp = CurrentTimeStamp + 40;
		else
			NextTimeStamp = 1000*m_RImageFileSource->GetTimeStamps()[m_CurrentFrameID+1];
		
		// Perform one step
		StepForward();
	}
	else if ( GetState() == PlayBackwardState )
	{
		// Compute next timestamp
		if ( m_CurrentFrameID == 0)
			NextTimeStamp = CurrentTimeStamp + 40;
		else
			NextTimeStamp = 1000*m_RImageFileSource->GetTimeStamps()[m_CurrentFrameID-1];

		// Perform one step
		StepBackward();
	}
	else
	{
		// This may happen when tsd is disabled
		//std::cout << "Invalid state detected..." << std::endl;
	}

	// Get the elapsed time for the performed step
	int Elapsed = Timer.elapsed();

	// Restart the timer
	if ( m_TimestampDeliveryFlag )
	{	
		int TargetInterval = NextTimeStamp-CurrentTimeStamp;
		int ActualInterval = TargetInterval-Elapsed;
		/*std::cout << "Interval check" << std::endl;
		std::cout << "	Target interval: " << TargetInterval << std::endl;
		std::cout << "	Elapsed time: " << Elapsed << std::endl;
		std::cout << "	Actual interval: " << (TargetInterval-Elapsed) << " (" << std::max(ActualInterval,1) << ")" << std::endl;*/
		m_Timer->start(std::max(ActualInterval,1));
	}
	else
	{
		//std::cout << "Starting with: " << m_FrameInterval << std::endl;
		m_Timer->start(m_FrameInterval);
	}
}


//----------------------------------------------------------------------------
void 
RImageFilePlayer::RequestFrame()
{
	// Sanity check
	if ( GetState() == ClosedState )
	{
		LOG_DEB("Frame requested but file is not opened");
		return;
	}

	// 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) stop timer (if running). Otherwise execute the pipeline
	if ( !Started() )
	{
		Stop();
	}
	else
	{
		// Request the frame
		m_AcquisitionPipeline->Lock();
		m_RImageFileSource->SetFrameIndex(m_CurrentFrameID);
		m_AcquisitionPipeline->Unlock();

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

	// Done here, unlock
	UnlockPlugin();	
}


//----------------------------------------------------------------------------
void
RImageFilePlayer::NewFrame()
{
	// Return if RImageFilePlayer does no more exist
	{
		QMutexLocker locker(&m_ValidityFlagMutex);
		if(!m_ValidityFlag)
			return;
	}

	m_PlayerWidget->SetFrameID(m_CurrentFrameID);
	emit UpdateGUI();
}


//----------------------------------------------------------------------------
void 
RImageFilePlayer::SetFrameInterval(int Interval)
{
	m_FrameInterval = Interval;
}


//----------------------------------------------------------------------------
void
RImageFilePlayer::SetParameter(QString Name, QString Value)
{	
	if (!strcmp(Name.toStdString().c_str(), "File"))
	{
		emit SetFileName(Value);
	}
}


} // end namespace ritk
