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

	Program:		Range Imaging Toolkit (RITK)
	Module:			ritkFrameGrabber.cxx
	Version:		$Revision: 2734 $
	Date:			$Date: 2012-03-07 13:17:13 +0100 (Mi, 07 Mrz 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 "ritkFrameGrabber.h"

#include "ritkDebugManager.h"

namespace ritk
{

//----------------------------------------------------------------------------
FrameGrabber::FrameGrabber()
{
	SetFilterMode(NONE);
	SetCopyMode(DEEP);
	//SetCopyMode(SHALLOW);
}


//----------------------------------------------------------------------------
FrameGrabber::~FrameGrabber()
{
}


//----------------------------------------------------------------------------
void
FrameGrabber::DoFiltering()
{
}


//----------------------------------------------------------------------------
void
FrameGrabber::GenerateData()
{
	StartTimer();

	// Copy and paste from RImageFilterWrapper. This is necessary to support generic images
	
	// Update the input. 
	typedef itk::ImageSource<InputImageType> SourceType;
	InputImageConstPointer OldInput = this->GetInput();
	SourceType::Pointer Source = dynamic_cast<SourceType*>((itk::ProcessObject*)this->GetInput()->GetSource());
	if ( Source && this->GetInput() != Source->GetOutput() )
		this->SetInput(Source->GetOutput());

#if 0
	// Update the output. Do this always
	//if ( !this->GetOutput() || strcmp(this->GetOutput()->GetNameOfClass(), this->GetInput()->GetNameOfClass()) != 0 )
	{
		// Hold handle to the old output
		OutputImagePointer OldOutput = this->GetOutput();

		// Set the output to match the input type
		OutputImageType::Pointer Output = dynamic_cast<OutputImageType*>((itk::LightObject*)this->GetInput()->CreateAnother());
		if ( !Output )
		{
			itkExceptionMacro(<<"Could not cast to output image type!");
		}
		/*else
		{
			std::cout << "Cloning input..." << std::endl;
		}*/
		this->ProcessObject::SetNthOutput(0, Output.GetPointer());

		// Remember old output. In particular, this will set the source of output #1 (= the old output) to ourself.
		this->ProcessObject::SetNthOutput(1,OldOutput);

		// Set the regions. Using the old input ensures correct region propagation trough the pipeline!
		this->GetOutput()->SetLargestPossibleRegion(OldInput->GetLargestPossibleRegion());
		this->GetOutput()->SetBufferedRegion(OldInput->GetBufferedRegion());
		this->GetOutput()->SetRequestedRegion(OldInput->GetRequestedRegion());
	}
#else

	// If we have no output or the output type mismatches the input type clear the old pool. A new pool is automaticall generated/resized later
	if ( !this->GetOutput() || strcmp(this->GetOutput()->GetNameOfClass(), this->GetInput()->GetNameOfClass()) != 0 )
	{
		m_RImagePool.clear();
	}

	// Find an available image in our pools
	bool ImageFound = false;
	while ( !ImageFound )
	{
		// Loop over the current pool
		for ( unsigned int i = 0; i < m_RImagePool.size(); i++ )
		{
			// Check the reference count
			int rc = m_RImagePool[i]->GetReferenceCount();
			//std::cout << "ImagePool[" << i << "] " << rc << std::endl;
			if ( rc == 1 )
			{
				// Hold handle to the old output
				OutputImagePointer OldOutput = this->GetOutput();

				// Set our output
				this->ProcessObject::SetNthOutput(0, m_RImagePool[i].GetPointer());

				// Remember old output. In particular, this will set the source of output #1 (= the old output) to ourself.
				this->ProcessObject::SetNthOutput(1,OldOutput);

				// Update our flag
				ImageFound = true;
				break;
			}
		}

		// Found no unused image -> Create/Resize the current pool
		if ( !ImageFound )
		{
			int OldSize = m_RImagePool.size();
			m_RImagePool.resize(OldSize+4, NULL);
			LOG_DEB("Resizing pool: N=" << m_RImagePool.size());
			for ( int i = 0; i < OldSize; i++ )
				std::cout << "RC for pool elem#" << i << ": " << m_RImagePool[i]->GetReferenceCount() << std::endl;

			for ( unsigned int i = OldSize; i < m_RImagePool.size(); i++ )
			{
				// Create output image type to match the input
				OutputImageType::Pointer tmp = dynamic_cast<OutputImageType*>((itk::LightObject*)this->GetInput()->CreateAnother());
				if ( !tmp )
				{
					itkExceptionMacro(<<"Could not clone input image type!");
				}

				// Set the regions and allocate the image (explicitely allocate the world coord sub image)
				tmp->SetLargestPossibleRegion(GetInput()->GetLargestPossibleRegion());
				tmp->SetBufferedRegion(GetInput()->GetBufferedRegion());
				tmp->SetRequestedRegion(GetInput()->GetRequestedRegion());
				tmp->Allocate();
				tmp->GetWorldCoordImage()->Allocate();

				// Add the image to the pool
				m_RImagePool[i] = tmp;
			}
		}
	}
#endif

	
	// Synchronize before calling the superclass' implementation
	InputImagePointer NonConstInput = const_cast<InputImageType*>(GetInput());
	if ( NonConstInput )
	{
		NonConstInput->SynchronizeGenericImportImageContainers();
	}
	else
		itkExceptionMacro(<<"Could not cast to const input image!");
	
	// Now copy the data
	if ( m_CopyMode == DEEP )
	{
		this->GetOutput()->DeepCopy(this->GetInput());
	}
	else
	{
		this->GetOutput()->Graft(this->GetInput());
	}

	// Stop timer
	StopTimer();


	double Time[2];
	this->GetElapsedTime(&Time[0], &Time[1]);
	//std::cout << "Elapsed time during memory relocator update: " << Time[0] << " (" << Time[1] << ")" << std::endl;
}


} // end namespace ritk
