/*------------------------------------------------------------------------------
	FILE	:	Interface.c
	CREATED	:	98/05/21
	AUTHOR	:	Catalin Cocos
	CONTENTS:	user end functions
------------------------------------------------------------------------------*/

#include "StdInc.h"
#include "Interface.h"

#include "Register.h"
#include "Parser.h"
#include "Refs.h"
#include "Error.h"

#ifdef LDT_LOG_STATISTICS
int LogFileOpened = 0;

TimerStruct TotalFlushtime = {0};
TimerStruct Totalsolvetime = {0};
TimerStruct Totalreadtime = {0};
TimerStruct TotalPostprtime = {0};
unsigned TotalFileCount = 0, TotalNumLinks = 0;
double TotalFileSize = 0.;

#endif

#ifdef LDT_LOG_MEMORY

unsigned long g_allocsize = 0;
unsigned long g_Maxallocsize = 0;
unsigned long g_size;

void* g_palloc;

unsigned long g_allocCount = 0;
unsigned long g_reallocCount = 0;
unsigned long g_freeCount = 0;

#endif

int g_nInitialized = 0;
extern int g_isFileNameValid = 1;

LDT_tdst_Link *g_pGetFrom = NULL;
unsigned short g_usLinkingPerstincence;

/* Link-Value association array
----------------------------------*/

LDT_tdst_DSArray g_arLinkAssocTable;



/* Initialization/Closing functions
-------------------------------------*/


/*------------------------------------------------------------------------------
	DESC.	:	initialization method can be called any number of times.
				The method must be called before using the other module functions !!!!!!
	OUTPUT	:	0 if the module was already initialized, 1 otherwise 
	CREATED	:	98/10/07        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_Initialize()
{
	if( g_nInitialized ) return 0;
	#ifdef LDT_USE_MMG /* initialization of the memory module */
	LDT_fn_v_Mem_InitModule();
	#endif /* LDT_USE_MMG */
	
	#ifdef LDT_LOG_MEMORY
	g_allocsize = 0;
	g_Maxallocsize = 0;
	g_allocCount = 0;
	g_reallocCount = 0;
	g_freeCount = 0;
	#endif

	#ifdef LDT_LOG_STATISTICS
	TotalFileCount = 0;
	TotalFileSize = 0.;
	TotalNumLinks = 0;
	TotalFlushtime.TimeCounter = 0;
	Totalsolvetime.TimeCounter = 0;
	Totalreadtime.TimeCounter = 0;
	TotalPostprtime.TimeCounter = 0;
	{
		FILE* file = fopen("LDT.log", LogFileOpened++?"a+":"w");
		time_t strTime;
		struct tm *newtime;
		time( &strTime );
		newtime = localtime( &strTime );
		fprintf(file, "===================================================================\n" );
		fprintf(file, "LDT module initialized: %s", asctime( newtime ) );
		fprintf(file, "===================================================================\n" );
		fclose(file);
	}
	#endif
	g_usLinkingPerstincence = 0; /* maximum linking perstincence */
	/* initialize the section type registration table */
	LDT_DSAr_Init( &g_ArSectionCallbacks );
	LDT_DSAr_SetSortMethod( &g_ArSectionCallbacks, LDT_Compare_CallbackEntries);
	LDT_DSAr_SetGranularity( &g_ArSectionCallbacks, 25 );

	/* initialize the file type registration table */
	LDT_DSAr_Init( &g_ArFileCallbacks );
	LDT_DSAr_SetSortMethod( &g_ArFileCallbacks, LDT_Compare_CallbackEntries);
	LDT_DSAr_SetGranularity( &g_ArFileCallbacks, 25 );

	/* initialize the paths array */
	LDT_DSAr_Init( &g_ArBaseDirectories );

	/* initialize the paths array */
	LDT_DSAr_Init( &g_ArPaths );
	LDT_DSAr_SetSortMethod( &g_ArPaths, LDT_Compare_CallbackEntries);
	LDT_DSAr_SetGranularity( &g_ArPaths, 25 );

	/* initialize the path strings array */
	LDT_DSAr_Init( &g_ArPathStrings );
	LDT_DSAr_SetSortMethod( &g_ArPathStrings, LDT_Compare_Strings);
	LDT_DSAr_SetGranularity( &g_ArPathStrings, 25 );
	
	/* initialize the post-process registration table */
	LDT_DSAr_Init( &g_ArSolvers );
	LDT_DSAr_SetSortMethod( &g_ArSolvers, SortSolvers );
	LDT_DSAr_SetGranularity( &g_ArSolvers, 25 );

	/* contents initialization */
	LDT_DSAr_Init( &TheFileDesc.arContents );
	LDT_DSAr_SetSortMethod( &TheFileDesc.arContents, LDT_Compare_Positions );
	LDT_DSAr_SetGranularity( &TheFileDesc.arContents, 100 );
	/* contents initialization */
#ifdef LDT_LOG_STATISTICS
	InitializeTimingEnvironment();
#endif

	#ifdef LDT_MULTITHREADED
	InitializeCriticalSection( &GReadExclusion );
	#endif
	/* initialize the parser */
	InitParser();

	/* first instance activated */
	#ifdef LDT_LOG_ERRORS
	InitErrFile();
	#endif

	/* initialize the link/value table */
	LDT_DSAr_Init( &g_arLinkAssocTable );
	LDT_DSAr_SetSortMethod( &g_arLinkAssocTable, LDT_Compare_LinkValues);
	LDT_DSAr_SetGranularity( &g_arLinkAssocTable, 100 );

	/* initialize the string array */
	LDT_DSAr_Init( &g_ArFilenames );
	LDT_DSAr_SetSortMethod( &g_ArFilenames, LDT_Compare_Strings);
	LDT_DSAr_SetGranularity( &g_ArFilenames, 100 );

	/* initialize the reference table */
	LDT_DSAr_Init( &g_ArRefs );
	LDT_DSAr_SetSortMethod( &g_ArRefs, LDT_Compare_Refs);
	LDT_DSAr_SetGranularity( &g_ArRefs, 100 );

	/* initialize the link table */ 
	LDT_DSHs_Init(&g_HashLinks,
				(int(*)(void*))LDT_Link_Dispersion,
				LDT_Compare_Links,
				(void(*)(void*))LDT_Delete_Link);
	LDT_DSHs_SetGranularity( &g_HashLinks, 100 );
	return (g_nInitialized = 1); /* the module was initialized */
}

/*------------------------------------------------------------------------------
	DESC.	:	Closing method can be called any number of times.
	OUTPUT	:	0 if the module was already closed, 1 otherwise
	CREATED	:	98/05/21        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_Close()
{
	if(!g_nInitialized) return 0;
/*_____________________________________________________________________________*/

	LDT_FlushAndForget(0);		/* load the unresolved refs and delete the internal linktable */
	LDT_CleanUpRegistrations();	/* delete the registered stuff */

/*ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ*/
	/* close the link table (deleting the existing links) */
	LDT_DSHs_Close( &g_HashLinks, 1);

	#ifdef LDT_MULTITHREADED
	DeleteCriticalSection( &GReadExclusion );
	#endif

	#ifdef LDT_LOG_ERRORS
	CLoseErrFile();
	#endif

	#ifdef LDT_LOG_STATISTICS
	{
		TimerStruct TempTimer;
		FILE* file = fopen("LDT.log", LogFileOpened++?"a+":"w");
		time_t strTime;
		struct tm *newtime;
		time( &strTime );
		newtime = localtime( &strTime );
		fprintf(file, "===================================================================\n" );
		fprintf(file, "Totals:\n\n" );

		fprintf(file, "Flush Time:\t\t%.3f\n", ReadTimer(&TotalFlushtime) );
		fprintf(file, "Read Time:\t\t%.3f\n", ReadTimer(&Totalreadtime) );
		#ifdef LDT_MULTITHREADED
		fprintf(file, "Process Time:\t\t%.3f\n", ReadTimer(&Totalsolvetime) );
		#else
		TempTimer.TimeCounter = Totalsolvetime.TimeCounter - Totalreadtime.TimeCounter;
		fprintf(file, "Process Time:\t\t%.3f\n", ReadTimer(&TempTimer));
		#endif		
		fprintf(file, "Postprocess Time:\t%.3f\n", ReadTimer(&TotalPostprtime) );

		#ifdef LDT_MULTITHREADED
		{
			TempTimer.TimeCounter = TotalPostprtime.TimeCounter + Totalsolvetime.TimeCounter + Totalreadtime.TimeCounter;
			fprintf(file, "\nMultithreaded disk read performance:\n");
			fprintf(file, "Useful Time:\t\t%.3f\n", ReadTimer(&TempTimer));
			fprintf(file, "Absloute Time Gain:\t%.3f\n", ReadTimer(&TempTimer) - ReadTimer(&TotalFlushtime));
			TempTimer.TimeCounter -= TotalFlushtime.TimeCounter;
			fprintf(file, "Disk Read Savings: \t%.1f%%\n", (ReadTimer(&TempTimer))*100/ReadTimer(&Totalreadtime));
		}
		#endif

		fprintf(file, "\nSections:\t\t%u", TotalNumLinks );
		fprintf(file, "\nFiles:\t\t\t%u", TotalFileCount );
		fprintf(file, "\nRead:\t\t\t%.3f MB\n\n", TotalFileSize/1024/1024 );

		#ifdef LDT_LOG_MEMORY
		fprintf(file, "\nMaximum Memory Used:\t%.3f MB\n", ((double)g_Maxallocsize)/1024/1024 );
		fprintf(file, "\nMalloc Count:\t\t%u\n", g_allocCount );
		fprintf(file, "Free Count:\t\t%u\n", g_freeCount );
		fprintf(file, "Realloc Count:\t\t%u\n", g_reallocCount );
		fprintf(file, "Memory Leaks:\t\t%.3f KB\n", ((double)g_allocsize)/1024 );
		fprintf(file, "\nWarning, time estimation is affected by the memory measurements. \n\n");
		#endif

		fprintf(file, "LDT module closed: %s", asctime( newtime ) );
		fprintf(file, "===================================================================\n" );
		fprintf(file, "\n\n\n");
		fclose(file);
	}
	#endif
		
	#ifdef LDT_USE_MMG /* release the  memory module */
	LDT_fn_v_Mem_CloseModule();
	#endif /* DMS_USE_MMG */
	
	g_nInitialized = 0;
	return 1;
}

/*------------------------------------------------------------------------------
	DESC.	:	unregisters all registered types, paths and solvers - memory clean-up
	CREATED	:	98/09/22        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
void LDT_CleanUpRegistrations()
{
	int i, j;
//	if(InstanceCount)
//	{
//		ERRoR(0, NULL, 0, "All types and paths registrations are deleted during a loading session");
//		return;
//	}
	if( g_nInitialized == 0 ) return;
	/* close the section type registration table */
	for( i = LDT_DSAr_GetUpperBound(&g_ArSectionCallbacks); i>=0; i--)
	{
		free(((LDT_tdst_CallbackEntry*)g_ArSectionCallbacks.pData[i])->szType);
		free( g_ArSectionCallbacks.pData[i]);
	}
    LDT_DSAr_RemoveAll( &g_ArSectionCallbacks );

	/* close the file type registration table */
	for( i = LDT_DSAr_GetUpperBound(&g_ArFileCallbacks); i>=0; i--)
	{
		free(((LDT_tdst_CallbackEntry*)g_ArFileCallbacks.pData[i])->szType);
		free( g_ArFileCallbacks.pData[i]);
	}
    LDT_DSAr_RemoveAll( &g_ArFileCallbacks );

	/* close the post-process registration table */
	{
		LDT_tdst_DSArray* pRef;
		for( i = LDT_DSAr_GetUpperBound(&g_ArSolvers); i>=0; i--)
		{
			pRef = (LDT_tdst_DSArray*)((tdst_Solver*)g_ArSolvers.pData[i])->hRef;
			if(pRef)
				for( j = LDT_DSAr_GetUpperBound(pRef); j>=0; j--)
					free( pRef->pData[j]);
			LDT_DSAr_delete( pRef );
			free( g_ArSolvers.pData[i]);
		}
	}
	LDT_DSAr_RemoveAll( &g_ArSolvers );

	/* close the base paths table */
	for( i = LDT_DSAr_GetUpperBound(&g_ArBaseDirectories); i>=0; i--)
		free(g_ArBaseDirectories.pData[i]);
	LDT_DSAr_RemoveAll(&g_ArBaseDirectories);

	/* close the path table */
	for( i = LDT_DSAr_GetUpperBound(&g_ArPaths); i>=0; i--)
	{
		LDT_DSAr_RemoveAll(&((LDT_tdst_TypePaths*)g_ArPaths.pData[i])->arPaths);
		free( ((LDT_tdst_TypePaths*)g_ArPaths.pData[i])->szExtension );
		free(g_ArPaths.pData[i]);
	}
	LDT_DSAr_RemoveAll(&g_ArPaths);

	/* close the path strings table */
	for( i = LDT_DSAr_GetUpperBound(&g_ArPathStrings); i>=0; i--)
		free(g_ArPathStrings.pData[i]);
	LDT_DSAr_RemoveAll(&g_ArPathStrings);

}

/* Link-Value association functions
-------------------------------------*/
/*------------------------------------------------------------------------------
	DESC.	:	associates an userd-supplied value to a link
	CREATED	:	98/08/19        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
void LDT_SetLinkValue( LDT_tdst_Link* pLink, unsigned long value )
{
	int idx, iidx;
	idx = LDT_DSAr_Index( &g_arLinkAssocTable, (LDT_tdst_LinkValue*)&pLink, &iidx );
	if( idx<0 )
	{	/*  the link is not present in the table */
		LDT_tdst_LinkValue* pNewAssoc = malloc(sizeof(LDT_tdst_LinkValue));
		pNewAssoc->pLink = pLink;
		pNewAssoc->value = value;
		LDT_DSAr_InsertAt( &g_arLinkAssocTable, iidx, pNewAssoc );
	}
	else
	{
		((LDT_tdst_LinkValue*)g_arLinkAssocTable.pData[idx])->value = value; /* link already present -> change the associated value */
#if defined(LDT_ERROR_TREATMENT) && ( LDT_WARNING_LEVEL >= 3 || LDT_ERROR_LEVEL >= 3)
		ERRoR(3, pLink, 0, "Link associated value already present. The old value was overwritten.");
#endif
	}

}

/*------------------------------------------------------------------------------
	DESC.	:	retrieves the user-supplied value associated to the link
	CREATED	:	98/08/19        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
unsigned long LDT_GetLinkValue( LDT_tdst_Link* pLink )
{
	int idx = LDT_DSAr_Index( &g_arLinkAssocTable, (LDT_tdst_LinkValue*)&pLink, NULL );
	if(idx < 0)
	{
#if defined(LDT_ERROR_TREATMENT) && ( LDT_WARNING_LEVEL >= 3 || LDT_ERROR_LEVEL >= 3)
		ERRoR(3, pLink, 0, "No associated value available for this link.");
#endif
		return 0;
	}
	return ((LDT_tdst_LinkValue*)LDT_DSAr_GetAt( &g_arLinkAssocTable, idx ))->value;
}

/*------------------------------------------------------------------------------
	DESC.	:	retrieves and removes the user-supplied value associated to the link
	CREATED	:	98/09/31        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
unsigned long LDT_RemoveLinkValue( LDT_tdst_Link* pLink )
{
	LDT_tdst_LinkValue* pAssoc;
	unsigned long value; 
	int idx = LDT_DSAr_Index( &g_arLinkAssocTable, (LDT_tdst_LinkValue*)&pLink, NULL );
	if(idx < 0)
	{
#if defined(LDT_ERROR_TREATMENT) && ( LDT_WARNING_LEVEL >= 3 || LDT_ERROR_LEVEL >= 3)
		ERRoR(3, pLink, 0, "No associated value available for this link.");
#endif
		return 0;
	}
	pAssoc = (LDT_tdst_LinkValue*)LDT_DSAr_RemoveAt( &g_arLinkAssocTable, idx );
	value = pAssoc->value;
	free(pAssoc);
	return value;
}

/* Loading functions
-------------------------------------*/
/*------------------------------------------------------------------------------
	DESC.	:	Find a link
	OUTPUT	:	The pointer to link ( NULL if not found )
				-1 on error;
	CREATED	:	98/08/19        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
LDT_tdst_Link* LDT_GetLink( char *szSection )
{
	LDT_tdst_Link *pLink, *pRetVal;
	if( szSection==NULL)
		return NULL;
	pLink=LDT_CreateLinkFromReference( szSection );
	pRetVal=LDT_DSHs_GetEqOf(&g_HashLinks, (void *)pLink);
	LDT_Delete_Link( pLink );
#if defined(LDT_ERROR_TREATMENT) && ( LDT_WARNING_LEVEL >= 2 || LDT_ERROR_LEVEL >= 2)
	if(!pRetVal)
		ERRoR(2, NULL, 0, "No link matches this description.");
#endif
	return pRetVal;
}


/*------------------------------------------------------------------------------
	DESC.	:	Load a section
	OUTPUT	:	The allocated object(can be NULL)
				-1 on error;
	CREATED	:	98/05/22        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
void* LDT_LoadSection( char* szSection )
{
	int errcode, level;
	LDT_tdst_Link *Link = NULL;
	g_pGetFrom=NULL;	/* MP-for refs */
	if(szSection)		/* loading request */
	{
		Link = LDT_CreateLinkFromReference( szSection );/* normal call */
		if( !Link ) return (void*)-1;					/* error */
		Link = LDT_DSHs_Insert( &g_HashLinks, Link, 1);
		g_pGetFrom=Link; /* MP-for refs */
		if( Link->dwFlags & LDT_LF_ALLOCATED )
			return Link->pObject;	/* the link was already processed */
		g_isFileNameValid = 0;
		errcode = Link->Type->Create( Link );		/* create the object */ 
		g_isFileNameValid = 1;
		if(errcode)
		{
#if defined(LDT_ERROR_TREATMENT) 
			ERRoR(0, Link, errcode, "Unable to create associated object.");
#endif
			return (void*)-1;
		}
		Link->dwFlags |= LDT_LF_ALLOCATED;								/* set the corresponding flag */
		AddReference( Link );											/* append the reference */
		return Link->pObject;
	}
	if( g_FileDesc ) 		/* sub-section loading */
	{
		void* pObject;
		LDT_tdst_CallbackEntry* Type = GetType( g_szType, LDT_REG_SECTION );
		Link=LDT_Create_Link( g_szId, g_szParent, g_FileDesc->szFile, Type, g_usLinkingPerstincence, NULL, pParent[g_FileDesc->iLevel-1] );
		Link=(LDT_tdst_Link *)LDT_DSHs_Insert( &g_HashLinks, (void *)Link, 1 );

		pParent[ g_FileDesc->iLevel ]=Link;
		g_CrtSection=Link;

		if( Link->dwFlags&LDT_LF_LOADED )
		{
			pObject = Link->pObject;	/* the link was already processed */
			LDT_SkipSection();
			if(	Link->dwFlags & LDT_LF_VOLATILE)
				free ( LDT_DSHs_RemoveEqOf( &g_HashLinks, Link)); /* remove volatile subsection*/
			return pObject;
		}
		if( (Link->dwFlags & LDT_LF_ALLOCATED) == 0 )
		{
			errcode =  Type->Create( Link );
			if(errcode )
			{
#if defined(LDT_ERROR_TREATMENT)
				ERRoR(0, Link, errcode, "Unable to create associated object.");
#endif
				return (void*)-1;
			}
			Link->dwFlags |= LDT_LF_ALLOCATED; /* the link was allocated */
		}
		level = g_FileDesc->iLevel - 1;
		errcode = Type->Load( Link );
		pObject =  Link->pObject;
		if(errcode)
		{
#if defined(LDT_ERROR_TREATMENT)
			ERRoR(0, Link, errcode, "Unable to load section. User error triggered in callback.");
#endif
			pObject = (void*)-1;
		}
		else if(level != g_FileDesc->iLevel)
		{
#if defined(LDT_ERROR_TREATMENT)
			ERRoR(0, Link, 0, "Erroneous load callback. Failure to reach the section end mark! (premature return or subsections not treated)");
#endif
			pObject = (void*)-1;
		}
		Link->dwFlags |= LDT_LF_LOADED; /* the link was loaded */
		if(	Link->dwFlags & LDT_LF_VOLATILE)
			LDT_Delete_Link( LDT_DSHs_RemoveEqOf( &g_HashLinks, Link)); /* remove volatile subsection*/
		return pObject;
	}
#if defined(LDT_ERROR_TREATMENT)
	ERRoR(0, NULL, 0, "Cannot Load Subsection. Called from outside any \"Load\" callback.");
#endif
	return (void*)-1;
}

/*------------------------------------------------------------------------------
	DESC.	:	Load a section
	OUTPUT	:	pointer to the allocated object
				( can be different from the received parameter -
				if the section was previously allocated)
				-1 on error;
	CREATED	:	98/05/22        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
void* LDT_LoadSectionAt( char* szSection, void* pSection )
{
	int errcode, level;
	LDT_tdst_Link *Link = NULL;
	g_pGetFrom=NULL;	/* MP-for refs */
	if(szSection)		/* loading request */
	{
		Link = LDT_CreateLinkFromReference( szSection );/* normal call */
		if( !Link ) return (void*)-1;					/* error */
		Link = LDT_DSHs_Insert( &g_HashLinks, Link, 1);
		g_pGetFrom=Link; /* MP-for refs */
		if( Link->dwFlags & LDT_LF_ALLOCATED )
			return Link->pObject;	/* the link was already processed */
		Link->pObject = pSection;										/* copy the object */ 
#if defined(LDT_ERROR_TREATMENT) && ( LDT_WARNING_LEVEL >= 2 || LDT_ERROR_LEVEL >= 2)
		if(!pSection)
			ERRoR(2, Link, 0, "Associated object is set to NULL.");
#endif
		Link->dwFlags |= LDT_LF_ALLOCATED;								/* set the corresponding flag */
		AddReference( Link );											/* append the reference */
		return Link->pObject;
	}
	if( g_FileDesc ) 		/* sub-section loading */
	{
		void* pObject;
		LDT_tdst_CallbackEntry* Type = GetType( g_szType, LDT_REG_SECTION );
		Link=LDT_Create_Link( g_szId, g_szParent, g_FileDesc->szFile, Type, g_usLinkingPerstincence, NULL, pParent[g_FileDesc->iLevel-1] );
		Link=(LDT_tdst_Link *)LDT_DSHs_Insert( &g_HashLinks, (void *)Link, 1 );

		pParent[ g_FileDesc->iLevel ]=Link;
		g_CrtSection=Link;

		if( Link->dwFlags&LDT_LF_LOADED )
		{
			pObject = Link->pObject;	/* the link was already processed */
			LDT_SkipSection();
			if(	Link->dwFlags & LDT_LF_VOLATILE)
				free ( LDT_DSHs_RemoveEqOf( &g_HashLinks, Link)); /* remove volatile subsection*/
			return pObject;
		}
		if( (Link->dwFlags & LDT_LF_ALLOCATED) == 0 )
		{
			Link->pObject = pSection;							/* copy the object */ 
#if defined(LDT_ERROR_TREATMENT) && ( LDT_WARNING_LEVEL >= 2 || LDT_ERROR_LEVEL >= 2)
			if(!pSection)
				ERRoR(2, Link, 0, "Associated object is set to NULL.");
#endif
			Link->dwFlags |= LDT_LF_ALLOCATED;					/* the link was allocated */
		}
		level = g_FileDesc->iLevel - 1;
		errcode = Type->Load( Link );
		pObject =  Link->pObject;
		if(errcode)
		{
#if defined(LDT_ERROR_TREATMENT) 
			ERRoR(0, Link, errcode, "Unable to load section. User error triggered in callback.");
#endif
			pObject = (void*)-1;
		}
		else if(level != g_FileDesc->iLevel)
		{
#if defined(LDT_ERROR_TREATMENT)
			ERRoR(0, Link, 0, "Erroneous load callback. Failure to reach the section end mark! (premature return or subsections not treated)");
#endif
			pObject = (void*)-1;
		}
		Link->dwFlags |= LDT_LF_LOADED; /* the link was loaded */
		if(	Link->dwFlags & LDT_LF_VOLATILE)
			LDT_Delete_Link( LDT_DSHs_RemoveEqOf( &g_HashLinks, Link)); /* remove volatile subsection*/
		return pObject;
	}
#if defined(LDT_ERROR_TREATMENT) 
	ERRoR(0, NULL, 0, "Cannot Load Subsection. Called from outside any \"Load\" callback.");
#endif
	return (void*)-1;
}

#ifdef LDT_MULTITHREADED

HANDLE GReadCount;			/* the number of files read from disk */
HANDLE GReadLimit;			/* the number of available files */
HANDLE GFileLimit = NULL;	/* the maximum number of files read in advance */

#pragma warning(disable: 4100) 
/*------------------------------------------------------------------------------
	DESC.	:	File Reading thread method
	CREATED	:	98/07/07        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
//unsigned int __stdcall DiskRead( void * p);

unsigned int __stdcall DiskRead( void * p)
{
	LDT_tdst_Refs * pRefs;
	LDT_tdst_MemFile* pFile;

	unsigned int uiPos=0;
	static char buffer[256];
	int ok=1;
//	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE ); 

	for(;;)
	{
		WaitForSingleObject( GReadLimit, INFINITE); /* wait to have the number of opened files less than the max allowed */
		WaitForSingleObject( GFileLimit, INFINITE); /* wait to have at least one file available for reading */
		EnterCriticalSection( &GReadExclusion );
		if((pRefs = g_pFileToRead)==NULL)
		{
			LeaveCriticalSection( &GReadExclusion );
			break;
		}
		g_pFileToRead = pRefs->p_NextReferencedFile;
		LeaveCriticalSection( &GReadExclusion );

//		SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); 
		pFile = AccessFile( pRefs->szFile ); /* open the file */
//		SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE ); 

		pRefs->pFile = pFile;
		ReleaseSemaphore( GReadCount, 1, NULL); /* the file was opened */
	}
	
	return 0;
}

#pragma warning(default: 4100) 
#endif

void LDT_SetLinkingPersistence (unsigned short Level)
{
	if( g_ArRefs.nSize )
		LDT_Flush();
	g_usLinkingPerstincence = Level;
}

void LDT_Forget(unsigned short Level)
{
	int i, gap, maxValue, HashDimension;
	LDT_tdst_DSArray* pCrtAr;
	void**	pData;

	LDT_DSAr_RemoveAll( &g_ArRefs ); /* the reference table is empty at this point - no other closing procedure necessary*/
	LDT_DSAr_RemoveAll( &TheFileDesc.arContents);

#ifdef LDT_USE_SINGLE_FILE_BUFFER
	if(GBuff) free(GBuff);
	GBuff = NULL;
	GBuffSize = 0;
#endif 

	if(Level)
	{	/* Partial clean-up. Not reccomended due to low speed performance */
		/* delete the invalid associations from the link/value table */
		for( i = gap = 0, maxValue = LDT_DSAr_GetSize(&g_arLinkAssocTable), pData = g_arLinkAssocTable.pData; i<maxValue; i++)
		{
			if(((short)(((LDT_tdst_LinkValue*)pData[i])->pLink->dwFlags)) >= Level )
			{
				free( pData[i]);
				gap++;
			}
			else
				pData[i-gap] = pData[i];
		}
		LDT_DSAr_SetSize(&g_arLinkAssocTable, maxValue - gap); /* adjust the size of the array */

		/* delete the not needed links from the links hash table */
		for(HashDimension = g_HashLinks.pfnDispersion( NULL ) - 1  ; HashDimension>=0; HashDimension--)
		{
			pCrtAr = g_HashLinks.pData + HashDimension;
			pData = pCrtAr->pData;
			for( i = gap = 0, maxValue = LDT_DSAr_GetSize(pCrtAr); i<maxValue; i++)
			{
				if(((short)(((LDT_tdst_Link*)pData[i])->dwFlags)) >= Level )
				{
					g_HashLinks.pfnDelete( pData[i]);
					gap++;
				}
				else
					pData[i-gap] = pData[i];
			}
			LDT_DSAr_SetSize(pCrtAr, maxValue - gap); /* adjust the size of the array */
		}
	}
	else
	{	/* Total clean-up. The normal operation */ 
		/*------------------------------- delete the existing links and the related data ----------------------*/

		/* close the link/value table(deleting all stored associations) */
		for( i = LDT_DSAr_GetUpperBound(&g_arLinkAssocTable), pData = g_arLinkAssocTable.pData; i>=0; i--)
			free( pData[i]);
		LDT_DSAr_RemoveAll( &g_arLinkAssocTable );
		/* close the string array (deleting all stored strings) */
		for( i = LDT_DSAr_GetUpperBound(&g_ArFilenames), pData = g_ArFilenames.pData; i>=0; i--)
			free( pData[i]);
		LDT_DSAr_RemoveAll( &g_ArFilenames );

		LDT_DSHs_RemoveAll( &g_HashLinks, 1);
	}
}

/*------------------------------------------------------------------------------
	DESC.	:	Reference solving function - performs loading & deletes the linktable afterwards
	OUTPUT	:	zero on error, nonzero if successful;
	CREATED	:	98/05/24        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_FlushAndForget(unsigned short Level)
{
	int ERR = LDT_Flush();
	LDT_Forget( Level );
	return ERR;
}

/*------------------------------------------------------------------------------
	DESC.	:	Reference solving function - performs the loading
	OUTPUT	:	zero on error, nonzero if successful;
	CREATED	:	98/05/24        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
extern unsigned char* g_ulPos;

int LDT_Flush()
{
	static int FlushLaunched = 0;
	int OK = 1, i;

	#ifdef LDT_LOG_STATISTICS
	unsigned CurrentLinks = 0;
	TimerStruct FlushTime = TotalFlushtime;
	TimerStruct SolveTime = Totalsolvetime;
	TimerStruct Readtime = Totalreadtime;
	TimerStruct Postprtime = TotalPostprtime ;
	unsigned FileCount = TotalFileCount;
	double FileSize = TotalFileSize;

	{	/* count the number of links */
		int HashDimension = g_HashLinks.pfnDispersion( NULL ) - 1 ;
		for( ; HashDimension>=0; HashDimension--)
			CurrentLinks += LDT_DSAr_GetSize( g_HashLinks.pData + HashDimension );
	}
	#endif

	if(FlushLaunched)
	{
#if defined(LDT_ERROR_TREATMENT)
		ERRoR(0, NULL, 0, "Recursive call to LDT_Flush() not allowed. Call aborted. ");
#endif
		return 0;
	}
	FlushLaunched = 1; /* sentinel */

	#ifdef LDT_LOG_STATISTICS
	StartTimer(&TotalFlushtime);
	#endif

	do
	{
		if(g_pFileToProcess)
		{	

			#ifdef LDT_MULTITHREADED
			
			unsigned thrdaddr;	/* the read thread */
			HANDLE Hthrd;		/* the read thread */

			GReadCount = CreateSemaphore( NULL, 0, LONG_MAX, NULL );				/* the number of files read from disk */
			GFileLimit = CreateSemaphore( NULL, g_ArRefs.nSize, LONG_MAX, NULL );	/* the number of available files */
			GReadLimit = CreateSemaphore( NULL, MAX_FILE_NO, LONG_MAX, NULL );		/* the maximum number of files read in advance */
		
			InitializeCriticalSection( &GReadExclusion );
			Hthrd = (void*)_beginthreadex(NULL, 0, DiskRead, NULL, 0, &thrdaddr);		/* start the thread that reads files from disk */
			#endif

			/* processing */
			for(;g_pFileToProcess;)
			{

				#ifdef LDT_MULTITHREADED
				WaitForSingleObject( GReadCount, INFINITE);
//				SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST ); 
				#endif

				#ifdef LDT_LOG_STATISTICS
				StartTimer(&Totalsolvetime);
				#endif

				if( Solve(g_pFileToProcess) ) /* the effective loading */
				{
#if defined(LDT_ERROR_TREATMENT) && ( LDT_WARNING_LEVEL >= 2 || LDT_ERROR_LEVEL >= 2)
					ERRoR(2, NULL, 0, "Unable to satisfy all loading requests for the previous file.");
#endif
					OK = 0;			/* Error encountered */
				}

				#ifdef LDT_LOG_STATISTICS
				StopTimer(&Totalsolvetime);
				#endif

				LDT_DSAr_RemoveAll(&g_pFileToProcess->Links);

				#ifdef LDT_MULTITHREADED
//				SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL ); 
				ReleaseSemaphore( GReadLimit, 1, NULL); /* signal that the file was processed */
				#endif
				{
					LDT_tdst_Refs * pRefs = g_pFileToProcess;
					g_pFileToProcess = g_pFileToProcess->p_NextReferencedFile;
					free( LDT_DSAr_RemoveAt( &g_ArRefs, LDT_DSAr_Index(&g_ArRefs, pRefs, NULL) ) );
				}

			}

			
			#ifdef LDT_MULTITHREADED
			ReleaseSemaphore( GReadLimit, 1, NULL );	/* allow the file reading thread to advance */
			ReleaseSemaphore( GFileLimit, 1, NULL );	/* allow the file reading thread to advance */
			WaitForSingleObject( Hthrd, INFINITE );
			CloseHandle( Hthrd );
			CloseHandle( GReadCount );
			CloseHandle( GFileLimit );
			CloseHandle( GReadLimit );

			GFileLimit = NULL;
			#endif
		}
			
		#ifdef LDT_LOG_STATISTICS
		StartTimer(&TotalPostprtime);
		#endif
		/* post-process */
		for( i=0; i<g_ArSolvers.nSize; i++ )
		{
			tdst_Solver *pSolver=(tdst_Solver *)LDT_DSAr_GetAt( &g_ArSolvers, i );
			pSolver->Solve( pSolver->hRef );
		}
		#ifdef LDT_LOG_STATISTICS
		StopTimer(&TotalPostprtime);
		#endif
	}
	while(g_pFileToProcess);

	#ifdef LDT_LOG_STATISTICS
	StopTimer(&TotalFlushtime);

	{	/* count the number of links */
		TimerStruct TempTimer;
		FILE* file = fopen("LDT.log", LogFileOpened++?"a+":"w");
		time_t strTime;
		struct tm *newtime;

		unsigned NumLinks = 0;
		int HashDimension = g_HashLinks.pfnDispersion( NULL ) - 1 ;
		for( ; HashDimension>=0; HashDimension--)
			NumLinks += LDT_DSAr_GetSize( g_HashLinks.pData + HashDimension );
		TotalNumLinks += NumLinks - CurrentLinks;

		FlushTime.TimeCounter = TotalFlushtime.TimeCounter - FlushTime.TimeCounter;
		SolveTime.TimeCounter = Totalsolvetime.TimeCounter - SolveTime.TimeCounter;
		Readtime.TimeCounter = Totalreadtime.TimeCounter - Readtime.TimeCounter;
		Postprtime.TimeCounter = TotalPostprtime.TimeCounter - Postprtime.TimeCounter;
		FileCount = TotalFileCount - FileCount;
		FileSize = TotalFileSize - FileSize;

		time( &strTime );
		newtime = localtime( &strTime );
		fprintf(file, "-------------------------------------------\n" );
		fprintf(file, "Flush Stats:\n\n" );

		fprintf(file, "Flush Time:\t\t%.3f\n", ReadTimer(&FlushTime) );
		fprintf(file, "Read Time:\t\t%.3f\n", ReadTimer(&Readtime) );
		#ifdef LDT_MULTITHREADED
		fprintf(file, "Process Time:\t\t%.3f\n", ReadTimer(&SolveTime) );
		#else
		TempTimer.TimeCounter = SolveTime.TimeCounter - Readtime.TimeCounter;
		fprintf(file, "Process Time:\t\t%.3f\n", ReadTimer(&TempTimer));
		#endif		
		fprintf(file, "Postprocess Time:\t%.3f\n", ReadTimer(&Postprtime) );

		#ifdef LDT_MULTITHREADED
		{
			TempTimer.TimeCounter = Postprtime.TimeCounter + SolveTime.TimeCounter + Readtime.TimeCounter;
			fprintf(file, "\nMultithreaded disk read performance:\n");
			fprintf(file, "Useful Time:\t\t%.3f\n", ReadTimer(&TempTimer));
			fprintf(file, "Absloute Time Gain:\t%.3f\n", ReadTimer(&TempTimer) - ReadTimer(&FlushTime));
			TempTimer.TimeCounter -= FlushTime.TimeCounter;
			fprintf(file, "Disk Read Savings: \t%.1f%%\n", (ReadTimer(&TempTimer))*100/ReadTimer(&Readtime));
		}
		#endif

		fprintf(file, "\nSections:\t\t%u", NumLinks - CurrentLinks );
		fprintf(file, "\nFiles:\t\t\t%u", FileCount );
		fprintf(file, "\nRead:\t\t\t%.3f MB\n\n", FileSize/1024/1024 );

		fprintf(file, "-------------------------------------------\n" );
		fprintf(file, "\n\n\n");
		fclose(file);

		}
		#endif /* LDT_LOG_STATISTICS */

	FlushLaunched = 0; /* sentinel removal */
	return OK; /* return the error code */
}


/* In-callback link manipulation functions
-------------------------------------*/

void LDT_SetVolatileSubsection( LDT_tdst_Link* Link)
{
	Link->dwFlags |= LDT_LF_VOLATILE;
}
