/*------------------------------------------------------------------------------
	FILE	:	Register.c
	CREATED	:	98/05/19
	AUTHOR	:	Catalin Cocos
	CONTENTS:	callback registering structures & functions
------------------------------------------------------------------------------*/

#include "StdInc.h"
#include "Register.h"
#include "Interface.h"
#include "Error.h"
#include "Refs.h"

/* Globals
-------------*/
extern int g_nInitialized;

LDT_tdst_DSArray g_ArSectionCallbacks;	/* the section callbacks */
LDT_tdst_DSArray g_ArFileCallbacks;		/* the file callbacks */
LDT_tdst_DSArray g_ArPaths;	            /* the paths */
LDT_tdst_DSArray g_ArPathStrings;       /* the path strings */
LDT_tdst_DSArray g_ArBaseDirectories;	/* the base paths */

LDT_tdst_CallbackEntry Default_Type = { ".*", LDT_Default_NULL_Create, LDT_Default_Load};

/* Functions 
-------------*/

/*------------------------------------------------------------------------------
	DESC.	:	compares two callback registration entries
	CREATED	:	98/05/12        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_Compare_CallbackEntries(const void** _pA, const void** _pB)
{
	int Result = stricmp(((LDT_tdst_CallbackEntry*) *_pA)->szType,
						((LDT_tdst_CallbackEntry*) *_pB)->szType );
	if(Result < 0)	return -1;
	return Result;
}


/*------------------------------------------------------------------------------
	DESC.	:	registers a type
	INPUT	:	the type;
				the pointer to the create callback;
				pointer to the load callback;
				Registering mode:	
					LDT_REG_SECTION - registers section callbacks
					LDT_REG_FILE	- registers file callbacks
	OUTPUT	:	non-zero if successful, 0 if the type is already registered or
				if the paramters are invalid
	CREATED	:	98/05/12        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_RegisterType(char* szType, int (*_Create) ( LDT_tdst_Link* ),
					 int (*_Load) ( LDT_tdst_Link* ),
					 int Mode)
{
//	char *sz;
	LDT_tdst_DSArray* pAr;
	LDT_tdst_CallbackEntry* pNewEntry, *pOldEntry;
	int position;
	
	if( !g_nInitialized ) return 0; /* table not initialized */
	if( !szType || !_Create || !_Load ) return 0;	/* invalid parameters */
	pAr = (Mode == LDT_REG_SECTION)? &g_ArSectionCallbacks: &g_ArFileCallbacks; /* select the array*/
	
	/* convert to lower case */
//	sz=szType;
//	for( ; *sz; sz++ )
//	  if( ( *sz>='A') && ( *sz<='Z' ) )
//	    *sz+='a'-'A';

	/* create the new entry */
	pNewEntry = malloc( sizeof(LDT_tdst_CallbackEntry));
	pNewEntry->szType = szType;
	pNewEntry->Create = _Create;
	pNewEntry->Load = _Load;

	if( LDT_DSAr_SAdd( pAr, pNewEntry, 0, &position) < 0 )
	{
		/* insertion failed */
#if defined(LDT_ERROR_TREATMENT)
		pOldEntry = LDT_DSAr_GetAt( pAr, position );
		if(	(pOldEntry->Create != _Create) || (pOldEntry->Load != _Load) )
			ERRoR(0, NULL, 0, "Type already registered. Registration failed.");
#endif
		free( pNewEntry );
		return 0;
	}
	strcpy( pNewEntry->szType = malloc( strlen(szType) + 1 ), szType ); /* copy the type string*/
	return 1;
}


/*------------------------------------------------------------------------------
	DESC.	:	returns a pointer to the type or NULL if the type is not registered;
				the parameters have the same signification as in the above function
	CREATED	:	98/05/12     
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
LDT_tdst_CallbackEntry* LDT_IsTypeRegistered( char* szType, int Mode)
{
	LDT_tdst_DSArray* pAr;

	/* convert to lower case */
//	char *sz;
//	sz=szType;
//	for( ; *sz; sz++ )
//	  if( ( *sz>='A') && ( *sz<='Z' ) )
//	    *sz+='a'-'A';

	if( !g_nInitialized ) return NULL; /* table not initialized */
	if( !szType) return NULL;			/* invalid parameter */
	pAr = (Mode == LDT_REG_SECTION)? &g_ArSectionCallbacks: &g_ArFileCallbacks;
	return LDT_DSAr_GetAt(pAr, LDT_DSAr_Index( pAr, (LDT_tdst_CallbackEntry*)&szType, NULL ));
}

/*------------------------------------------------------------------------------
	DESC.	:	returns a pointer to the type or the default type if the type is not registered;
				the parameters have the same signification as in the above function
	CREATED	:	98/05/12     
	NOTE	:	the input string MUST BE LOWERCASE
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
LDT_tdst_CallbackEntry* GetType( char* szType, int Mode)
{
	LDT_tdst_DSArray* pAr;
	LDT_tdst_CallbackEntry* pType;

	if(!g_nInitialized || !szType)
		return &Default_Type;
	pAr = (Mode == LDT_REG_SECTION)? &g_ArSectionCallbacks: &g_ArFileCallbacks;
	pType = LDT_DSAr_GetAt(pAr, LDT_DSAr_Index( pAr, (LDT_tdst_CallbackEntry*)&szType, NULL ));
	if( !pType )
	{
#if defined(LDT_ERROR_TREATMENT) && ( LDT_WARNING_LEVEL >= 3 || LDT_ERROR_LEVEL >= 3)
		char buffer [300];
		sprintf(buffer, "The type \"%s\" (%s) is not registered. The default callback will be used.", szType, (Mode == LDT_REG_SECTION)?"SECTION":"FILE");
		ERRoR((Mode == LDT_REG_SECTION)?3:4, NULL, 0, buffer );
#endif
		return &Default_Type;
	}
	return pType;
}

/*------------------------------------------------------------------------------
	DESC.	:	unregisters an existing type
	OUTPUT	:	non-zero if successful, 0 on error
	CREATED	:	98/05/12        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_UnregisterType( char* szType, int Mode)
{
//	char *sz;
	int idx;
	LDT_tdst_DSArray* pAr;
	LDT_tdst_CallbackEntry TestEntry, *pDelEntry;

	if( !g_nInitialized || !szType ) return 1; /*  no opened session or invalid type */

	/* convert to lower case */
//	sz=szType;
//	for( ; *sz; sz++ )
//	  if( ( *sz>='A') && ( *sz<='Z' ) )
//	    *sz+='a'-'A';

	TestEntry.szType = szType;
	pAr = (Mode == LDT_REG_SECTION)? &g_ArSectionCallbacks: &g_ArFileCallbacks; /* select the array*/
	idx = LDT_DSAr_Index( pAr, &TestEntry, NULL );
	if( idx < 0) return 1; /* not found */

	/* solve all references before proceeding */
	if(!LDT_Flush()) return 0; /* if the flush failed, return */

	/* remove & delete the entry */
	pDelEntry = LDT_DSAr_RemoveAt( pAr, idx );
	free( pDelEntry->szType );
	free( pDelEntry );
	return 1;
}

/*------------------------------------------------------------------------------
	DESC.	:	registers a Base Directory - if present the base directories will
				always prefix the paths
	INPUT	:	the base directory
	OUTPUT	:	0 if successful, !0 on error
	CREATED	:	98/09/24        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_AddBaseDirectory( char* szPath )
{
//	char *sz;
	char path[_MAX_PATH];
	int i;

	if( !g_nInitialized || !szPath ) return 1; /*  no opened session or invalid parameters */
	
	 /* Get the path */
	while (*szPath == ' ') szPath ++;
	strcpy( path, szPath );
	for( i = strlen(path)-1; i>=0 && path[i] == ' '; i--) path[i] = 0;
	if(path[strlen(path)-1] != '\\') strcat( path, "\\");
	  
	/* convert file path string to lower case */
//	sz=path;
//	for( ; *sz; sz++ )
//	  if( ( *sz>='A') && ( *sz<='Z' ) )
//	    *sz+='a'-'A';
	
	/* add the file path to the base directories list */
	LDT_DSAr_Add( &g_ArBaseDirectories, strcpy( malloc( strlen(path) +1 ), path));
	return 0;
}

/*------------------------------------------------------------------------------
	DESC.	:	registers a Path
	INPUT	:	the path, the file types extensions(without the leading'.'), separated by spaces
	OUTPUT	:	0 if successful, !0 on error
	CREATED	:	98/05/12        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_RegisterPath( char* szPath, char* szFileTypes )
{
	char *sz, * szNewPath;
	char buffer[_MAX_EXT];
	char path[_MAX_PATH];
	int i, idx, iidx;
	LDT_tdst_TypePaths Dummy;
	Dummy.szExtension = buffer;

	if( !g_nInitialized || !szPath || !szFileTypes ) return 1; /*  no opened session or invalid parameters */
	

	 /* Get the path */
	while (*szPath == ' ') szPath ++;
	strcpy( path, szPath );
	for( i = strlen(path)-1; i>=0 && path[i] == ' '; i--) path[i] = 0;
	if(path[strlen(path)-1] != '\\') strcat( path, "\\");
	  
	/* convert file path string to lower case */
//	sz=path;
//	for( ; *sz; sz++ )
//	  if( ( *sz>='A') && ( *sz<='Z' ) )
//	    *sz+='a'-'A';
	/* add the file path to the path collection */

	idx = LDT_DSAr_Index( &g_ArPathStrings, path, &iidx);
	if( idx<0 )
		/* insert the new path */
		LDT_DSAr_InsertAt(&g_ArPathStrings, iidx, strcpy( szNewPath = malloc( strlen(path) +1), path));
	else
		/* we already have it */
		szNewPath = g_ArPathStrings.pData[idx];

	
	/* convert file type string to lower case */
//	sz=szFileTypes;
//	for( ; *sz; sz++ )
//	  if( ( *sz>='A') && ( *sz<='Z' ) )
//	    *sz+='a'-'A';
	  
	sz=szFileTypes;
	while ( *sz )
	{
		int cnt = 0;
		while (*sz == ' ') sz++;
		for(; *sz!=' ' && *sz ; sz++, cnt++ )
			buffer[cnt] = *sz;
		buffer[cnt] = 0;
		if(*buffer)
		{
			/* we have a valid file type */
			int idx,iidx;
			idx = LDT_DSAr_Index( &g_ArPaths, &Dummy, &iidx );
			if(idx<0)
			{
				/* type not registered before */
				LDT_tdst_TypePaths* pPath = malloc( sizeof(LDT_tdst_TypePaths));
				strcpy( pPath->szExtension = malloc( strlen(buffer) + 1 ), buffer);
				LDT_DSAr_Init(&pPath->arPaths);
				LDT_DSAr_SetGranularity(&pPath->arPaths, 10);
				LDT_DSAr_Add(&pPath->arPaths, szNewPath);
				LDT_DSAr_InsertAt( &g_ArPaths, iidx, pPath );
			}
			else
				/* type already present */
				LDT_DSAr_Add(&((LDT_tdst_TypePaths*)g_ArPaths.pData[idx])->arPaths, szNewPath);
		}
	}
	return 0;
}

#ifdef LDT_LOG_STATISTICS
/* Log/stat stuff
-------------------*/
unsigned TimerStartCount = 0;
unsigned TimerStopCount = 0;

LONGLONG Frequency;
LONGLONG TimingOverhead = 0;
LONGLONG StartOverhead = 0;
LONGLONG StopOverhead = 0;

void InitializeTimingEnvironment()
{
	LARGE_INTEGER li;
	LONGLONG tmpli;
	int i;
	TimerStruct time, dummy;
	QueryPerformanceFrequency( &li );
	Frequency = li.QuadPart; /* the clock frequency */

	InitTimer( &time );
	for( i = 0; i<32768; i++) /* 2^15 */
	{
		StartTimer( &time );
		StopTimer( &time );
	}
	TimingOverhead = Int64ShraMod32(time.TimeCounter, 15); /* the timing overhead */

	InitTimer( &time );
	for( i = 0; i<32768; i++) /* 2^15 */
	{
		StartTimer( &time );
		StartTimer( &dummy );
		StopTimer( &time );
	}
	tmpli = time.TimeCounter;

	InitTimer( &time );
	InitTimer( &dummy );
	for( i = 0; i<32768; i++) /* 2^15 */
	{
		StartTimer( &time );
		StartTimer( &dummy );
		StopTimer( &dummy );
		StopTimer( &time );
	}
	StartOverhead  = Int64ShraMod32(tmpli , 15); /* the stoptiming overhead */
	StopOverhead = Int64ShraMod32(time.TimeCounter - tmpli , 15); /* the stoptiming overhead */

	InitTimer( &time );
	InitTimer( &dummy );
	StartTimer( &time );
	StartTimer( &dummy );
	StopTimer( &dummy );
	StopTimer( &time );
}

void InitTimer(TimerStruct* time)
{
	time->TimeCounter = 0;
}

void StartTimer(TimerStruct* time)
{
	time->LastStartCount = ++TimerStartCount;
	time->LastStopCount = TimerStopCount;
	QueryPerformanceCounter((LARGE_INTEGER*)&time->LastValue);
}

void StopTimer(TimerStruct* time)
{
	LARGE_INTEGER li;
	QueryPerformanceCounter(&li);
	time->TimeCounter += li.QuadPart
						 - time->LastValue
						 - TimingOverhead
						 - (TimerStartCount-time->LastStartCount)*StartOverhead
						 - (TimerStopCount-time->LastStopCount)*StopOverhead;
	TimerStopCount++;
}

double ReadTimer(TimerStruct* time)
{
	return ((double)time->TimeCounter)/Frequency;
}

#endif
