/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
***************************************************************************/
#if defined(GLM_EXPORT_ALEMBIC)

#pragma once

#include <glmString.h>
#include <glmArray.h>
#include <glmVector3.h>
#include <glmPODArray.h>
#include <glmMap.h>

#include <vector>

#include "glmCrowdIOAPI.h"
#include "glmAlembic.h"

namespace glm
{
	namespace crowdio
	{
		class GLM_CROWDIO_API PVData
		{
		public:
			PVData();
			~PVData();
			void clear();
			bool empty() const;

			/// Per vertex additionnal data
			GlmMap<GlmString, std::vector<float>> _floatData;
			GlmMap<GlmString, std::vector<Imath::V3f>> _vectorData;
			GlmMap<GlmString, std::vector<Imath::C3f>> _colorData;
		};

		// Defines an alembic polymesh. It's a compound of samples that share the same number of vertices, indices, polygons, and count of vertices per face.
		class GLM_CROWDIO_API AlembicPolymesh
		{
		public:

			// Inner class that stores the vertices, normals et uvs for a sample of polymesh
			class GLM_CROWDIO_API Sample
			{
			public:
				Sample(unsigned int vertexCount, unsigned int normalCount, unsigned int uvCount);

				virtual ~Sample();

				/**
				* \brief Returns the array of vertices
				*/
				const ::Alembic::AbcGeom::Abc::float32_t* getVertices() const;

				/**
				* \brief Returns the array of normals
				*/
				const ::Alembic::AbcGeom::Abc::float32_t* getNormals() const;

				/**
				* \brief Returns the array of UVs
				*/
				const ::Alembic::AbcGeom::Abc::float32_t* getUVs() const;

				/**
				* \brief Returns the array of vertices
				*/
				::Alembic::AbcGeom::Abc::float32_t* touchVertices();

				/**
				* \brief Returns the array of normals
				*/
				::Alembic::AbcGeom::Abc::float32_t* touchNormals();

				/**
				* \brief Returns the array of UVs
				*/
				::Alembic::AbcGeom::Abc::float32_t* touchUVs();

				/**
				* \brief Sets the global transformation matrix
				*/
				void setGlobalMatrix(const ::Alembic::AbcGeom::Abc::M44d &mat);

				/**
				* \brief Returns the global transformation matrix
				*/
				::Alembic::AbcGeom::Abc::M44d getGlobalMatrix() const;

				/**
				* \brief Returns the array of userdata values (sorted in the same order than user data names in the Polymesh)
				*/
				glm::PODArray<float>& touchFloatUserDataValues();
				glm::Array<Vector3>& touchVectorUserDataValues();

				/**
				* \brief Deletes inner data
				*/
				void release();

			private:

				/// The vertices
				::Alembic::AbcGeom::Abc::float32_t* _vertices;

				/// The normals
				::Alembic::AbcGeom::Abc::float32_t* _normals;

				/// The UVs
				::Alembic::AbcGeom::Abc::float32_t* _uvs;

				/// The global transformation matrix
				::Alembic::AbcGeom::Abc::M44d _globalMatrix;

				/// Userdata
                glm::PODArray<float> _floatUserDataValues;
                glm::Array<Vector3> _vectorUserDataValues;
			};

			//************************************************************
			/*! @name Constructor & Destructor
			*/ //*********************************************************
			//@{
			/**
			* \brief Constructor
			*/
			AlembicPolymesh(const GlmString& fullPathname, const GlmString& shadingGroupNames,
				const glm::Array<glm::GlmString>& floatUserDataNames, const glm::Array<glm::GlmString>& vectorUserDataNames, 
				unsigned int nbVertices, unsigned int nbIndices, 
				unsigned int nbNormalIndices, bool normalsPerControlPoint, 
				unsigned int nbUVIndices, bool uvsPerControlPoint, 
				unsigned int nbPolygons, unsigned int nbNormals, unsigned int nbUVs);

			/**
			* \brief Destructor
			*/
			virtual ~AlembicPolymesh();
			//@}

			//************************************************************
			/*! @name Accessors
			*/ //*********************************************************
			//@{

			/**
			* \brief Returns the array of vertex indices
			*/
			const ::Alembic::AbcGeom::Abc::int32_t* getVertexIndices() const;
			const ::Alembic::AbcGeom::Abc::uint32_t* getNormalIndices(); // not const, can create the uint array of vertex indices
			const ::Alembic::AbcGeom::Abc::uint32_t* getUVIndices(); // not const, can create the uint array of vertex indices

			/**
			* \brief Returns the array of per face vertex count
			*/
			const ::Alembic::AbcGeom::Abc::int32_t* getPerFaceCount() const;

			::Alembic::AbcGeom::Abc::int32_t* touchVertexIndices();
			::Alembic::AbcGeom::Abc::uint32_t* touchNormalIndices();
			::Alembic::AbcGeom::Abc::uint32_t* touchUVIndices();

			/**
			* \brief Returns the array of per face vertex count
			*/
			::Alembic::AbcGeom::Abc::int32_t* touchPerFaceCount();

			unsigned int getNbVertices() const { return _nbVertices; }
			unsigned int getNbNormals() const { return _nbNormals; }
			unsigned int getNbUVs() const { return _nbUVs; }
			unsigned int getNbIndices() const;
			unsigned int getNbNormalIndices() const;
			unsigned int getNbUVIndices() const;
			unsigned int getNbPolygons() const;

			//const GlmString& getName() const;

			unsigned int getNbSamples() const;
			void addNewSample();
			const Sample* getSampleAt(unsigned int iSample);
			Sample* touchSampleAt(unsigned int iSample);
			Sample* getLastSample();
			
			/**
			* \brief Gets the list of Samples
			*/
			const glm::PODArray<Sample*> getSamples() const;

			/**
			* \brief Touches the list of Samples
			*/
			glm::PODArray<Sample*> touchSamples();

			/**
             * \brief Touches per vertex data
             */
			glm::Array<std::vector<float>>& touchFloatPVDataValues();
            glm::Array<std::vector<Imath::V3f>>& touchPVUserDataValues();

			//************************************************************
			/*! @name Data Members
			*/ //*********************************************************
			//@{

			// the parent hierarchy such as /body/left_hand/name
			GlmString _fullPathName;

			/// the shading group names
			GlmString _shadingGroupNames;

			/// the names of the user data
			glm::Array<glm::GlmString> _floatUserDataNames;
			glm::Array<glm::GlmString> _vectorUserDataNames;

			/// The number of vertices
			unsigned int _nbVertices;
			unsigned int _nbNormals;
			unsigned int _nbUVs;

			/// The number of indices
			unsigned int _nbIndices;
			unsigned int _nbNormalIndices;
			unsigned int _nbUVIndices;
			
			/// The number of polygons
			unsigned int _nbPolygons;

			bool _normalsPerControlPoint;
			bool _uvsPerControlPoint;

			/// The array of per face vertex count
			::Alembic::AbcGeom::Abc::int32_t* _perFaceCount;

			/// The array of vertex indices
			::Alembic::AbcGeom::Abc::int32_t* _vertexIndices;
			::Alembic::AbcGeom::Abc::uint32_t* _vertexIndicesUINT; // Alembic stores int vertex indices, but uint normal and uv indices... so let's duplicate when normal / uv per control point needed
			::Alembic::AbcGeom::Abc::uint32_t* _normalIndices;
			::Alembic::AbcGeom::Abc::uint32_t* _uvIndices;

			/// The array of Samples
			glm::PODArray<Sample*> _samples;

			/// Per vertex additionnal data
            PVData _pvData;

			/// The indice of the current sample
			int _currentSampleIndex;
			//@}
		};
	}
}

#endif // GLM_EXPORT_ALEMBIC
