// SPDX-License-Identifier: LGPL-2.0-or-later
// Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI)

#pragma once

#include <vector>

//Local
#include "CCToolbox.h"
#include "CCTypes.h"

namespace CCCoreLib
{
	class GenericIndexedCloudPersist;
	class GenericProgressCallback;
	class DgmOctree;
	class ReferenceCloud;

	//! A standard container to store several subsets of points
	/** Several algorithms of the AutoSegmentationTools toolbox return a collection of subsets of points
		corresponding to each segmented part. Such a collection is generally stored in this type of container.
	**/
	using ReferenceCloudContainer = std::vector<ReferenceCloud *>;

	//! Several point cloud auto-segmentation algorithms (Connected Components, Front propagation, etc.)
	class CC_CORE_LIB_API AutoSegmentationTools : public CCToolbox
	{
	public:

		//! Labels connected components from a point cloud
		/** The algorithm is described in Daniel Girardeau-Montaut's PhD manuscript
			(Chapter 3, section 3.2.4). The main parameter is the maximal distance
			between two points in order to consider them as belonging to the same
			connected component. Actually, this parameter is not expressed as is, but
			as the length of a cell of the octree for a given level of subdivision (this
			leeds to a great enhancement of the process speed). If the level is "n",
			then the cell size will be equal to the maximum length of the bounding box
			of the point cloud divided by 2^n. The greater the level "n" is, the smaller
			the cell size will be, and therefore the process will produce more
			connected components.To label the CCs, this implementation of the algorithm
			use the distance field (via Generic3dPoint::setDist). So be sure to
			store the original distance field (or to deviate the setDist process) if
			you don't want it to be replaced.
			\param theCloud the point cloud to label
			\param level the level of subdivision of the octree (between 1 and MAX_OCTREE_LEVEL)
			\param sixConnexity indicates if the CC's 3D connexity should be 6 (26 otherwise)
			\param progressCb the client application can get some notification of the process progress through this callback mechanism (see GenericProgressCallback)
			\param inputOctree the cloud octree if it has already been computed
			\return the number of components (>= 0) or an error code (< 0 - see DgmOctree::extractCCs)
		**/
		static int labelConnectedComponents(GenericIndexedCloudPersist* theCloud,
											unsigned char level,
											bool sixConnexity = false,
											GenericProgressCallback* progressCb = nullptr,
											DgmOctree* inputOctree = nullptr);

		//! Extracts connected components from a point cloud
		/** This method shloud only be called after the connected components have been
			labeled (see AutoSegmentationTools::labelConnectedComponents). This
			implementation of the algorithm assumes that the CCs labels are stored for
			each point in the associated scalar field.
			Warning: be sure to set the labels S.F. as OUTPUT (reading)
			\param theCloud the point cloud to segment
			\param ccc the extracted connected compenents (as a list of subsets of points)
			\return success
		**/
		static bool extractConnectedComponents(GenericIndexedCloudPersist* theCloud, ReferenceCloudContainer& ccc);

		//! Segment a point cloud by propagating fronts constrained by values of the point cloud associated scalar field
		/** The algorithm is described in Daniel Girardeau-Montaut's PhD manuscript
			(Chapter 3, section 3.3). It consists mainly in propagating a front on
			the surface implicitly represented by the point cloud and making this
			propagation dependent on the scalar values associated to each point (such
			as the distance information computed between the point cloud and another
			entity). The propgation is realized with the Fast Marching Algorithm applied
			on a gridded structure (the octree in this case).
			Warning: be sure to activate an OUTPUT scalar field on the input cloud
			\param theCloud the point cloud to segment
			\param minSeedDist the minimum value associated to the point where to start the propagation from ("security" value)
			\param radius spherical neighborhood size (or 0 for automatic size)
			\param octreeLevel level of subdivision where to apply the gridding (the greater it is, the smaller and numerous the segmented parts will be)
			\param theSegmentedLists the segmented parts (as a list of subsets of points)
			\param applyGaussianFilter to specify if a gaussian filter should be applied after computing the scalar field gradient (to smooth the results)
			\param progressCb the client application can get some notification of the process progress through this callback mechanism (see GenericProgressCallback)
			\param inputOctree the cloud octree if it has already be computed
			\param alpha the gaussian filter kernel size (needed only if a gaussian filtering pass is required)
			\return success
		**/
		static bool frontPropagationBasedSegmentation(	GenericIndexedCloudPersist* theCloud,
														PointCoordinateType radius,
														ScalarType minSeedDist,
														unsigned char octreeLevel,
														ReferenceCloudContainer& theSegmentedLists,
														GenericProgressCallback* progressCb = nullptr,
														DgmOctree* inputOctree = nullptr,
														bool applyGaussianFilter = false,
														float alpha = 2.0f);
	};
}
