//////////////////////////////////////////////////////////////////////////////////////////
// 
//  Copyright (c) 2024 Advanced Micro Devices, Inc. All Rights Reserved.
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in all
//  copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//  SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#define HIPRT_MAJOR_VERSION 3
#define HIPRT_MINOR_VERSION 1
#define HIPRT_PATCH_VERSION 0
#define HIPRT_HASH_VERSION 0xcb09c56

#define HIPRT_API_VERSION 3001
#define HIPRT_VERSION_STR "03001"
#define HIP_VERSION_STR "7.2"

#include <hiprt/hiprt_types.h>
#include <assert.h>

#ifdef __cplusplus
extern "C" {
#endif

#ifdef _WIN32
#define HIPRTAPI __stdcall
#else
#define HIPAPI
#define HIP_CB
#endif

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <windows.h>

/* Utility macros. */

using DynamicLibrary = HMODULE;

#define dynamic_library_open( path ) LoadLibraryA( path )
#define dynamic_library_close( lib ) FreeLibrary( lib )
#define dynamic_library_find( lib, symbol ) GetProcAddress( lib, symbol )
#else
#include <dlfcn.h>

using DynamicLibrary = void*;

#define dynamic_library_open( path ) dlopen( path, RTLD_NOW )
#define dynamic_library_close( lib ) dlclose( lib )
#define dynamic_library_find( lib, symbol ) dlsym( lib, symbol )
#endif

static DynamicLibrary hiprt_lib;

#define _LIBRARY_FIND_CHECKED( lib, name )               \
	name = (t##name*)dynamic_library_find( lib, #name ); \
	assert( name );

#define _LIBRARY_FIND( lib, name ) name = (t##name*)dynamic_library_find( lib, #name );

#define HIPRT_LIBRARY_FIND_CHECKED( name ) _LIBRARY_FIND_CHECKED( hiprt_lib, name )
#define HIPRT_LIBRARY_FIND( name ) _LIBRARY_FIND( hiprt_lib, name )
#if defined( _WIN32 )
#define HIPRT_LIB_NAME             \
	"hiprt" HIPRT_VERSION_STR "64" \
	".dll"
#else
#define HIPRT_LIB_NAME                \
	"libhiprt" HIPRT_VERSION_STR "64" \
	".so"
#endif

enum
{
	HIPRTEW_SUCCESS				= 0,
	HIPRTEW_ERROR_OPEN_FAILED	= -1,
	HIPRTEW_ERROR_ATEXIT_FAILED = -2,
	HIPRTEW_ERROR_OLD_DRIVER	= -3,
	HIPRTEW_NOT_INITIALIZED		= -4,
};

// function types
using thiprtCreateContext = hiprtError
HIPRTAPI( uint32_t hiprtApiVersion, hiprtContextCreationInput& input, hiprtContext& outContext );
using thiprtDestroyContext = hiprtError HIPRTAPI( hiprtContext context );

using thiprtCreateGeometry = hiprtError HIPRTAPI(
	hiprtContext				   context,
	const hiprtGeometryBuildInput& buildInput,
	const hiprtBuildOptions		   buildOptions,
	hiprtGeometry&				   geometryOut );

using thiprtDestroyGeometry = hiprtError HIPRTAPI( hiprtContext context, hiprtGeometry outGeometry );

using thiprtBuildGeometry = hiprtError HIPRTAPI(
	hiprtContext				   context,
	hiprtBuildOperation			   buildOperation,
	const hiprtGeometryBuildInput& buildInput,
	const hiprtBuildOptions		   buildOptions,
	hiprtDevicePtr				   temporaryBuffer,
	hiprtApiStream				   stream,
	hiprtGeometry				   geometryOut );

using thiprtBuildGeometries = hiprtError HIPRTAPI(
	hiprtContext				   context,
	hiprtBuildOperation			   buildOperation,
	uint32_t					   numGeometries,
	const hiprtGeometryBuildInput* buildInputs,
	const hiprtBuildOptions		   buildOptions,
	hiprtDevicePtr				   temporaryBuffer,
	hiprtApiStream				   stream,
	hiprtGeometry*				   geometriesOut );

using thiprtGetGeometryBuildTemporaryBufferSize = hiprtError HIPRTAPI(
	hiprtContext context, const hiprtGeometryBuildInput& buildInput, const hiprtBuildOptions buildOptions, size_t& outSize );

using thiprtCompactGeometry = hiprtError
HIPRTAPI( hiprtContext context, hiprtApiStream stream, hiprtGeometry geometryIn, hiprtGeometry& geometryOut );

using thiprtCompactGeometries = hiprtError HIPRTAPI(
	hiprtContext	context,
	uint32_t		numGeometries,
	hiprtApiStream	stream,
	hiprtGeometry*	geometriesIn,
	hiprtGeometry** geometriesOut );

using thiprtCreateScene = hiprtError HIPRTAPI(
	hiprtContext context, const hiprtSceneBuildInput& buildInput, const hiprtBuildOptions buildOptions, hiprtScene& outScene );

using thiprtCreateScenes = hiprtError HIPRTAPI(
	hiprtContext				context,
	uint32_t					numScenes,
	const hiprtSceneBuildInput* buildInputs,
	const hiprtBuildOptions		buildOptions,
	hiprtScene**				scenesOut );

using thiprtDestroyScene  = hiprtError	HIPRTAPI( hiprtContext context, hiprtScene outScene );
using thiprtDestroyScenes = hiprtError HIPRTAPI( hiprtContext context, uint32_t numScenes, hiprtScene* scenes );

using thiprtBuildScene = hiprtError HIPRTAPI(
	hiprtContext				context,
	hiprtBuildOperation			buildOperation,
	const hiprtSceneBuildInput* buildInput,
	const hiprtBuildOptions		buildOptions,
	hiprtDevicePtr				temporaryBuffer,
	hiprtApiStream				stream,
	hiprtScene					sceneOut );

using thiprtBuildScenes = hiprtError HIPRTAPI(
	hiprtContext				context,
	hiprtBuildOperation			buildOperation,
	uint32_t					numScenes,
	const hiprtSceneBuildInput* buildInputs,
	const hiprtBuildOptions		buildOptions,
	hiprtDevicePtr				temporaryBuffer,
	hiprtApiStream				stream,
	hiprtScene*					scenesOut );

using thiprtCompactScene = hiprtError
HIPRTAPI( hiprtContext context, hiprtApiStream stream, hiprtScene sceneIn, hiprtScene& sceneOut );

using thiprtCompactScenes = hiprtError
HIPRTAPI( hiprtContext context, uint32_t numScenes, hiprtApiStream stream, hiprtScene* scenesIn, hiprtScene** scenesOut );

using thiprtGetSceneBuildTemporaryBufferSize = hiprtError
HIPRTAPI( hiprtContext context, const hiprtSceneBuildInput& buildInput, const hiprtBuildOptions buildOptions, size_t* outSize );

using thiprtGetScenesBuildTemporaryBufferSize = hiprtError HIPRTAPI(
	hiprtContext				context,
	uint32_t					numScenes,
	const hiprtSceneBuildInput* buildInput,
	const hiprtBuildOptions		buildOptions,
	size_t&						sizeOut );

using thiprtGetGeometriesBuildTemporaryBufferSize = hiprtError HIPRTAPI(
	hiprtContext				   context,
	uint32_t					   numGeometries,
	const hiprtGeometryBuildInput* buildInputs,
	const hiprtBuildOptions		   buildOptions,
	size_t&						   sizeOut );

using thiprtCreateFuncTable = hiprtError
HIPRTAPI( hiprtContext context, uint32_t numGeomTypes, uint32_t numRayTypes, hiprtFuncTable& outFuncTable );

using thiprtSetFuncTable = hiprtError
HIPRTAPI( hiprtContext context, hiprtFuncTable funcTable, uint32_t geomType, uint32_t rayType, hiprtFuncDataSet set );

using thiprtDestroyFuncTable = hiprtError HIPRTAPI( hiprtContext context, hiprtFuncTable funcTable );

using thiprtCreateGlobalStackBuffer = hiprtError
HIPRTAPI( hiprtContext context, const hiprtGlobalStackBufferInput& input, hiprtGlobalStackBuffer& stackBufferOut );

using thiprtDestroyGlobalStackBuffer = hiprtError HIPRTAPI( hiprtContext context, hiprtGlobalStackBuffer stackBuffer );

using thiprtSaveGeometry = hiprtError HIPRTAPI( hiprtContext context, hiprtGeometry inGeometry, const char* filename );
using thiprtLoadGeometry = hiprtError HIPRTAPI( hiprtContext context, hiprtGeometry& outGeometry, const char* filename );

using thiprtSaveScene = hiprtError HIPRTAPI( hiprtContext context, hiprtScene inScene, const char* filename );
using thiprtLoadScene = hiprtError HIPRTAPI( hiprtContext context, hiprtScene& outScene, const char* filename );

using thiprtExportGeometryAabb = hiprtError
HIPRTAPI( hiprtContext context, hiprtGeometry inGeometry, hiprtFloat3& outAabbMin, hiprtFloat3& outAabbMax );
using thiprtExportSceneAabb = hiprtError
HIPRTAPI( hiprtContext context, hiprtScene inScene, hiprtFloat3& outAabbMin, hiprtFloat3& outAabbMax );

using thiprtBuildTraceKernels = hiprtError HIPRTAPI(
	hiprtContext	  context,
	uint32_t		  numFunctions,
	const char**	  funcNames,
	const char*		  src,
	const char*		  moduleName,
	uint32_t		  numHeaders,
	const char**	  headers,
	const char**	  includeNames,
	uint32_t		  numOptions,
	const char**	  options,
	uint32_t		  numGeomTypes,
	uint32_t		  numRayTypes,
	hiprtFuncNameSet* funcNameSets,
	hiprtApiFunction* functionsOut,
	hiprtApiModule*	  moduleOut,
	bool			  cache );

using thiprtBuildTraceKernelsFromBitcode = hiprtError HIPRTAPI(
	hiprtContext	  context,
	uint32_t		  numFunctions,
	const char**	  funcNames,
	const char*		  moduleName,
	const char*		  bitcodeBinary,
	size_t			  bitcodeBinarySize,
	uint32_t		  numGeomTypes,
	uint32_t		  numRayTypes,
	hiprtFuncNameSet* funcNameSets,
	hiprtApiFunction* functionsOut,
	bool			  cache );

using thiprtSetCacheDirPath = hiprtError HIPRTAPI( hiprtContext context, const char* path );
using thiprtSetLogLevel		= hiprtError	 HIPRTAPI( hiprtContext context, hiprtLogLevel level );

// function pointers
extern thiprtCreateContext*							hiprtCreateContext;
extern thiprtDestroyContext*						hiprtDestroyContext;
extern thiprtCreateGeometry*						hiprtCreateGeometry;
extern thiprtDestroyGeometry*						hiprtDestroyGeometry;
extern thiprtBuildGeometry*							hiprtBuildGeometry;
extern thiprtBuildGeometries*						hiprtBuildGeometries;
extern thiprtCompactGeometry*						hiprtCompactGeometry;
extern thiprtCompactGeometries*						hiprtCompactGeometries;
extern thiprtGetGeometryBuildTemporaryBufferSize*	hiprtGetGeometryBuildTemporaryBufferSize;
extern thiprtGetGeometriesBuildTemporaryBufferSize* hiprtGetGeometriesBuildTemporaryBufferSize;
extern thiprtCreateScene*							hiprtCreateScene;
extern thiprtCreateScenes*							hiprtCreateScenes;
extern thiprtDestroyScene*							hiprtDestroyScene;
extern thiprtDestroyScenes*							hiprtDestroyScenes;
extern thiprtBuildScene*							hiprtBuildScene;
extern thiprtBuildScenes*							hiprtBuildScenes;
extern thiprtGetSceneBuildTemporaryBufferSize*		hiprtGetSceneBuildTemporaryBufferSize;
extern thiprtGetScenesBuildTemporaryBufferSize*		hiprtGetScenesBuildTemporaryBufferSize;
extern thiprtCompactScene*							hiprtCompactScene;
extern thiprtCompactScenes*							hiprtCompactScenes;
extern thiprtCreateFuncTable*						hiprtCreateFuncTable;
extern thiprtSetFuncTable*							hiprtSetFuncTable;
extern thiprtDestroyFuncTable*						hiprtDestroyFuncTable;
extern thiprtCreateGlobalStackBuffer*				hiprtCreateGlobalStackBuffer;
extern thiprtDestroyGlobalStackBuffer*				hiprtDestroyGlobalStackBuffer;
extern thiprtSaveGeometry*							hiprtSaveGeometry;
extern thiprtLoadGeometry*							hiprtLoadGeometry;
extern thiprtSaveScene*								hiprtSaveScene;
extern thiprtLoadScene*								hiprtLoadScene;
extern thiprtExportGeometryAabb*					hiprtExportGeometryAabb;
extern thiprtExportSceneAabb*						hiprtExportSceneAabb;
extern thiprtBuildTraceKernels*						hiprtBuildTraceKernels;
extern thiprtBuildTraceKernelsFromBitcode*			hiprtBuildTraceKernelsFromBitcode;
extern thiprtSetCacheDirPath*						hiprtSetCacheDirPath;
extern thiprtSetLogLevel*							hiprtSetLogLevel;

#if defined( _ENABLE_HIPRTEW )
thiprtCreateContext*						 hiprtCreateContext;
thiprtDestroyContext*						 hiprtDestroyContext;
thiprtCreateGeometry*						 hiprtCreateGeometry;
thiprtDestroyGeometry*						 hiprtDestroyGeometry;
thiprtBuildGeometry*						 hiprtBuildGeometry;
thiprtBuildGeometries*						 hiprtBuildGeometries;
thiprtCompactGeometry*						 hiprtCompactGeometry;
thiprtCompactGeometries*					 hiprtCompactGeometries;
thiprtGetGeometryBuildTemporaryBufferSize*	 hiprtGetGeometryBuildTemporaryBufferSize;
thiprtGetGeometriesBuildTemporaryBufferSize* hiprtGetGeometriesBuildTemporaryBufferSize;
thiprtCreateScene*							 hiprtCreateScene;
thiprtCreateScenes*							 hiprtCreateScenes;
thiprtDestroyScene*							 hiprtDestroyScene;
thiprtDestroyScenes*						 hiprtDestroyScenes;
thiprtBuildScene*							 hiprtBuildScene;
thiprtBuildScenes*							 hiprtBuildScenes;
thiprtGetSceneBuildTemporaryBufferSize*		 hiprtGetSceneBuildTemporaryBufferSize;
thiprtGetScenesBuildTemporaryBufferSize*	 hiprtGetScenesBuildTemporaryBufferSize;
thiprtCompactScene*							 hiprtCompactScene;
thiprtCompactScenes*						 hiprtCompactScenes;
thiprtCreateFuncTable*						 hiprtCreateFuncTable;
thiprtSetFuncTable*							 hiprtSetFuncTable;
thiprtDestroyFuncTable*						 hiprtDestroyFuncTable;
thiprtCreateGlobalStackBuffer*				 hiprtCreateGlobalStackBuffer;
thiprtDestroyGlobalStackBuffer*				 hiprtDestroyGlobalStackBuffer;
thiprtSaveGeometry*							 hiprtSaveGeometry;
thiprtLoadGeometry*							 hiprtLoadGeometry;
thiprtSaveScene*							 hiprtSaveScene;
thiprtLoadScene*							 hiprtLoadScene;
thiprtExportGeometryAabb*					 hiprtExportGeometryAabb;
thiprtExportSceneAabb*						 hiprtExportSceneAabb;
thiprtBuildTraceKernels*					 hiprtBuildTraceKernels;
thiprtBuildTraceKernelsFromBitcode*			 hiprtBuildTraceKernelsFromBitcode;
thiprtSetCacheDirPath*						 hiprtSetCacheDirPath;
thiprtSetLogLevel*							 hiprtSetLogLevel;
#endif

static DynamicLibrary dynamic_library_open_find( const char** paths )
{
	int i = 0;
	while ( paths[i] != NULL )
	{
		DynamicLibrary lib = dynamic_library_open( paths[i] );
		if ( lib != NULL )
		{
			return lib;
		}
		++i;
	}
	return NULL;
}

static void hiprtewHipExit( void )
{
	if ( hiprt_lib != NULL )
	{
		/*  Ignore errors. */
		dynamic_library_close( hiprt_lib );
		hiprt_lib = NULL;
	}
}

static void hiprtewInit( int* resultDriver )
{
#if defined( __APPLE__ )
	const char* hiprt_paths[] = { "", NULL };
#else
	const char* hiprt_paths[] = { HIPRT_LIB_NAME, NULL };
#endif
	static int initialized	  = 0;
	static int s_resultDriver = 0;

	if ( initialized )
	{
		*resultDriver = s_resultDriver;
		return;
	}

	initialized = 1;

	int error = atexit( hiprtewHipExit );
	if ( error )
	{
		s_resultDriver = HIPRTEW_ERROR_ATEXIT_FAILED;
		*resultDriver  = s_resultDriver;
		return;
	}

	// may be check for old hiprt version

	/* Load library. */
	hiprt_lib = dynamic_library_open_find( hiprt_paths );

	if ( hiprt_lib == NULL )
	{
		s_resultDriver = HIPRTEW_ERROR_OPEN_FAILED;
		*resultDriver  = s_resultDriver;
		return;
	}

	/* Fetch all function pointers. */
	HIPRT_LIBRARY_FIND_CHECKED( hiprtCreateContext );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtDestroyContext );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtCreateGeometry );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtDestroyGeometry );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtBuildGeometry );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtBuildGeometries );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtGetGeometryBuildTemporaryBufferSize );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtGetGeometriesBuildTemporaryBufferSize );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtCreateScene );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtCreateScenes );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtDestroyScene );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtDestroyScenes );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtBuildScene );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtBuildScenes );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtGetSceneBuildTemporaryBufferSize );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtGetScenesBuildTemporaryBufferSize );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtCreateFuncTable );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtSetFuncTable );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtDestroyFuncTable );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtCreateGlobalStackBuffer );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtDestroyGlobalStackBuffer );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtSaveGeometry );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtLoadGeometry );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtSaveScene );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtLoadScene );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtExportGeometryAabb );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtExportSceneAabb );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtBuildTraceKernels );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtBuildTraceKernelsFromBitcode );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtSetCacheDirPath );
	HIPRT_LIBRARY_FIND_CHECKED( hiprtSetLogLevel );

	s_resultDriver = HIPRTEW_SUCCESS;
	*resultDriver  = s_resultDriver;
}

#ifdef __cplusplus
}
#endif // cpp
