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

#pragma once

#include "glmCoreAPI.h"
#include "glmRTTI.h"
#include "glmAssetGroup.h"
#include "glmMeshAsset.h"
#include "glmDeepDeformerAsset.h"
#include "glmGeometryAsset.h"
#include "glmShadingGroup.h"
#include "glmRenderingType.h"
#include "glmGenericSpecificConverterMapping.h"
#include "glmCharacterUserData.h"
#include "glmShaderAsset.h"
#include "glmShaderAttribute.h"
#include "glmClothAsset.h"
#include "glmGolaemCharacterBlindDataOverride.h"
#include "glmGolaemCharacterSkeletonOverride.h"
#include "glmPhysicsOverride.h"
#include "glmGolaemCharacterInterface.h"
#include "glmBoneDisplayConfiguration.h"

#include <glmGolaemCharacterDeclarations.h> // GolaemCharacter declarations

#define GLM_GCH_FIRST_EXPORTTO_VERSION 3

#define GLM_GCH_VERSION 24

namespace glm
{
    class HierarchicalSkeletonDescriptionV1;

    struct GolaemCharacterV23;

    class GLM_CORE_API GolaemCharacterV24 : public GolaemCharacterInterface
    {
        GLM_VIRTUAL_RTTI("b59741ec-4b1e-4e6c-a758-86c48b665a65", glm::GolaemCharacterV24, addAttributes);

    public:
        GLM_CLASS_STAT_ID( glm::MemoryStatIds::Simulation);

        /// Name of the character
        glm::GlmString _name = "Character";
        int _fileVersion = getCurrentVersionNumber();

        glm::PODArray<int> _mainAssetGroups;
        glm::PODArray<int> _mainShaderAttributes;

        glm::Array<AssetGroupV4> _allAssetGroups;
        glm::Array<MeshAssetV8> _meshAssets;

        glm::Array<DeepDeformerAsset> _deepDeformerAssets;

        glm::GlmMap<short, glm::Array<GeometryAssetV4>> _geometryAssetsPerTags; // short = geometryTag, array<geoAsset> = the list of LODs of this geometryTag

        glm::Array<ShadingGroupV1> _shadingGroups;

        glm::Array<ShaderAttributeV3> _shaderAttributes;

        glm::Array<ShaderAssetV2> _shaderAssets;

        glm::Array<RenderingTypeV4> _renderingTypes;

        GenericSpecificConverterMappingV8 _converterMapping; // animation data
        CharacterUserDataV0 _characterUserData;              // additional user data

        Array<SkeletonOverrideV0> _skeletonOverrides;
        Array<BlindDataOverrideV0> _blindDataOverrides;
		Array<PhysicsOverrideV0> _physicsOverrides;

        Vector3 _perceptionShapeExtents = Vector3(1.f);
        Vector3 _perceptionShapeOffset = Vector3(0.f);

        Array<BoneDisplayConfigurationV0> _skeletonDisplay;

        glm::GlmString _runtimeFilePath; // runtime only

    public:
        GolaemCharacterV24();
        ~GolaemCharacterV24();

        LoadCharacterFileErrorCode::Value loadCharacterFile(const glm::GlmString& filePath, bool resolveRelativePaths = true);
        void loadCharacter(const GolaemCharacterV24& character);

        SaveCharacterFileErrorCode::Value saveCharacterFile(const glm::GlmString& filePath);
        SaveCharacterFileErrorCode::Value saveCharacterFileXML(const glm::GlmString& filePath);
        SaveCharacterFileErrorCode::Value saveCharacterFileBinary(const glm::GlmString& filePath);

        static int getCurrentVersionNumber();
        static const glm::GlmString getCharacterVersionName(int characterVersionNumber);

        size_t getGeometryAssetsCount() const;
        size_t getGeometryAssetsCount(short geometryTag) const;
        glm::Array<GeometryAssetV4>* getGeometryAssets(short geometryTag);

        /**
		* \brief Returns the geometry asset matching the input settings
		* \param idGeometry Id of the geometry file to return (unchanged if distanceForLOD < 0, else updated according to LOD)
		* \param distanceForLOD distance to use if several files are found in the geoTag. if < 0 will use idGeometry, else will find suitable LOD
		* \param randomPickLOD : a value from 0 to 1.0 to chose between files if in several files range
		*/
        const GeometryAssetV4* getGeometryAsset(short geometryTag, size_t& idGeometry, float distanceForLOD = -1, const PODArray<float>* overrideMinLodDistances = NULL, const PODArray<float>* overrideMaxLodDistances = NULL) const;

        const GeometryAssetV4* getGeometryAssetFirstLOD(short geometryTag) const;

        void getFirstAvailableAbsoluteGeometryFile(glm::GlmString& resultFilePath, const char* referenceDirectory) const;

        int findAssetGroupIdx(const glm::GlmString& assetGroupName) const;
        int findMeshAssetIdx(const glm::GlmString& meshAssetName) const;
        int findDeepDeformerAssetIdx(const glm::GlmString& deepDeformerAssetName) const;        
        int findShadingGroupIdx(const glm::GlmString& shadingGroupName) const;
        int findShaderAssetIdx(const glm::GlmString& shaderAssetName) const;
        int findRenderingTypeIdx(const glm::GlmString& renderingTypeName) const;

        int findShaderAttributeIdx(const glm::GlmString& shaderAttributeName) const;

        int findShaderAsset(const ShadingGroupV1& shadingGroup, const glm::GlmString& classification) const;

        static int findWeightOverride(const WeightOverrideV1*& weightOverride, const glm::Array<RenderingTypeV4>& renderingTypes, int renderingTypeIdx, int assetIndex, WeightOverrideType::Value overrideType);

        /**
		* \brief If the mesh is a fur, return the meshes the fur is applied to
		*/
        void getFurMeshes(const MeshAssetV8& meshAsset, glm::PODArray<int>& meshIds, glm::PODArray<bool>& furEnabledMeshes) const;

        /**
         * \brief V22 backwards compatibility
         * \note V23 adds Deep Deformer asset 
         */
        void createFromV23(const GolaemCharacterV23& characterOlderVersion);

        // Accessors
        const GenericSpecificConverterMappingV8& getConverterMapping() const;
        void setConverterMapping(const GenericSpecificConverterMappingV8& mapping);
        const CharacterUserDataV0& getCharacterUserData() const;
        void setCharacterUserData(const CharacterUserDataV0& userData);

        // Override Accessors
        void expandMainShaderAttributes();
        void setShadingNamespace(const glm::GlmString& shadingNamespace);

        void updateRelativePaths(const glm::GlmString& filePath);

        void autoComputePerceptionShape();

        void autoComputeSkeletonDisplay(const PODArray<int>& autoComputeBones);

    private:
        GLM_DISALLOW_COPY_AND_ASSIGN(GolaemCharacterV24);
        static void addAttributes();
    };

    // matches lod level and geometry tag
    bool GLM_CORE_API glmMeshAssetIsInGeoAsset(const MeshAsset& meshAsset, uint32_t geoAssetLodLevel, short geoAssetGeometryTag);
        
} // namespace glm
