/* -*- mia-c++  -*-
 *
 * This file is part of MIA - a toolbox for medical image analysis 
 * Copyright (c) Leipzig, Madrid 1999-2016 Gert Wollny
 *
 * MIA is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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 the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with MIA; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef mia_3d_stackdisttrans_hh
#define mia_3d_stackdisttrans_hh

#include <vector>
#include <mia/3d/defines3d.hh>
#include <mia/3d/vector.hh>
#include <mia/2d/image.hh>
#include <mia/core/iohandler.hh>
#include <mia/core/ioplugin.hh>

NS_MIA_BEGIN

/**
   \ingroup misc
   \brief A distance transform that stores distances to a 3D binary mask given as slices.  

   This class implements a 3D distance transform that makes it possible to 
   evaluate the distance of each point in a 3D volume to a binary mask 
   that is given by a set of 2D binary images that are interpreted like a 
   3D volume. 
   It implements the Techical Report 
     Pedro F. Felzenszwalb and Daniel P. Huttenlocher
     Distance Transforms of Sampled Functions
     Cornell Computing and Information Science TR2004-1963
*/

class EXPORT_3D C2DStackDistanceTransform: public CIOData {
public: 
	/// helper type for IO plugin handling 
	typedef C2DStackDistanceTransform type; 

	/// search path component for the plugin handling 
	static const char * const data_descr; 
	
	/// search path component for the plugin handling 
	static const char * const type_descr; 


	/**
	   Return type for the result of the evaluated distances
	 */
	struct DistanceFromPoint {

		/**
		   Initializing constructor for the distance type 
		   \param _point location 
		   \param _distance of the point to whatever is measured 
		 */
		DistanceFromPoint(C3DBounds _point, float _distance):
			point(_point), 
			distance(_distance) 
			{}; 
		C3DBounds point; /*!< location of the point */
		float distance;  /*!< distance of the point to the mask */
	}; 

	
	C2DStackDistanceTransform() = default; 

	/**
	   The copy constructor 
	 */
	C2DStackDistanceTransform(const C2DStackDistanceTransform& /*other*/) = default; 

	/**
	   Create a copy of this object 
	 */
	C2DStackDistanceTransform *clone() const __attribute__((warn_unused_result)); 

	/**
	   Construct the distance grid and initialize it
	   \param slice first image of the image stack 
	   \param voxel_size size of a voxel
	 */

	C2DStackDistanceTransform(const C2DImage& slice, const C3DFVector& voxel_size = C3DFVector::_1);
	
	/**
	   Read the given slice of the image stack and update the distance maps. 
	   \param slice the mask data 
	   \param q the z-position of the slice
	 */

	void read( const C2DImage& slice, int q); 
	

	/**
	   Evaluate the distances for a given input mask. For each mask point in image 
	   that is set to thrue the distance is evaluated. 
	   \param s z-location of the slice 
	   \param image the actual mask 
	*/
	std::vector<DistanceFromPoint> get_slice(size_t s, const C2DImage& image) const; 

	/**
	   Save the distcane transform to a file. 
	   \param filename The name of the file to save the transform to 
	   \return true if sucessful
	*/
	bool save(const std::string& filename); 

private: 	
	
	float d(float fq, float q, float fv, float v)const; 
	
	void dt1d(std::vector<float>& f, float scale)const; 
	void dt2d(C2DFImage& image)const; 
	
	struct SParabola {
		int k; 
		int v; 
		float z;
		float fv; 
	};
	
	C2DBounds m_size; 
	C3DFVector m_voxel_size; 
	std::vector<int> m_k; 
	std::vector< std::vector<SParabola> > m_zdt;
	
};

/**
   Write a point-distance pair 
   \param os output stream 
   \param v the poin-distance 
   \returns the output stream 
*/

inline std::ostream& operator << (std::ostream& os, const C2DStackDistanceTransform::DistanceFromPoint& v) 
{
	os << "[" << v.point << ":" << v.distance << "]"; 
	return os; 
}

inline bool operator == (const C2DStackDistanceTransform::DistanceFromPoint& lhs, 
			 const C2DStackDistanceTransform::DistanceFromPoint& rhs) 
{
	return (lhs.point == rhs.point) && (lhs.distance == rhs.distance);
}


/// Base class for the generic IO of transformations 
typedef TIOPlugin<C2DStackDistanceTransform> C2DStackDistanceTransformIO; 

/// Plug-in handler for the transformation IO plug-ins 
typedef THandlerSingleton< TIOPluginHandler<C2DStackDistanceTransformIO> > C2DStackDistanceTransformIOPluginHandler;

/// data key type for temporary storage of 3D transformations \sa CDatapool 
typedef C2DStackDistanceTransformIOPluginHandler::Instance::DataKey C2DStackDistanceTransformIODataKey;

NS_MIA_END

#endif
