/*	Golaem Crowd IO API - v0.00 - Copyright (C) Golaem SA.  All Rights Reserved.

The implementation of this library is in the glmCrowdIO dynamic library redistributed with Golaem Crowd

QUICK NOTES:
Primarily of interest to pipeline developers to generate Golaem Crowd entities geometry

Brief documentation under "DOCUMENTATION" below.

Revision 0.00 (2015-11-09) release notes:

- Functions and structures for creation, read/write and destruction of Golaem Crowd entities geometry
*/
#pragma once

#include <glmCrowdIOAPI.h>

#include <math.h>
#include <float.h>
#include <stdint.h>
#include <stdio.h>
#include <vector>
#include <stdlib.h>
#include <string.h>

// DOCUMENTATION
//
// ===========================================================================
// Golaem Crowd entities geometry generation
//
// Depends on glmHistory.h / glmSimulationData.h / glmFrameData.h to read Golaem Crowd simulation caches
// Also needs the gcha, caa and fbx files
//
// Usage example:
//
//		glm::crowdio::init();
//		GlmGeometryGenerationContext context;
//		memset(&context, 0, sizeof(GlmGeometryGenerationContext));
//		context._frame = 10;
//		context._crowdFieldCount = 2;
//		context._crowdFieldNames = GLM_ALLOC_CAST(char(*)[GIO_NAME_LENGTH], , //		context._crowdFieldNames, glm::MemoryStatIds::Cache)
//		strcpy(context._crowdFieldNames[0], "crowdField1");
//		strcpy(context._crowdFieldNames[1], "crowdField2");
//		strcpy(context._cacheName, "stadium");
//		context._cacheDirectoryCount = 1;
//		context._cacheDirectories = GLM_ALLOC_CAST(char(*)[GIO_NAME_LENGTH], , //		context._cacheDirectories, glm::MemoryStatIds::Cache)
//		strcpy(context._cacheDirectories[0], "/home/username/maya/projects/default/export/stadium/cache");
//		context._characterFilesDirectoryCount = 1;
//		context._characterFilesDirectories = GLM_ALLOC_CAST(char(*)[GIO_NAME_LENGTH], , //		context._characterFilesDirectories, glm::MemoryStatIds::Cache)
//		strcpy(context._characterFilesDirectories[0], "/home/username/Golaem/GolaemCrowdCharacterPack-4.2.0.1/crowd/characters/crowdMan_light.gcha");
//		context._excludedEntityCount = 0;
//		...
//		glmBeginGeometryGeneration(&context);
//		GlmEntityGeometry* geometries = GLM_ALLOC_CAST(GlmEntityGeometry*, , geometries, glm::MemoryStatIds::Cache)
//		for (unsigned int iEntity = 0; iEntity < context._entityCount; ++iEntity)
//		{
//			glmCreateEntityGeometry(&geometries[iEntity], &context, &context._entityBBoxes[iEntity]);
//		}
//		...
//		for (unsigned int iEntity = 0; iEntity < context._entityCount; ++iEntity)
//		{
//			GlmEntityBoundingBox* bbox = &context._entityBBoxes[iEntity];
//			printf("Entity ID: %u \n", bbox->_entityId);
//			GlmEntityGeometry* geometry = &geometries[iEntity];
//			printf("Number of meshes: %u \n", geometry->_meshCount);
//			for (unsigned int iMesh = 0; iMesh < geometry->_meshCount; ++iMesh)
//			{
//				GlmMesh* mesh = &geometry->_meshes[iMesh];
//				printf("Mesh name: %s \n", mesh->_name);
//				printf("Mesh vertice count: %u \n", mesh->_verticeCount);
//				for (unsigned int iFrame = 0; iFrame < context._frameToProcessCounts[bbox->_crowdFieldIndex]; ++iFrame)
//				{
//					printf("For frame: %u \n", context._framesToProcess[bbox->_crowdFieldIndex][iFrame]);
//					printf("First vertice: [%f, %f, %f] \n", mesh->_vertices[iFrame][0][0], mesh->_vertices[iFrame][0][1], mesh->_vertices[iFrame][0][2]);
//				}
//			}
//		}
//		...
//		for (unsigned int iEntity = 0; iEntity < context._entityCount; ++iEntity)
//		{
//			glmDestroyEntityGeometry(&geometries[iEntity]);
//		}
//		GLM_FREE( geometries);
//		glmEndGeometryGeneration(&context);
//		GLM_FREE_NOPROFILING( context._crowdFieldNames);
//		GLM_FREE_NOPROFILING( context._cacheDirectories);
//		GLM_FREE_NOPROFILING( context._characterFilesDirectories);
//		GLM_FREE_NOPROFILING( context._excludedEntities);
//		glm::crowdio::finish();
//

//////////////////////////////////////////////////////////////////////////////
//
// Golaem Crowd entities geometry generation API
//
#define GIO_NAME_LENGTH 2048
#define GIO_LAYOUTS_LENGTH 16384
#define GIO_MAX_MESHES 2000
#define GIO_SHADER_NAME_LENGTH 256
#define GIO_SHADING_LENGTH 10000
#define GIO_TRACE_LENGTH 1024
#define GIO_MAX_POLYS 2000000
#define GIO_MAX_FRAMES 10000
#define GIO_MAX_INSTANCE_MESHES 10000
#define GIO_MAX_INSTANCE_MATRIX_PER_ENTITY 1000
#define GIO_SHADER_GROUP_NOT_FOUND UINT16_MAX - 1
#define GIO_SHADER_GROUP_EVAL_MODE UINT16_MAX

#define GCG_MAGIC_NUMBER 0x6C60
#define GTG_MAGIC_NUMBER 0x6760

namespace glm
{
    namespace crowdio
    {
        //-------------------------------------------------------------------------
        // Types and Structures
        //-------------------------------------------------------------------------

        typedef enum
        {
            GIO_SUCCESS,
            GIO_GSC_FILE_OPEN_FAILED,             // simulation cache files open failed
            GIO_INVALID_CHARACTER,                // a character has not been assigned a Rendering Type from the Rendering Attributes panel
            GIO_CHARACTER_INVALID_RENDERING_TYPE, // a character has an invalid Rendering Type. Using the first Rendering Type found
            GIO_CHARACTER_NO_MESH_ASSET,          // a character has no mesh asset
            GIO_FRAME_NO_DATA,                    // insufficient data in simulation cache for the frame context->_frame
            GIO_FBX_FILE_OPEN_FAILED,             // failed to load the Fbx file of a Character
            GIO_FBX_FILE_MESH_NOT_FOUND,          // failed to find a mesh in the Fbx file of a Character
            GIO_GCG_FILE_OPEN_FAILED,             // failed to load the gcg file of a Character
            GIO_GCG_FILE_BAD_FORMAT,              // failed to load the gcg file of a Character
            GIO_GCG_BAD_CACHE,                    // the input cache has wrong number of points
            GIO_GCG_BONE_SCALE_NOT_ONE,           // the skeleton has not a 1 scale in rest pose or bind pose, this mess up the skinning, forbid that
            GIO_GCG_FILE_NO_MESH_FOUND,           // no mesh
            GIO_GCG_FILE_MESH_NOT_FOUND,          // failed to find a mesh in the gcg file of a Character
            GIO_GCG_ROOT_NOT_FOUND,               // could not find root by name in input fbx while converting
            GIO_GCG_BONE_NOT_FOUND,               // could not find root by name in input fbx while converting
            GIO_MESH_NO_SHADER_GROUP,             // failed to find a shader group for a mesh
            GIO_MESH_NO_NORMAL,                   // the input mesh has no normals when generating a geometry file, must generate them
            GIO_INVALID_CONTEXT,                  // context passed as a parameter is invalid, either it has not yet been created or destroyed
            GIO_INVALID_BONE_COUNT                // bone count mismatch between cache and GCHA
        } GlmGeometryGenerationStatus;

        typedef enum
        {
            GLM_NORMAL_PER_CONTROL_POINT,         // as much normals as vertices, access normals via _trianglesVertexIndices / _polygonVertexIndices (_trianglesNormalIndices / _polygonNormalIndices are set to these pointers)
            GLM_NORMAL_PER_POLYGON_VERTEX,        // Only for polygons (not triangles): as much normals as polyTotalVertexCount, access directly array in poly vertex order (0 = first vertex of first poly, 1 = second vertex of first poly, etc). Triangles need a matching indexed array (to match poly vertices)
            GLM_NORMAL_PER_POLYGON_VERTEX_INDEXED // free count, access normals via _trianglesNormalIndices / _polygonNormalIndices.
        } GlmNormalMode;

        typedef enum
        {
            GLM_UV_PER_CONTROL_POINT,
            GLM_UV_PER_POLYGON_VERTEX,
            GLM_UV_PER_POLYGON_VERTEX_INDEXED
        } GlmUVMode;

        typedef enum
        {
            GLM_SKIN_RIGID,
            GLM_SKIN_LINEAR,
            GLM_SKIN_DUALQ,
            GLM_SKIN_BLEND
        } GlmSkinningType;

        struct GlmFileString
        {
            uint16_t _allocSize;
            char* _string;
        };

        // -------------------------------------------------------
        struct GlmBlendTarget_0
        {
            GlmFileString _targetName;
            uint32_t _controlPointsCount;
            uint32_t* _controlPointsIndices;    // size = _controlPointsCount
            float (*_controlPointsPosition)[3]; // size = _controlPointsCount

			uint32_t _normalCount; 
			uint32_t* _normalIndices; // size = _normalCount
			float(*_normals)[3];      // size = _normalCount 
			//float(*_tangents)[3];      // size would be potentially different from normalCount, too costly and seldomly used. need a _tangentCount and a _tangentIndices if needed
        };
        typedef GlmBlendTarget_0 GlmBlendTarget;

        // -------------------------------------------------------
        struct GlmBlendShapeChannel_0
        {
            GlmFileString _channelName; // used to alias the blendshape weights
            float _deformPercent;       // runtime deform percent, in between should be handled on this basis
            uint16_t _blendTargetCount;
            GlmBlendTarget* _blendTargets; //_blendTargetCount size
            float* _fullDeformPercents;    // each target fulldeform match a certain percent, in between are blended according to deformPercent (logically located between two _fullDeformPercents)
        };
        typedef GlmBlendShapeChannel_0 GlmBlendShapeChannel;

        // -------------------------------------------------------
        struct GlmBlendShape_0
        {
            char _blendShapeName[GIO_NAME_LENGTH]; // name of the blendshape for blindData update
            uint16_t _blendShapeChannelCount;
            GlmBlendShapeChannel* _blendShapeChannels;
			uint8_t _supportNegativeWeights;
        };
        typedef GlmBlendShape_0 GlmBlendShape;

        struct GlmFileMeshVertex_0
        {
            float _position[3];

            // skinning
            float _skinInfluenceBlendWeight;   // 0.f for linear, 1.f for dual Q, between for blend
            uint8_t _skinVertexInfluenceCount; // read inline all influences to get first vertex n1 influence(s), then vertex second n2 influence(s), etc
            float* _skinInfluenceWeights;      // size = _skinVertexInfluenceCount
            uint16_t* _skinInfluenceBoneId;    // size = _skinVertexInfluenceCount
        };
        typedef GlmFileMeshVertex_0 GlmFileMeshVertex;

        struct GlmFileMesh_0
        {
            GlmFileString _meshName; // name of the mesh

            uint8_t _skinningType; // 0 : rigid, 1 : linear, 2 : dualQ, 3:blend
            uint16_t _rigidSkinningBoneId;

            float _defaultLocalToWorldMatrix[16];

            // control points
            uint32_t _vertexCount;        // number of GlmFileMeshVertex in this mesh, post optimization/merge
            GlmFileMeshVertex* _vertices; // _vertexCount * GlmFileMeshVertex

            // normals
            uint8_t _normalMode;   // normals can be either GLM_NORMAL_PER_CONTROL_POINT, GLM_NORMAL_PER_POLYGON_VERTEX, or GLM_NORMAL_PER_POLYGON_VERTEX_INDEXED
            uint32_t _normalCount; // either == _vertexCount, _polygonsTotalVertexCount or anything for GLM_NORMAL_PER_POLYGON_VERTEX_INDEXED
            float (*_normals)[3];

            // UVs
            size_t _uvSetCount;                          // number of uv set for this mesh
            char (*_uvSetNames)[GIO_SHADER_NAME_LENGTH]; // array size dimension = _uvSetCount, names of the uv set for this mesh
            uint8_t _uvMode;                             // can be either GLM_UV_PER_CONTROL_POINT, GLM_UV_PER_POLYGON_VERTEX, or GLM_UV_PER_POLYGON_VERTEX_INDEXED
            uint32_t* _uvCoordCount;                     //  array size first dimension = _uvSetCount, either == _vertexCount, _polygonsTotalVertexCount or anything for GLM_NORMAL_PER_POLYGON_VERTEX_INDEXED
            float** _us;                                 // U coordinates, array size first dimension = _uvSetCount
            float** _vs;                                 // V coordinates, array size first dimension = _uvSetCount

            // triangles (for previz)
            uint32_t _triangleCount;                // number of triangles per mesh
            uint32_t (*_trianglesVertexIndices)[3]; // id of vertices by polygon, array size = _triangleCount * 3
            uint32_t (*_trianglesNormalIndices)[3]; // id of normals per triangle, array size = _triangleCount * 3. If per control point, points on _trianglesVertexIndices
            uint32_t (*_trianglesUVIndices)[3];     // id of vertices by polygon, array size = _triangleCount * 3. If per control point, points on _trianglesVertexIndices

            // polygons (for rendering / subdiv surface / etc)
            uint32_t _polygonCount;             // number of polygons per mesh
            uint32_t* _polygonsVertexCount;     // number of vertices by polygon, array size = _polygonCount
            uint32_t _polygonsTotalVertexCount; // total number of polygon indices (for allocation)
            uint32_t* _polygonsVertexIndices;   // id of vertices by polygon, array size = sum (_verticePerPolygonCount[(_polygonCount[0...N]] = _polygonsTotalVertexCount
            uint32_t* _polygonsNormalIndices;   // either points on _polygonsVertexIndices pointer, or sized against _polygonsTotalVertexCount for mode GLM_NORMAL_PER_POLYGON_VERTEX_INDEXED
            uint32_t* _polygonsUVIndices;       // either points on _polygonsVertexIndices pointer, or sized against _polygonsTotalVertexCount

            // structure to have triangle fallBack per face
            uint32_t _perPolyTriangleTotalCount;  // helper to get total triangle Ids
            uint32_t* _perPolyTriangleIds;        // continuous list of all faces matching triangles, size = _polygonCount * triangles
            uint32_t* _perPolyFirstTriangleIndex; // first triangle location in above list for all faces, size = _polygonCount

            // "geometry" transform animation
            uint32_t _animTransformCount;              // can be 0
            float (*_animationLocalToWorldMatrix)[16]; // one per animation transform count
            double _animStartTime;
            double _animStopTime;

            // vertex cache
            uint32_t _vertexCacheFrameCount;
            double _vertexCacheStartTime;
            double _vertexCacheStopTime;
            float (*_vertexCachePositions)[3]; // _original_ vertexCount * frameCount

            // blendshapes (not used for rendering)
            uint16_t _blendShapeCount;
            GlmBlendShape* _blendShapes;
        };

		struct GlmFileMesh_1
		{
			GlmFileString _meshName; // name of the mesh

			uint8_t _skinningType; // 0 : rigid, 1 : linear, 2 : dualQ, 3:blend
			float _skinningLocalToWorldMatrix[16]; // usually the same as the single reference, but may differ if the initial reference is deleted so keep it there for a correct skinning

			// control points
			uint32_t _vertexCount;        // number of GlmFileMeshVertex in this mesh, post optimization/merge
			GlmFileMeshVertex* _vertices; // _vertexCount * GlmFileMeshVertex

			// normals
			uint8_t _normalMode;   // normals can be either GLM_NORMAL_PER_CONTROL_POINT, GLM_NORMAL_PER_POLYGON_VERTEX, or GLM_NORMAL_PER_POLYGON_VERTEX_INDEXED
			uint32_t _normalCount; // either == _vertexCount, _polygonsTotalVertexCount or anything for GLM_NORMAL_PER_POLYGON_VERTEX_INDEXED
			float(*_normals)[3];

			uint32_t _tangentCount; // Tangent use the same storage mode as normal. either == _vertexCount, _polygonsTotalVertexCount or anything for GLM_NORMAL_PER_POLYGON_VERTEX_INDEXED
			float(*_tangents)[3];

			// UVs
			size_t _uvSetCount;                          // number of uv set for this mesh
			char(*_uvSetNames)[GIO_SHADER_NAME_LENGTH]; // array size dimension = _uvSetCount, names of the uv set for this mesh
			uint8_t _uvMode;                             // can be either GLM_UV_PER_CONTROL_POINT, GLM_UV_PER_POLYGON_VERTEX, or GLM_UV_PER_POLYGON_VERTEX_INDEXED
			uint32_t* _uvCoordCount;                     //  array size first dimension = _uvSetCount, either == _vertexCount, _polygonsTotalVertexCount or anything for GLM_NORMAL_PER_POLYGON_VERTEX_INDEXED
			float** _us;                                 // U coordinates, array size first dimension = _uvSetCount
			float** _vs;                                 // V coordinates, array size first dimension = _uvSetCount

			// triangles (for previz)
			uint32_t _triangleCount;                // number of triangles per mesh
			uint32_t(*_trianglesVertexIndices)[3]; // id of vertices by polygon, array size = _triangleCount * 3
			uint32_t(*_trianglesNormalIndices)[3]; // id of normals per triangle, array size = _triangleCount * 3. If per control point, points on _trianglesVertexIndices
			uint32_t(*_trianglesUVIndices)[3];     // id of vertices by polygon, array size = _triangleCount * 3. If per control point, points on _trianglesVertexIndices

			// polygons (for rendering / subdiv surface / etc)
			uint32_t _polygonCount;             // number of polygons per mesh
			uint32_t* _polygonsVertexCount;     // number of vertices by polygon, array size = _polygonCount
			uint32_t _polygonsTotalVertexCount; // total number of polygon indices (for allocation)
			uint32_t* _polygonsVertexIndices;   // id of vertices by polygon, array size = sum (_verticePerPolygonCount[(_polygonCount[0...N]] = _polygonsTotalVertexCount
			uint32_t* _polygonsNormalIndices;   // either points on _polygonsVertexIndices pointer, or sized against _polygonsTotalVertexCount for mode GLM_NORMAL_PER_POLYGON_VERTEX_INDEXED
			uint32_t* _polygonsUVIndices;       // either points on _polygonsVertexIndices pointer, or sized against _polygonsTotalVertexCount

			// structure to have triangle fallBack per face
			uint32_t _perPolyTriangleTotalCount;  // helper to get total triangle Ids
			uint32_t* _perPolyTriangleIds;        // continuous list of all faces matching triangles, size = _polygonCount * triangles
			uint32_t* _perPolyFirstTriangleIndex; // first triangle location in above list for all faces, size = _polygonCount
			
			// vertex cache
			uint32_t _vertexCacheFrameCount;
			double _vertexCacheStartTime;
			double _vertexCacheStopTime;
			float(*_vertexCachePositions)[3]; // _original_ vertexCount * frameCount

			// blendshapes (not used for rendering)
			uint16_t _blendShapeCount;
			GlmBlendShape* _blendShapes;
		};
		typedef GlmFileMesh_1 GlmFileMesh;

		struct GlmFileMeshTransform_0
		{
			GlmFileString _referenceName; // name of the reference
			uint16_t _meshIndex; // in geometryFile mesh array, in [0,_meshCount-1] range

			uint16_t _rigidSkinningBoneId;

			float _defaultLocalToWorldMatrix[16];
			// "geometry" transform animation
			uint32_t _animTransformCount;              // can be 0
			float(*_animationLocalToWorldMatrix)[16]; // one per animation transform count
			double _animStartTime;
			double _animStopTime;
		};
		typedef GlmFileMeshTransform_0 GlmFileMeshTransform;

        // Render data for an entity: meshes
        struct GlmGeometryFile_0
        {
            uint16_t _meshCount;
            GlmFileMesh* _meshes;

			uint16_t _transformCount;
			GlmFileMeshTransform* _transforms;

            uint16_t _boneCount;
            GlmFileString* _boneNames; // name of the bones
            uint16_t* _boneParenting;

            float (*_bindPoseWorldPositionsInverse)[3];   // Bindpose positions (translate component of inverse bindpose bones matrices), used for skinning. Can be incomplete (let to null) if bone is not used in skinning.
            float (*_bindPoseWorldOrientationInverse)[4]; // Bindpose orientations (orientation component of inverse bindpose bones matrices), used for skinning. Can be incomplete (let to identity) if bone is not used in skinning.

            float (*_restPoseWorldPosition)[3];    // rest positions (translate component of inverse rest matrix), used for skinning
            float (*_restPoseWorldOrientation)[4]; // rest orientations (orientation component of inverse rest matrix), used for skinning
            float _restPoseScale;
            float _invBindPoseScale;

            float (*_boneJointOrientations)[4]; // can't rebuild a maya skeleton without pre rotation information, which is joint orientation. boneOffsetOrientations is inverse of R*JO
        };
        typedef GlmGeometryFile_0 GlmGeometryFile;

        //-------------------------------------------------------------------------
        // Functions
        //-------------------------------------------------------------------------

        // convert a status in an error message
        extern GLM_CROWDIO_API const char* glmConvertGeometryGenerationStatus(GlmGeometryGenerationStatus status);

        extern GLM_CROWDIO_API void glmFileSetString(GlmFileString& string, const char* value);
		extern GLM_CROWDIO_API void glmFileCopyString(GlmFileString& destString, GlmFileString& srcString);

        // write/read a GolaemCharacterGeoemtry file
        extern GLM_CROWDIO_API void glmClearGeometryFile(GlmGeometryFile& geometry);
		extern GLM_CROWDIO_API void glmClearFileMesh(GlmFileMesh& mesh);
		extern GLM_CROWDIO_API void glmClearFileMeshTransform(GlmFileMeshTransform& meshTransform);

        extern GLM_CROWDIO_API GlmGeometryGenerationStatus glmWriteGeometryFile(const char* filename, GlmGeometryFile& geometry);
        extern GLM_CROWDIO_API GlmGeometryGenerationStatus glmReadGeometryFile(const char* filename, GlmGeometryFile& geometry);

        // generic write/read functions
		extern GLM_CROWDIO_API void glmFileWriteMeshV0(const GlmFileMesh_0& mesh, FILE* fp);
		extern GLM_CROWDIO_API void glmFileReadMeshV0(GlmFileMesh_0& mesh, FILE* fp, int version);

        extern GLM_CROWDIO_API void glmFileWriteMesh(const GlmFileMesh& mesh, FILE* fp);
		extern GLM_CROWDIO_API void glmFileWriteMeshTransform(const GlmFileMeshTransform& meshTransform, FILE* fp);
        extern GLM_CROWDIO_API void glmFileReadMesh(GlmFileMesh& mesh, FILE* fp, int version);
		extern GLM_CROWDIO_API void glmFileReadMeshTransform(GlmFileMeshTransform& mesh, FILE* fp, int version);

        extern GLM_CROWDIO_API void glmShrinkBlendshapesInPlace(crowdio::GlmGeometryFile& geometryFile);

		extern GLM_CROWDIO_API void glmCopyDataFromMesh_0ToMesh_1(uint16_t meshIndex, const GlmFileMesh_0& meshV0, GlmFileMesh_1& mesh, GlmFileMeshTransform_0& meshTransform);
		//extern GLM_CROWDIO_API void glmCopyDataFromMesh_1ToMesh_0(uint16_t meshIndex, const GlmFileMesh_1& mesh, const GlmFileMeshTransform_0& meshTransform, GlmFileMesh_0& meshV0);
    } // namespace crowdio
} // namespace glm
