//##########################################################################
//#                                                                        #
//#                              CLOUDCOMPARE                              #
//#                                                                        #
//#  This program 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; version 2 or later of the License.      #
//#                                                                        #
//#  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.                          #
//#                                                                        #
//#          COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI)             #
//#                                                                        #
//##########################################################################

#ifndef CC_SUB_MESH_HEADER
#define CC_SUB_MESH_HEADER

//Local
#include "ccBBox.h"
#include "ccGenericMesh.h"

class ccMesh;

//! A sub-mesh
/** Equivalent to a CCCoreLib::ReferenceCloud for a mesh
**/
class QCC_DB_LIB_API ccSubMesh : public ccGenericMesh
{
public:

	//! Default constructor
	explicit ccSubMesh(ccMesh* parentMesh);
	//! Destructor
	~ccSubMesh() override = default;

	//! Returns class ID
	CC_CLASS_ENUM getClassID() const override { return CC_TYPES::SUB_MESH; }

	//inherited methods (ccHObject)
	ccBBox getOwnBB(bool withGLFeatures = false) override;
	bool isSerializable() const override { return true; }

	//inherited methods (ccGenericMesh)
	ccGenericPointCloud* getAssociatedCloud() const override;
	void refreshBB() override;
	bool interpolateNormalsBC(unsigned triIndex, const CCVector3d& w, CCVector3& N) override;
	bool interpolateColors(unsigned triIndex, const CCVector3& P, ccColor::Rgb& color) override;
	bool interpolateColorsBC(unsigned triIndex, const CCVector3d& w, ccColor::Rgb& color) override;
	bool interpolateColors(unsigned triIndex, const CCVector3& P, ccColor::Rgba& color) override;
	bool interpolateColorsBC(unsigned triIndex, const CCVector3d& w, ccColor::Rgba& color) override;
	bool getColorFromMaterial(unsigned triIndex, const CCVector3& P, ccColor::Rgba& color, bool interpolateColorIfNoTexture) override;
	bool getVertexColorFromMaterial(unsigned triIndex, unsigned char vertIndex, ccColor::Rgba& color, bool returnColorIfNoTexture) override;
	bool hasMaterials() const override;
	const ccMaterialSet* getMaterialSet() const override;
	int getTriangleMtlIndex(unsigned triangleIndex) const override;
	bool hasTextures() const override;
	TextureCoordsContainer* getTexCoordinatesTable() const override;
	void getTriangleTexCoordinates(unsigned triIndex, TexCoords2D* &tx1, TexCoords2D* &tx2, TexCoords2D* &tx3) const override;
	bool hasPerTriangleTexCoordIndexes() const override;
	void getTriangleTexCoordinatesIndexes(unsigned triangleIndex, int& i1, int& i2, int& i3) const override;
	bool hasTriNormals() const override;
	void getTriangleNormalIndexes(unsigned triangleIndex, int& i1, int& i2, int& i3) const override;
	bool getTriangleNormals(unsigned triangleIndex, CCVector3& Na, CCVector3& Nb, CCVector3& Nc) const override;
	NormsIndexesTableType* getTriNormsTable() const override;
	unsigned capacity() const override;
	void setGlobalShift(const CCVector3d& shift) override { /* this method shouldn't be called on ccSubMesh instances */ assert(false); }
	void setGlobalScale(double scale) override { /* this method shouldn't be called on ccSubMesh instances */ assert(false); }

	//inherited methods (ccDrawableObject)
	bool hasColors() const override;
	bool hasNormals() const override;
	bool hasScalarFields() const override;
	bool hasDisplayedScalarField() const override;
	bool normalsShown() const override;

	//inherited methods (GenericIndexedMesh)
	inline unsigned size() const override { return static_cast<unsigned>(m_triIndexes.size()); }
	void forEach(genericTriangleAction action) override;
	inline void placeIteratorAtBeginning() override { m_globalIterator = 0; }
	CCCoreLib::GenericTriangle* _getNextTriangle() override; //temporary object
	CCCoreLib::GenericTriangle* _getTriangle(unsigned index) override; //temporary object
	CCCoreLib::VerticesIndexes* getNextTriangleVertIndexes() override;
	CCCoreLib::VerticesIndexes* getTriangleVertIndexes(unsigned triangleIndex) override;
	void getTriangleVertices(unsigned triangleIndex, CCVector3& A, CCVector3& B, CCVector3& C) const override;
	void getBoundingBox(CCVector3& bbMin, CCVector3& bbMax) override;
	bool interpolateNormals(unsigned triIndex, const CCVector3& P, CCVector3& N) override;

	//! Returns global index (i.e. relative to the associated mesh) of a given element
	/** \param localIndex local index (i.e. relative to the internal index container)
	**/
	inline unsigned getTriGlobalIndex(unsigned localIndex) const { return m_triIndexes[localIndex]; }

	//! Returns the global index of the triangle pointed by the current element
	inline unsigned getCurrentTriGlobalIndex() const { assert(m_globalIterator < size()); return m_triIndexes[m_globalIterator]; }

	//! Forwards the local element iterator
	inline void forwardIterator() { ++m_globalIterator; }

	//! Clears the mesh
	void clear(bool releaseMemory);

	//! Triangle global index insertion mechanism
	/** \param globalIndex a triangle global index
		\return false if not enough memory
	**/
	bool addTriangleIndex(unsigned globalIndex);

	//! Triangle global index insertion mechanism (range)
	/** \param firstIndex first triangle global index of range
		\param lastIndex last triangle global index of range (excluded)
		\return false if not enough memory
	**/
	bool addTriangleIndex(unsigned firstIndex, unsigned lastIndex);

	//! Sets global index for a given element
	/** \param localIndex local index
		\param globalIndex global index
	**/
	void setTriangleIndex(unsigned localIndex, unsigned globalIndex);

	//! Reserves some memory for hosting the triangle references
	/** \param n the number of triangles (references)
	**/
	bool reserve(size_t n);

	//! Presets the size of the vector used to store triangle references
	/** \param n the number of triangles (references)
	**/
	bool resize(size_t n);

	//! Returns the associated mesh
	inline ccMesh* getAssociatedMesh() { return m_associatedMesh; }

	//! Returns the associated mesh (const version)
	inline const ccMesh* getAssociatedMesh() const { return m_associatedMesh; }

	//! Sets the associated mesh
	/** \param mesh parent mesh
		\param unlinkPreviousOne whether to remove any dependency with the previous parent mesh (if any)
	**/
	void setAssociatedMesh(ccMesh* mesh, bool unlinkPreviousOne = true);

	//! Indexes map for createNewSubMeshFromSelection
	using IndexMap = std::vector<unsigned int>;

	//! Creates a new sub mesh with the visible vertices only
	/** \param removeSelectedTriangles specifies if the faces composed only of 'selected' vertices should be removed or not
		\param selectedTriangleIndexes a map of the triangles indexes that will be used in the new sub msh (or -1 if they are discarded)
		\param newRemainingTriangleIndexes if an index map is provided, it will be used to 'translate' global indexes of triangles remaining in the source sub-mesh
		\return the new sub-mesh if successful
	**/
	ccSubMesh* createNewSubMeshFromSelection(	bool removeSelectedTriangles,
												const std::vector<int>& selectedTriangleIndexes,
												IndexMap* newRemainingTriangleIndexes = nullptr );

	//! Creates a new sub mesh with the selected vertices only
	/** Creates a new sub-mesh structure with the vertices that are tagged as "visible" (see ccGenericPointCloud::visibilityArray).
		\param removeSelectedTriangles specifies if the faces composed only of 'selected' vertices should be removed or not
		\param selectedTriangleIndexes a map of the triangles indexes that will be used in the new sub msh (or -1 if they are discarded)
		\param newRemainingTriangleIndexes if an index map is provided, it will be used to 'translate' global indexes of triangles remaining in the source sub-mesh
		\return the new sub-mesh if successful
	**/
	ccSubMesh* createNewSubMeshFromSelection(bool removeSelectedTriangles, IndexMap* newRemainingTriangleIndexes = nullptr);

protected:

	//inherited from ccHObject
	bool toFile_MeOnly(QFile& out, short dataVersion) const override;
	bool fromFile_MeOnly(QFile& in, short dataVersion, int flags, LoadedIDMap& oldToNewIDMap) override;
	short minimumFileVersion_MeOnly() const override;
	void onUpdateOf(ccHObject* obj) override;

	//! Associated mesh
	ccMesh* m_associatedMesh;

	//! Container of 3D triangles indexes
	using ReferencesContainer = std::vector<unsigned int>;

	//! Indexes of (some of) the associated mesh triangles
	ReferencesContainer m_triIndexes;

	//! Iterator on the triangles references container
	unsigned m_globalIterator;

	//! Bounding-box
	ccBBox m_bBox;
};

#endif //CC_SUB_MESH_HEADER
