/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
***************************************************************************/
#pragma once

#include "glmCrowdIOAPI.h"
#include "glmRTTI.h"
#include <glmPODArray.h>
#include <glmString.h>
#include <glmSmartPointer.h>

#include <glmGolaemCharacterDeclarations.h> // GolaemCharacter declarations

#define GLM_FUR_CACHE_VERSION 3

namespace glm
{
    struct ShaderAssetDataContainer;

    namespace crowdio
    {
        class AlembicCharacter;

        struct LoadFurCacheErrorCode
        {
            enum Value
            {
                SUCCESS,
                FILE_NOT_FOUND,
                INCORRECT_VERSION,
                INVALID_FILE,
                END
            };
        };

        struct SaveFurCacheErrorCode
        {
            enum Value
            {
                SUCCESS,
                FILE_CREATION_FAILED,
                END
            };
        };

        //-------------------------------------------------------------------------
        struct GLM_CROWDIO_API FurIds
        {

        public:
            size_t _furMeshAssetIdx; // mesh asset idx in character file
            size_t _furAssetIdx;     // fur asset idx in character file
            size_t _meshInFurIdx;    // mesh in fur asset idx
            size_t _furCacheIdx;     // fur cache idx
            size_t _renderMeshIdx;   // render geometry asset idx
            size_t _geoMeshIdx;      // geometry file asset idx
        };

        //-------------------------------------------------------------------------
        struct GLM_CROWDIO_API FurSkinInfluence
        {
        private:
            // need rtti for serialising into binary stream
            GLM_RTTI("bd070b81-4232-4158-8268-ee330fcc5444", glm::crowdio::FurSkinInfluence, addAttributes);

        public:
            float _weight;
            int32_t _boneId;

            FurSkinInfluence()
                : _weight(0.f)
                , _boneId(0)
            {
            }

            bool operator<(const FurSkinInfluence& other) const
            {
                return _weight < other._weight;
            }

        private:
            static void addAttributes();
        };

        //-------------------------------------------------------------------------
        struct GLM_CROWDIO_API FurSkinVertex
        {
        private:
            // need rtti for serialising into binary stream
            GLM_RTTI("6aa2c67d-8603-433f-95e4-3fa13db1b65e", glm::crowdio::FurSkinVertex, addAttributes);

        public:
            FurSkinVertex()
                : _blendWeight(0.f)
                , _skinInfluences()
            {
            }

            float _blendWeight; // 0.f for linear, 1.f for dual Q, between for blend

            glm::Array<FurSkinInfluence> _skinInfluences;

        private:
            static void addAttributes();
        };

        //struct GLM_CROWDIO_API FurSkinVertexArray
        //{
        //private:
        //	// need rtti for serialising into binary stream
        //	GLM_RTTI("87253365-6d89-4324-ae9b-358d63147cf2", glm::crowdio::FurSkinVertexArray, addAttributes);

        //public:
        //	glm::Array<FurSkinVertex> _furSkinVertices;

        //private:
        //	static void addAttributes();
        //};

        //-------------------------------------------------------------------------
        struct FurSkinningType
        {
            enum Value
            {
                RIGID,
                LINEAR,
                DUALQ,
                BLEND,
                NONE,
                END
            };
        };

        // utility class to be able to unpack stream of older versions
        //-------------------------------------------------------------------------
        struct FurCacheV0
        {
            // data members are arrays because there can be several curve groups in one file (XGen splits into groups if there are too many primitives and/or the fur is applied to several meshes)

            // data loaded from alembic
            Array<PODArray<uint32_t>> _numVertices; // number of vertices for each curve
            Array<Array<Vector3>> _positions;       // positions of vertices for each curve
            Array<PODArray<float>> _widths;         // widths of vertices for each curve
            Array<Array<Vector2>> _uvs;             // uvs of vertices for each curve
            GlmString _filePath;                    // file path from which the fur was loaded

            // additional data from the associated geometry
            PODArray<int> _meshIds;            // mesh id of each curve group
            Array<PODArray<int>> _faceIds;     // only used in v0, not needed in v1
            Array<PODArray<int>> _triangleIds; // only used in v0, not needed in v1
            Array<Array<Vector2>> _baryUVs;    // only used in v0, not needed in v1
            Array<PODArray<int>> _triVtxIds;
            Array<Array<Vector3>> _tetraBaryCoords; // since v1
        };

        //-------------------------------------------------------------------------
        struct GLM_CROWDIO_API FurCurveGroup
        {
        public:
            // data loaded from alembic
            PODArray<uint32_t> _numVertices; // number of vertices for each curve
            Array<Vector3> _positions;       // positions of vertices for each curve
            PODArray<float> _widths;         // widths of vertices for each curve, or empty
            Array<Vector2> _uvs;             // uvs of vertices for each curve, or empty

            // additional data from the associated geometry
            int _supportMeshId;              // support mesh id of each curve of this group (useful if no skinning)
            PODArray<int> _triVtxIds;        // support mesh vertex indices for barycenter computation
            Array<Vector3> _tetraBaryCoords; // support mesh terahedron barycenter if no skinning

            // degree & skinning info - since v2
            char _curveDegrees;                 // degree for each curve
            PODArray<short> _skinningType;      // skinning type for each curve, values are FurSkinningType::Value
            Array<FurSkinVertex> _skinningInfo; // vertex skinning info - per vertex

            Array<GlmString> _floatPropertiesNames;
            Array<Array<float> > _floatProperties;
            Array<GlmString> _vector3PropertiesNames;
            Array < Array<Vector3> > _vector3Properties;
        };

        //-------------------------------------------------------------------------
        class GLM_CROWDIO_API FurCache : public ReferenceCounter
        {
        public:
			GLM_CLASS_STAT_ID(glm::MemoryStatIds::Simulation)
            typedef glm::SmartPointer<FurCache> SP;

            int _version;

            GlmString _filePath; // file path from which the fur was loaded

            // data members are arrays because there can be several curve groups in one file (XGen splits into groups if there are too many primitives and/or the fur is applied to several meshes)
            Array<FurCurveGroup> _curveGroups;

            FurCache();

            LoadFurCacheErrorCode::Value loadFurCache(const GlmString& filePath);
            SaveFurCacheErrorCode::Value saveFurCache(const GlmString& filePath);

            bool loadFromAlembic(const GlmString& filePath, GlmString& loadDetails, GolaemCharacter* glmCharacter);

            void getContentDescription(GlmString& contentDescription);
        };

#if defined(GLM_EXPORT_ALEMBIC)
        void writeFurCacheToAlembicCharacter(
            AlembicCharacter* abcCharacter,
            const GolaemCharacter& sdkCharacter,
            const Array<Array<Vector3>>& inDeformedFurVertices,
            const PODArray<FurIds>& furIds,
            const Array<FurCache::SP>& furCache,
            int entityIdx,
            int characterIdx,
            const ShaderAssetDataContainer* shaderDataContainer);
#endif

    } // namespace crowdio
} // namespace glm