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

#pragma once

#include "glmArray.h"
#include "glmMap.h"
#include "glmVector3.h"
#include "glmString.h"
#include "glmCrowdIOAPI.h"
#include "glmCoreDefinitions.h"

#if GLM_IS_PLATFORM_WINDOWS
#include <glmWindows.h>
#endif

namespace glm
{ 
	namespace crowdio
	{
		/// Target modes
        struct GLM_CROWDIO_API DeepFrameworkDeviceModeWrapper
		{
			enum Value
			{
				CPU_PYTORCH = 0,
				GPU_PYTORCH_FP32,
				GPU_PYTORCH_FP16,
				GPU_TENSORRT,
				END
			};
		};

		struct GLM_CROWDIO_API DeepMeshVertexDefinition
		{
		public:
			DeepMeshVertexDefinition() {}

			DeepMeshVertexDefinition(int32_t start, int32_t count) : _startVertexIndex(start), _vertexCount(count)
			{}

			int32_t _startVertexIndex = -1;
			int32_t _vertexCount = -1;
		};

		class GLM_CROWDIO_API DeepInferenceWrapper
		{
		public:

			DeepInferenceWrapper();  // eventually will load deep library, replace #if GLM_USE_DEEP_INFERENCE by if(deepAvailable)
			~DeepInferenceWrapper();

			//int32_t getDeepAPIVersion(); // insure we have a compatible API and GolaemCore versions

			void initDeepLibrary();

			bool isDeepAvailable() { return _deepIsAvailable; }

			bool loadModel(const GlmString& IOConfFilepath, DeepFrameworkDeviceModeWrapper::Value device, DeepFrameworkDeviceModeWrapper::Value subspacesDevice, bool PCAOnCPU); // -> garder ioConf et DeepModelData, retourne void* modelData
			
			bool unloadModel(const GlmString& IOConfFilePath);

			bool getInputNames(const GlmString& IOConfFilePath, Array<GlmString>& jointNames, Array<GlmString>& matrixNames, Array<GlmString>& floatNames);

			bool getMeshNames(const GlmString& IOConfFilePath, Array<GlmString>& replacedNames, Array<GlmString>& linearNames);

			bool getMeshVertexStartIndexAndCount(const GlmString& IOConfFilePath, PODArray<int>& startVertices, PODArray<int>& vertexCount);

			// returns the total vertices count
			int getMeshNamesAndCount(const GlmString& IOConfFilepath, GlmMap<GlmString, DeepMeshVertexDefinition>& meshVertexDef);

			//bool getOutputFloatCount(const GlmString& IOConfFilePath, int& outputFloatCount);

			// a user is one operation of one mesh, for one entity, for one frame
			bool registerUser(const GlmString& IOConfFilepath, void*& modelUserData); // return deepModeluserData ID// retainModel should be default, and force clear when 1
            
			bool setQuality(void* modelUserData, float quality); // return deepModeluserData ID// retainModel should be default, and force clear when 1

			bool infer(void* modelUserData, size_t batchCount, const PODArray<float>& inputs, PODArray<float>& outputs);

			bool unregisterUser(void* modelUserData);

			void setRetainUnusedModels(bool retain);

			void clearUnusedModels();

			void finishDeepLibrary();

		protected:
			bool _deepIsAvailable = false;

#if GLM_IS_PLATFORM_LINUX
			void* _deepInferenceLib = NULL;
#elif GLM_IS_PLATFORM_WINDOWS
			HMODULE _DLLHandle = NULL;
#endif

			int32_t (*getDeepAPIVersionPtr)() = NULL;
			void (*initDeepLibraryPtr)() = NULL;
            bool (*loadModelPtr)(const char* IOConfFilepath, int /*DeepFrameworkDeviceModeWrapper::Value*/ differentialDevice, int /*DeepFrameworkDeviceModeWrapper::Value*/ subspaceDevice, bool PCAOnCPU) = NULL;
			bool (*isLoadedPtr)(const char* IOConfFilepath) = NULL;
			bool (*unloadModelPtr)(const char* IOConfFilepath) = NULL;
			int (*getMaxNameLengthPtr)() = NULL;
			bool (*getInputCountPtr)(const char* IOConfFilepath, int& jointCount, int& matrixCount, int& floatCount);
			bool (*getInputNamesPtr)(const char* IOConfFilepath, char** jointNames, char** matrixNames, char** floatNames);
			bool (*getMeshCountPtr)(const char* IOConfFilepath, int& meshCount);
			bool (*getMeshNamesPtr)(const char* IOConfFilepath, char** replacedNames, char** linearNames);
			bool (*getMeshVertexStartIndexAndCountPtr)(const char* IOConfFilepath, int* startVertices, int* vertexCount);
			bool (*registerUserPtr)(const char* IOConfFilepath, void*& modelUserData) = NULL;
            bool (*setQualityPtr)(void* modelUserData, float quality);
			bool (*getOutputFloatCountPtr)(void* modelUserData, int& outputFloatCount);
            bool (*inferPtr)(void* modelUserData, const float* inputs, size_t inputSize, float* outputs, size_t outputSize) = NULL;
			bool (*unregisterUserPtr)(void* modelUserData) = NULL;
			void (*finishDeepLibraryPtr)() = NULL;
			void (*setRetainUnusedModelsPtr)(bool) = NULL;
			void (*clearUnusedModelsPtr)() = NULL;
		};
	}
}