#ifndef MITOGEN_TOOLBOX_BP_LISTS_H
#define MITOGEN_TOOLBOX_BP_LISTS_H

#include <vector>
#include <i3d/image3d.h>
#include <i3d/resolution.h>

#include "../settings.h"

/**
* Resets the input \e BPList as well as \e BPCentre such that it represents
* boundary of the object given in the \e mask. The coordinates of points
* are given in microns with respect to the \e mask resolution and offset.
* Depending on the last parameter, a pixel in the \e mask image represents
* an object if its value is >0 (when \e ID == 0) or if its value is \e ID
* (when \e ID > 0).
*
* The input mask image must not contain holes.
*
* To prevent from holes (empty voxels) in the rendered mask images, which happens
* due to poor sampling of boundary points after several transformation of these,
* besides the mask voxel themselves, their half-pixel distant "buddies" are added
* into the \e BPList as well. Currently, up to 3 buddies, one along x,y, and z-axis,
* respectively, are added.
*
* We create the list \e BPList by eroding \e mask by one line to obtain
* outer boundary points, the remaining pixels are considered inner boundary points.
* All points are ofcourse added to the \e BPList, the outer ones are added first.
* How many outer points were added to the \e BPList is signalled in the return value.
* The distinction between inner and outer points may be used to optimize
* some functions, e.g. collision detection in which only the outer points
* can be considered.
* 
* \param[out] BPList		list of boundary points to be created
* \param[out] BPCentre		geometric central position of the created list
* \param[in] mask		source mask image
* \param[in] ID			what ID to look for in the \e mask
*
* \return The function returns the number of outer boundary points in the newly
* created list.
*/
template <class MV>
size_t ResetBoundaryPoints(std::vector< i3d::Vector3d<float> /**/> &BPList,
			   i3d::Vector3d<float> &BPCentre,
			   i3d::Image3d<MV> const &mask,
			   const MV ID=0);

/**
* Moves the \e BPList according to the vector flow field \e FF
* and recalculates the \e BPCentre afterwards.
*
* \param[in,out] BPList		list of boundary points to be updated
* \param[in,out] BPCentre	geometric central position of the created list
* \param[in] FF			flow field to move the points in \e BPList
*/
void MoveBPListAndCentre(std::vector< i3d::Vector3d<float> /**/> &BPList,
			 i3d::Vector3d<float> &BPCentre,
			 FlowField<float> const &FF);

/**
* Moves the \e BPList according to the given vector
* and recalculates the \e BPCentre afterwards.
*
* \param[in,out] BPList		list of boundary points to be updated
* \param[in,out] BPCentre	geometric central position of the created list
* \param[in] shift			vector to move the points in \e BPList
*/
void MoveBPListAndCentre(std::vector< i3d::Vector3d<float> /**/> &BPList,
			 i3d::Vector3d<float> &BPCentre,
			 const i3d::Vector3d<float> &shift);

/**
 * Fills in the \e mask image with \e value using the list of boundary points \e BPList,
 * which are drawn into the image using the nearest neighborhood pixel. After the points
 * are drawn into an empty image, it is morphologically closed with 6-neighborhood 3D
 * structuring element. The result is submitted, or "projected", to the \e mask using pixels
 * with value \e value.
 *
 * The \e mask image must be initialized already. It is not tested whether we are not
 * overwritting something in the \e mask nor values are added to existing pixel values.
 *
 * Of course, the resolution and offset of the \e mask is acknowledged.
 *
 * \param[in,out] mask		render mask into this image
 * \param[in] value		"colour" of the rendered mask
 * \param[in] BPList		list of boundary points to outline the mask
 * \param[in] BPCentre		deprecated and ignored parameter, waits for code cleaning 
 * \param[in] OuterBP		deprecated and ignored parameter, waits for code cleaning 
 */
template <class MV>
void RenderBPListIntoMask(i3d::Image3d<MV> &mask, const MV value,
			  std::vector< i3d::Vector3d<float> /**/> const &BPList,
			  i3d::Vector3d<float> const &BPCentre,
			  const int OuterBP=-1);

/**
 * Just like the RenderBPListIntoMask() but every point is first translated
 * according to the given flow field \e FF.
 *
 * \param[in,out] mask		render mask into this image
 * \param[in] value		"colour" of the rendered mask
 * \param[in] BPList		list of boundary points to outline the mask
 * \param[in] BPCentre		deprecated and ignored parameter, waits for code cleaning 
 * \param[in] OuterBP		deprecated and ignored parameter, waits for code cleaning 
 * \param[in] FF		vector flow field
 */
template <class MV>
void RenderBPListIntoMask(i3d::Image3d<MV> &mask, const MV value,
			  std::vector< i3d::Vector3d<float> /**/> const &BPList,
			  i3d::Vector3d<float> const &BPCentre,
			  const int OuterBP,
			  FlowField<float> const &FF);

/**
 * This function is essentially the RenderBPListIntoMask() with the only difference
 * that it first looks where it draws. If the place to be filled is already occupied,
 * i.e. value is not 0, nothing is drawn there. Everything else is the same as the
 * RenderBPListIntoMask() function.
 *
 * Of course, the resolution and offset of the \e mask is acknowledged.
 *
 * \param[in,out] mask		render mask into this image
 * \param[in] value		"colour" of the rendered mask
 * \param[in] BPList		list of boundary points to outline the mask
 * \param[in] BPCentre		deprecated and ignored parameter, waits for code cleaning 
 * \param[in] OuterBP		deprecated and ignored parameter, waits for code cleaning 
 */
template <class MV>
void SoftRenderBPListIntoMask(i3d::Image3d<MV> &mask, const MV value,
			  std::vector< i3d::Vector3d<float> /**/> const &BPList,
			  i3d::Vector3d<float> const &BPCentre,
			  const int OuterBP=-1);

/**
 * Creates the \e mask image and initialize it with zeros.
 * The \e mask image must, however, \b have \b already \b set its resolution.
 *
 * The function updates offset and size of the \e mask in order to centre
 * the object, given with boundary points \e BPList, within the image and
 * to provide some extra room around it. The width of this "extra boundary"
 * is a sum of the two following widths.
 *
 * The first width of this "extra boundary" on one side is \e perX times
 * the object width and the height of it on one side is \e perY times
 * the object height, etc. For example, the width of the image afterwards
 * will be 2 times this width of the "extra boundary" plus the width of
 * the object. This boundary should accomodate cell shape changes.
 *
 * The second width to include in the "extra boundary" sum is a minimum
 * required width to accomodate motion of cell in the coming \e noFrames
 * time points. The xy-plane is considered separately from the z-axis.
 * The width and height in the xy-plane is computed as the greater from cell
 * width or height plus 2 * (\e noFrames+0.1) times the constant
 * cellLookDistance from constants.h (note that cellLookDistance is hard
 * maximum length of translational movement of a cell between frame to frame).
 * The width of "extra boundary" along the z-axis is \e noFrames+0.1 times
 * 1/resolution_in_z_axis (i.e. the width of one pixel in microns). This is
 * because a cell is allowed to move max 1px in z-direction between frames
 * in the Cell::ScmUniversalMovement() function.
 *
 * The pixel grid of the created \e mask should be aligned with the pixel
 * grid of the scene images. Therefore, the \b offset of the scene images
 * \b should \b be \b copied in the offset of this \e mask image \b beforehand.
 * \b Also, the \b resolution of the \e mask \b must \b be already \b set in it.
 * Basically, one just \b copies \b offset \b and \b resolution of the scene
 * image before calling this function.
 *
 * This function is expected to be called prior any cell cycle phase
 * simulation function with extra room such that movement of cell within
 * the phase still fits into the mask image.
 *
 * \param[in,out] mask		render mask into this image
 * \param[in] BPList		list of boundary points to outline the mask
 * \param[in] noFrames		for how many frames the created mask should exist
 * \param]in] cellLookDistance	how far the cell can see its neighbourhood
 * \param[in] perX		relative width of the vertical extra space
 * \param[in] perY		relative height of the horizontal extra space
 * \param[in] perZ		relative thickness of the extra slices
 */
template <class MV>
void AllocateAndZeroNewMask(i3d::Image3d<MV> &mask,
			    std::vector< i3d::Vector3d<float> /**/> const &BPList,
			    const size_t noFrames,
				 const float cellLookDistance,
			    const float perX=0.2f,
			    const float perY=0.2f,
			    const float perZ=0.2f);

/**
 * Prepares the \e mask image with AllocateAndZeroNewMask() and then
 * fills it with RenderBPListIntoMask(). Owing to the former function,
 * the output image is initialized with zeros beforehand.
 *
 * The pixel grid of the created \e mask should be aligned with the pixel
 * grid of the scene images. Therefore, the \b offset of the scene images
 * \b should \b be \b copied in the offset of this \e mask image \b beforehand.
 * \b Also, the \b resolution of the \e mask \b must \b be already \b set in it.
 * Basically, one just \b copies \b offset \b and \b resolution of the scene
 * image before calling this function.
 *
 * This function is expected to be called prior any cell cycle phase
 * simulation function with extra room such that movement of cell within
 * the phase still fits into the mask image.
 *
 * \param[in,out] mask		render mask into this image
 * \param[in] value		"colour" of the rendered mask
 * \param[in] BPList		list of boundary points to outline the mask
 * \param[in] BPCentre		deprecated and ignored parameter, waits for code cleaning 
 * \param[in] OuterBP		deprecated and ignored parameter, waits for code cleaning 
 * \param[in] noFrames		for how many frames the created mask should exist
 * \param[in] cellLookDistance		how far the cell canwatch its neighbourhood
 * \param[in] perX		relative width of the vertical extra space
 * \param[in] perY		relative height of the horizontal extra space
 * \param[in] perZ		relative thickness of the extra slices
 */
template <class MV>
void RenderBPListIntoNewMask(i3d::Image3d<MV> &mask, const MV value,
			  std::vector< i3d::Vector3d<float> /**/> const &BPList,
			  i3d::Vector3d<float> const &BPCentre,
			  const int OuterBP,
			  const size_t noFrames,
			  const float cellLookDistance,
			  const float perX=0.2f,
			  const float perY=0.2f,
			  const float perZ=0.2f);

#endif
