/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A. - All Rights Reserved.                        *
*                                                                          *
***************************************************************************/

#ifndef GLM_FRAMEDATA_H
#define GLM_FRAMEDATA_H

#include <glmCrowdIOAPI.h>
#include "glmSimulationCacheCommon.h"
#include "glmSimulationData.h"
#include <glmAngle.h>

#include "stdint.h"
#include "stdio.h"

#include <glmGolaemCharacterDeclarations.h> // GolaemCharacter declarations

namespace glm
{
    //predeclarations
    class Vector3;
    class Quaternion;
    class SpecificPosture;

    namespace crowdio
    {
        //-------------------------------------------------------------------------
        // Frame Data Types
        //-------------------------------------------------------------------------

        // Simulation cache format----------------------------------------------------
        typedef enum
        {
            GSC_O128_P96, // Golaem Simulation Cache, orientations and positions not quantized
            GSC_O64_P96,  // Golaem Simulation Cache, orientations quantized on 64bit, positions not quantized
            GSC_O32_P96,  // Golaem Simulation Cache, orientations quantized on 32bit, positions not quantized
            GSC_O128_P48, // Golaem Simulation Cache, orientations not quantized, positions quantized on 48bit
            GSC_O64_P48,  // Golaem Simulation Cache, orientations quantized on 64bit, positions quantized on 48bit
            GSC_O32_P48,  // Golaem Simulation Cache, orientations quantized on 32bit, positions quantized on 48bit
        } GlmSimulationCacheFormat;

        // per-frame data
        typedef struct GlmFrameData_v0
        {
            uint8_t _cacheFormat;               // read cache format is stored to be able to rewrite this cache data with same format
            uint32_t _simulationContentHashKey; // to check if simulation matches frame file
            uint8_t _hasSquashAndStretch;       // if true at write time, cacheFormat will be forced to unquantized positions (P96) if P48 was chosen.

            uint8_t* _entityEnabled; // mask for emitters not already emitted, or killed after emission - access with simulation data _entityToBakeIndex, array size = _entityToBakeCount

            // EntityType
            // /!\ Order is NOT the same as entity Arrays : it is ordered per EntityType, then per entity in its entityType : ET0.entity0, ET0.entity1, ET0.entity2, ..., ET1.entity0, ET1.entity1, ...
            float (*_bonePositions)[3];            // bones positions per Entity per EntityType, array size = _boneCount * _entityCountPerEntityType * _entityTypeCount
            float (*_boneOrientations)[4];         // bones orientations per Entity per EntityType, array size = _boneCount * _entityCountPerEntityType * _entityTypeCount
            float (*_snsValues)[4];                // Squatch and stretch values per EntityType, array size = _snsCountPerEntityType * entityCountPerEntityType* entityTypeCount
            float* _blindData;                     // blind data per Entity per EntityType, array size = _blindDataCount * _entityCountPerEntityType * _entityTypeCount : [ET0-E0-0blindDataCount][ET0-E1-0blindDataCount][ET1-E0-0blindDataCount]etc..
            uint16_t* _geoBehaviorGeometryIds;     // geometry behavior geometry id, array size = _entityCountPerEntityType * _entityTypeCount
            float (*_geoBehaviorAnimFrameInfo)[3]; // geometry behavior animation frame info per Entity of an Entitype, array size =  _entityCountPerEntityType * _entityTypeCount, NULL if no geometry behavior. Infos are current, start and stop frames
            uint8_t* _geoBehaviorBlendModes;       // geometry behavior blend mode per Entity of an Entitype, array size =  _entityCountPerEntityType * _entityTypeCount, NULL if no geometry behavior - stores a boolean as 0 or 1

            // allocation data for cloth, to avoid reallocation each frame if memory space is sufficient (not serialized)
            uint32_t _clothAllocatedEntities;
            uint32_t _clothAllocatedIndices;
            uint32_t _clothAllocatedVertices;

            // cloth :
            // entity uses cloth if _clothEntityCount != 0 && _entityClothIndex[entityIndex] is not -1
            // one cloth entity may have several cloth meshes.

            // serialization helpers
            uint32_t _clothEntityCount;      // if 0, skip reading of everything else cloth-related (let to NULL)
            uint32_t _clothTotalMeshIndices; // shortcut for reading
            uint32_t _clothTotalVertices;    // shortcut for reading

            // is entity using cloth ? if so, which cloth index to use if cloth arrays
            int32_t* _entityClothIndex; // helper : matching cloth entity index, or -1 if not used - access with simulation data _entityToBakeIndex, array size = _entityToBakeCount, beware it is SIGNED

            // one entity have _clothEntityMeshCount[clothEntityIndex] meshes
            uint32_t* _clothEntityMeshCount; // count of cloth mesh indices for this cloth entity, size = _clothEntityCount, read in entity order

            // one clothIndex has _clothEntityMeshCount[clothEntityIndex] meshes
            // mesh indices in geometry starts at _clothEntityFirstAssetMeshIndex.
            // there vertices starts at _clothEntityFirstMeshVertex (sequential read of all meshes)
            // the vertices are quantized according to _clothEntityQuantizationReference & _clothEntityQuantizationMaxExtent
            uint32_t* _clothEntityFirstAssetMeshIndex;     // helper : mesh indices for a given clothEntity starts at this index
            uint32_t* _clothEntityFirstMeshVertex;         // vertices for a given clothEntity starts at this index. Add all meshes vertices count to shift to right vertex for a given mesh
            float (*_clothEntityQuantizationReference)[3]; // _clothEntityCount elements, gives reference vertex for quantization = this entity vertices (min+max)/2 in all dimensions
            float* _clothEntityQuantizationMaxExtent;      //_clothEntityCount elements, gives maxExtent of cloth for quantization max of (_clothEntityQuantizationReference - min value) in all dimensions

            // for each mesh, from _clothEntityFirstMeshVertex[clothIndex] to _clothEntityFirstMeshVertex[clothIndex] + _clothEntityMeshCount[clothIndex], we must read _clothMeshVertexCount

            // cloth data per mesh
            uint32_t* _clothMeshIndicesInCharAssets; // mesh indices, in original geometry, for cloth, in serial, entity0 indices, then entity 1 indices, etc
            uint32_t* _clothMeshVertexCount;         // number of vertices to read for a given mesh index

            // cloth vertex positions array
            float (*_clothVertices)[3]; // vertices position : clothEntity 0 mesh (0 to n1) vertices, clothEntity 1 mesh (0-n2), ..., up to last clothEntity last mesh vertices

            // ppAttribute
            float** _ppFloatAttributeData;       // float data per particle attributes, array size first dimension = _floatAttributeCount, second dimension - access with simulation data _entityToBakeIndex, array size = _entityToBakeCount
            float (**_ppVectorAttributeData)[3]; // vector data per particle attributes, array size = _vectorAttributeCount, second dimension - access with simulation data _entityToBakeIndex, array size = _entityToBakeCount

            // runtime attributes
            float* _runtimeAdditionalUpTranslation; // access with simulation data _entityToBakeIndex, array size = _entityToBakeCount
        } GlmFrameData_v0;
        typedef GlmFrameData_v0 GlmFrameData;

        //----------------------------------------------
        typedef struct GlmFrameToLoad_v0
        {
            int _frameIndex;
            const GlmFrameData* _frame;
        } GlmFrameToLoad;

        //-------------------------------------------------------------------------
        // Frame data functions
        //-------------------------------------------------------------------------

        // allocate *frameData
        extern GLM_CROWDIO_API void createFrameData(GlmFrameData** frameData, const GlmSimulationData* simulationData);
        extern GLM_CROWDIO_API void createAndCopyFrameData(const GlmFrameData* src, GlmFrameData** dst, const GlmSimulationData* simulationData);
        extern GLM_CROWDIO_API void createAndCopyFrameData(const GlmFrameData* srcFrameData, const GlmSimulationData* srcSimulationData, GlmFrameData** dstFrameData, const GlmSimulationData* dstSimulationData);
        // deallocate *frameData and set it to NULL
        extern GLM_CROWDIO_API void destroyFrameData(GlmFrameData** frameData, const GlmSimulationData* simulationData);

        // read a .gscf file in the previously allocated *frameData
        // return GSC_SUCCESS || GSC_FILE_OPEN_FAILED || GSC_FILE_MAGIC_NUMBER_ERROR || GSC_FILE_VERSION_ERROR || GSC_FILE_FORMAT_ERROR
        extern GLM_CROWDIO_API GlmSimulationCacheStatus readFrameData(GlmFrameData* frameData, const GlmSimulationData* simulationData, const char* file);

        // write *frameData in a .gscf file
        // return GSC_SUCCESS || GSC_FILE_OPEN_FAILED
        extern GLM_CROWDIO_API GlmSimulationCacheStatus writeFrameData(const char* file, const GlmFrameData* frameData, const GlmSimulationData* simulationData);

        extern GLM_CROWDIO_API void voidEntityFrameData(GlmFrameData* frameDataOut, const GlmSimulationData* simuDataIn, const GlmSimulationData* simuDataOut, int sourceIndexInCrowdField, int destIndexInCrowdField, int geoBeDestination);

        // create an entityTransform array from TransformHistory and GlmSimulationData

        // Compute the size in bytes allocated for a GlmFrameData
        extern GLM_CROWDIO_API uint64_t computeFrameDataSize(const GlmFrameData* frameData, const GlmSimulationData* simulationData);

        // allocate cloth data for frame data (except entityInUse), not in createFrameData because cloth count can change at every frame
        extern GLM_CROWDIO_API void createClothData(const GlmSimulationData* simuData, GlmFrameData* frameData, unsigned int clothEntityCount, unsigned int clothIndices, unsigned int clothVertices);

        // check the quantization max value validity before chosing a cache format that quantize the position
        extern GLM_CROWDIO_API char checkQuantization(const GlmSimulationData* simData, const GlmFrameData* frameData, int64_t* failedEntityId);

        extern GLM_CROWDIO_API void copyEntityFrameData(const GlmFrameData* frameDataIn, GlmFrameData* frameDataOut, const GlmSimulationData* simuDataIn, const GlmSimulationData* simuDataOut, int sourceIndexInCrowdField, int destIndexInCrowdField, int geoBeSource, int geoBeDestination);
        extern GLM_CROWDIO_API void interpolateEntityFrameData(const GlmFrameData* frameDataIn1, const GlmFrameData* frameDataIn2, float fraction, GlmFrameData* frameDataOut, const GlmSimulationData* simuDataIn, const GlmSimulationData* simuDataOut, int sourceIndexInCrowdField, int destIndexInCrowdField, int geoBeSource, int geoBeDestination);
        extern GLM_CROWDIO_API void interpolateFrameData(const GlmSimulationData* simulationData, const GlmFrameData* frameData1, const GlmFrameData* frameData2, float ratio, GlmFrameData* result); // ratio must be between 0 (full frame 1) and 1 (full frame 2), frames must be of same simulationData

        extern GLM_CROWDIO_API void translateFrameDataEntity(const GlmSimulationData* simulationData, GlmFrameData* frameData, int entityIndexInSimData, const glm::Vector3& translation);
        extern GLM_CROWDIO_API void translateFrameDataEntity(float (*bonePositionsPtrDest)[3], uint16_t boneCount, const glm::Vector3& translation);
        extern GLM_CROWDIO_API void rotateFrameDataEntity(const GlmSimulationData* simulationData, GlmFrameData* frameData, int entityIndexInSimData, const glm::Quaternion& rotation, const glm::Vector3& pivotPoint);
        extern GLM_CROWDIO_API void rotateFrameDataEntity(float (*bonePositionsPtrDest)[3], float (*boneOrientationPtrDest)[4], uint16_t boneCount, const glm::Quaternion& rotation, const glm::Vector3& pivotPoint);
        //extern GLM_CROWDIO_API void scaleFrameDataEntity(const GlmSimulationData* simulationData, GlmFrameData* frameData, int entityIndexInSimData, const glm::Vector3& scale, const glm::Vector3& scalePivotPoint);

        extern GLM_CROWDIO_API void setFrameDataEntityTransforms(const GlmSimulationData* simulationData, GlmFrameData* frameData, int entityIndexInSimData, const glm::Vector3& position, const glm::Quaternion& orientation);
        extern GLM_CROWDIO_API void setFrameDataEntityPosition(const GlmSimulationData* simulationData, GlmFrameData* frameData, int entityIndexInSimData, const glm::Vector3& position);
        extern GLM_CROWDIO_API void setFrameDataEntityOrientation(const GlmSimulationData* simulationData, GlmFrameData* frameData, int entityIndexInSimData, const glm::Quaternion& orientation);

        extern GLM_CROWDIO_API void setFrameDataEntityPosture(const GlmSimulationData* simulationData, GlmFrameData* frameData, int entityIndexInSimData, const HierarchicalSkeletonDescription* skeletonDescription, const SpecificPosture* skeletonPosture, int geoBeDestination);
        extern GLM_CROWDIO_API void getFrameDataEntityPosture(SpecificPosture& outSkeletonPosture, const HierarchicalSkeletonDescription* skeletonDescription, const GlmSimulationData* simulationData, const GlmFrameData* frameData, int entityIndexInSimData);
        extern GLM_CROWDIO_API void IKOnFrameDataEntity(const GlmSimulationData* simulationData, GlmFrameData* frameData, int entityIndexInSimData, const GenericSpecificConverterMapping& converterMapping, Vector3 desiredEOIKWPos, Quaternion desiredEOIKWOri, Angle IkNormalOffset, int ikChainIndex, int auxExt, int operationMode); //operationMode: 0 to add, 1 to set

        extern GLM_CROWDIO_API uint32_t getClothEntityMeshCount(const GlmFrameData* frameData, int clothEntityIndex);
        extern GLM_CROWDIO_API uint32_t getClothEntityIMeshVertexCount(const GlmFrameData* frameData, int clothEntityIndex, int iMesh);                             // a clothEntity has "meshCount" meshes. Get each of its index in all cloth entities meshes cache via this.
        extern GLM_CROWDIO_API void getClothEntityIMeshVerticesPtr(const GlmFrameData* frameData, int clothEntityIndex, int iMesh, float (**outFirstVertexPtr)[3]); // a clothEntity has "meshCount" meshes. Get each of its index in all cloth entities meshes cache via this.

        extern GLM_CROWDIO_API void setGeometryFileIndexEntity(const GlmSimulationData* simulationData, GlmFrameData* frameData, int entityIndexInSimData, int geometryFileIndex);
    } // namespace crowdio
} // namespace glm

#endif // GLM_FRAMEDATA_H
