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

	Program:		Range Imaging Toolkit (RITK)
	Module:			ritkRImageVTKData.cxx
	Version:		$Revision: 1761 $
	Date:			$Date: 2011-09-23 17:25:41 +0200 (Fr, 23 Sep 2011) $

	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 "ritkRImageVTKData.h"

#include <itkRescaleIntensityImageFilter.h>
#include <itkThresholdImageFilter.h>
#include <itkScalarImageToHistogramGenerator.h>
#include <vtkObjectFactory.h>

//#include <vtkPoints.h>
//#include <vtkCellArray.h>
#include <vtkPointData.h>
#include <vtkProperty.h>
#include <vtkPolyDataWriter.h>


namespace ritk
{

//----------------------------------------------------------------------------
// Needed when we don't use the vtkStandardNewMacro.
vtkInstantiatorNewMacro(RImageVTKData);


//----------------------------------------------------------------------------
RImageVTKData* RImageVTKData::New()
{
	// First try to create the object from the vtkObjectFactory
	vtkObject* ret = vtkObjectFactory::CreateInstance("RImageVTKData");
	if( ret )
		return static_cast<RImageVTKData*>(ret);
	return new RImageVTKData();
}


//----------------------------------------------------------------------------
RImageVTKData::RImageVTKData()
{
	m_MeshInitialized = false;
	m_PolyDataType = RImageVTKData::POINTCLOUD;

	m_Points = vtkSmartPointer<vtkPoints>::New();
}


RImageVTKData::~RImageVTKData()
{
}


//----------------------------------------------------------------------------
void
RImageVTKData::GeneratePolyData(const RImageType *Image, bool UseRImageFlag)
{
	RImageType::WorldCoordImageConstPointer WC = Image->GetWorldCoordImage();
	if ( WC->GetPixelContainer()->Size() == 0 )
	{
		std::cerr << "Invalid frame!";
		throw std::exception();
	}

	// Create VTK points out of the point cloud
	
	typedef itk::ImageRegionConstIterator<RImageType::WorldCoordImageType> WorldCoordIteratorType;
	typedef itk::ImageRegionConstIterator<RImageType::FlagImageType> FlagIteratorType;
	WorldCoordIteratorType itWC(Image->GetWorldCoordImage(),Image->GetWorldCoordImage()->GetRequestedRegion());
	FlagIteratorType itF(Image->GetFlagImage(),Image->GetFlagImage()->GetRequestedRegion());
	long id = 0;
	for ( itWC.GoToBegin(),itF.GoToBegin(); !itWC.IsAtEnd(); ++itWC,++itF )
	{
		// If we already have a mesh, we simply modify the point
		if ( !m_MeshInitialized )
		{
			//if ( itF.Value() )
				m_Points->InsertPoint(id,itWC.Value()[0],itWC.Value()[1],itWC.Value()[2]);
		}
		else
		{		
			//if ( itF.Value() )
				m_WorldCoordPolyData->GetPoints()->SetPoint(id,itWC.Value()[0],itWC.Value()[1],itWC.Value()[2]);
		}
		id++;
    }

	// We don't have a valid mesh, create one
	if ( !m_MeshInitialized )
	{
		unsigned int Width  = Image->GetRangeImage()->GetRequestedRegion().GetSize(0);
		unsigned int Height = Image->GetRangeImage()->GetRequestedRegion().GetSize(1);

		// Create topology of point cloud. This is necessary!
		vtkSmartPointer<vtkCellArray> Conn = vtkSmartPointer<vtkCellArray>::New();
		vtkSmartPointer<vtkPolyData> Poly = vtkSmartPointer<vtkPolyData>::New();
		switch(m_PolyDataType)
		{
			case POINTCLOUD:
				for ( vtkIdType i = 0; i < m_Points->GetNumberOfPoints(); i++ )
				{
					Conn->InsertNextCell(1, &i);
				}
				Poly->SetPoints(m_Points);
				Poly->SetVerts(Conn);
				m_WorldCoordPolyData = Poly;				
				break;

			case TRIANGLESTRIPS:
				for (unsigned int j = 0; j < Height-1; j++)
				{
					Conn->InsertNextCell(Width*2); // 2 times width???
					for (unsigned int i = 0; i < Width; i++)
					{
						Conn->InsertCellPoint(j*Width+i);
						Conn->InsertCellPoint(j*Width+i+Width); // 2 times width???
					}
				}
				Poly->SetPoints(m_Points);
				Poly->SetStrips(Conn);
				m_WorldCoordPolyData = Poly;
				break;

			case DELAUNAYTRIANGLES:
				// Perform delaunay triangulation
				Poly->SetPoints(m_Points);
				vtkSmartPointer<vtkDelaunay2D> Delaunay = vtkSmartPointer<vtkDelaunay2D>::New();
				Delaunay->SetInput(Poly);
				Delaunay->Update();
				//m_WorldCoordPolyData->DeepCopy(Delaunay->GetOutput());
				m_WorldCoordPolyData = Delaunay->GetOutput();
				break;
		}
		m_MeshInitialized = true;
	}
	else
	{
		// Create topology of point cloud. This is necessary!
		vtkSmartPointer<vtkCellArray> Conn = vtkSmartPointer<vtkCellArray>::New();
		vtkSmartPointer<vtkPolyData> Poly = vtkSmartPointer<vtkPolyData>::New();
		switch(m_PolyDataType)
		{
			case DELAUNAYTRIANGLES:
				// Perform delaunay triangulation
				Poly->SetPoints(m_Points);
				vtkSmartPointer<vtkDelaunay2D> Delaunay = vtkSmartPointer<vtkDelaunay2D>::New();
				Delaunay->SetInput(Poly);
				Delaunay->Update();
				//m_WorldCoordPolyData->DeepCopy(Delaunay->GetOutput());
				m_WorldCoordPolyData = Delaunay->GetOutput();
				break;
		}
	}
}


//----------------------------------------------------------------------------
void
RImageVTKData::SetPolyDataType(PolyDataType Type)
{
	if ( Type == m_PolyDataType )
		return;

	m_PolyDataType = Type;
	m_MeshInitialized = false;	

	return;
}


//----------------------------------------------------------------------------
vtkSmartPointer<vtkPolyData>
RImageVTKData::GetPolyData()
{
	return m_WorldCoordPolyData;
}


} // end namespace ritk
