/*------------------------------------------------------------------------------
	FILE	:	Link.c
	CREATED	:	98/05/19
	AUTHOR	:	Catalin Cocos
	CONTENTS:	linktable/position functions
------------------------------------------------------------------------------*/

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

/* Globals
-------------*/

LDT_tdst_DSHash	g_HashLinks;					/* the linktable */
LDT_tdst_DSArray	g_ArRefs;					/* the references, grouped by file */
LDT_tdst_DSArray	g_ArFilenames;				/* the filename strings */
LDT_tdst_Refs* g_pFileToProcess = NULL;		/* the file to load */
LDT_tdst_Refs* g_pLastFileQueued = NULL;	/* the last fiel referenced */
#ifdef LDT_MULTITHREADED
LDT_tdst_Refs* g_pFileToRead = NULL;		/* the file to read */
#endif

extern unsigned short g_usLinkingPerstincence;

char LDT_EMPTY_STR[1] = {0};



/*------------------------------------------------------------------------------
	DESC.	:	methods to allow external access to the list of filenames
	CREATED	:	98/05/19        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_GetReferencedFilesNumber()
{
	return g_ArFilenames.nSize;
}

const char* LDT_GetReferencedFile(int idx)
{
	return (const char*) LDT_DSAr_GetAt(&g_ArFilenames, idx);
}

/*------------------------------------------------------------------------------
	DESC.	:	computes the dispersion value for a link
	CREATED	:	98/05/19        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
#pragma warning ( disable: 4244)
int LDT_Link_Dispersion( LDT_tdst_Link* pObj )
{
	char* szName;
	unsigned char pHashVal = 0;
	if( !pObj ) return 256;
	szName = (pObj->dwFlags & LDT_LF_FILE)? pObj->szFile: pObj->szName;
//	while(*szName)	pHashVal = (unsigned char) ( (pHashVal<<5) + pHashVal + *szName++);
/* hash computing is CASE INSENSITIVE */
	while(*szName)
		pHashVal += (unsigned char) ( (pHashVal<<5) | 0xDF&*szName++);
	return pHashVal;
}
#pragma warning ( default: 4244)


/*------------------------------------------------------------------------------
	DESC.	:	Compares two links
	CREATED	:	98/05/19        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_Compare_Links(const void** _pA, const void** _pB)
{
	LDT_tdst_Link* A = (LDT_tdst_Link*) *_pA;
	LDT_tdst_Link* B = (LDT_tdst_Link*) *_pB;
	
	int Result = A->szFile - B->szFile;
	if(Result == 0)
	{
		Result =  A->Type - B->Type;
		if(Result == 0)
		{
			Result = stricmp( A->szParent, B->szParent);
			if(Result == 0)
				Result = stricmp( A->szName, B->szName );
		}
	}
	if( Result<0 ) return -1;
	return Result;
}

/*------------------------------------------------------------------------------
	DESC.	:	Compares two links
	CREATED	:	98/08/27        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_Compare_LinkValues(const void** _pA, const void** _pB)
{
	long Result = ((LDT_tdst_LinkValue*)*_pA)->pLink - ((LDT_tdst_LinkValue*)*_pB)->pLink;
	if( Result<0 ) return -1;
	return Result;
}

/*------------------------------------------------------------------------------
	DESC.	:	creates a link record, given its description
	CREATED	:	98/05/21   
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
LDT_tdst_Link* LDT_CreateLinkFromReference(	char* szSection )	/* the section description */
{
	unsigned long dwFlags =  g_usLinkingPerstincence;
	LDT_tdst_CallbackEntry* Type;
	
	LDT_tdst_Link* Link = NULL;
	char *szType, *szParent = NULL, *szName = NULL, *szFile = NULL;

	/* cut the ':^' from the section desc.*/
	char *sz=szSection;
	int delta = 0;
	for( ; *sz; sz++ )
		if( *sz == ':' && (sz[1] == '^' || !sz[1]))
			delta--;
		else sz[delta]=*sz;

	sz[delta]=0;
	
	szParent = strchr( szSection, '^');	/* get the parent description */
	szType = strrchr( szSection, '^');	/* get the section type name */

	/* get the section type */
	if(!szType)
	{
		/* the reference is to a file */
		dwFlags |= LDT_LF_FILE;			
		if((szType = strrchr( szSection, '.' )) != NULL)
			szType++;
		Type = GetType( szType, LDT_REG_FILE );
		szType = NULL;
	}
	else
	{
		/* the reference is to a section */
		szName = strrchr(szType, ':');	/* get the section name */
		if(szName) *szName++ = 0;
		*szType = 0;
		Type = GetType( szType+1, LDT_REG_SECTION);
		if( szType == szParent ) szParent = NULL;
		else *szParent++ = 0;
	}
	
	/* get the name of the file (in which the section is located) */
	if( *szSection == '*')/* the current file */
	{
		if( g_FileDesc ) szFile = g_FileDesc->szFile; /* we are inside a file, all correct */
	}
	else
	{	/* an absolute path */
		int iidx, idx = LDT_DSAr_Index( &g_ArFilenames, szSection, &iidx );
		if(idx>=0) szFile = g_ArFilenames.pData[idx]; /* we have it in the string table */
		else
		{	/* the filename is not present in the string table - it is added now */
			strcpy( szFile = (char*) malloc( strlen(szSection) + 1 ), szSection);
			LDT_DSAr_InsertAt(&g_ArFilenames, iidx, szFile );
		}
	}

	if( szFile )
	{
		/* we have a valid section description */
		Link= (LDT_tdst_Link *) malloc( sizeof( LDT_tdst_Link ) );
		if( szName && *szName)
		{
			Link->szName=(char*) malloc( strlen( szName ) +1 );
			strcpy( Link->szName, szName );
		}
		else Link->szName=LDT_EMPTY_STR;
		if( szParent )
		{
			Link->szParent=(char*) malloc( strlen( szParent) +1 );
			strcpy( Link->szParent, szParent );
		}
		else Link->szParent=LDT_EMPTY_STR;

		Link->szFile = szFile;
		Link->Type = Type;
		Link->dwFlags = dwFlags;
		Link->pObject = NULL;
		Link->pParent = g_CrtSection;
		Link->wUnsolvedRefs = 0; /* MP - postprocess */
	}
#if defined(LDT_ERROR_TREATMENT)
	else
		ERRoR(0, NULL, 0, "Unable to create Link. Invalid Link Description");
#endif

	if( szType) *( szType ) = '^';
	if( szName ) *( szName-1 ) = ':';
	if( szParent) *( szParent -1 ) = '^';
	return Link;
}

/*------------------------------------------------------------------------------
	DESC.	:	creates a link record
	CREATED	:	98/05/21   
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
LDT_tdst_Link* LDT_Create_Link(	char*	szName,				/* the section name */
								char*	szParent,			/* the section parent */
								char*	szFile,				/* the file containing the section  - stored in a string table */
								LDT_tdst_CallbackEntry*	Type,
								unsigned long	dwFlags,	/* flags */
								void*			pObject,	/* the allocated memory structure or NULL */
								LDT_tdst_Link*	pParent)	/* the section that requested the loading */
{
	LDT_tdst_Link *Link;
	if (!Type) return NULL;

	Link = (LDT_tdst_Link *) malloc( sizeof( LDT_tdst_Link ) );
	if( szName && *szName )
	{
		Link->szName=(char*) malloc( strlen( szName ) +1 );
		strcpy( Link->szName, szName );
	}
	else Link->szName=LDT_EMPTY_STR;
	if( szParent && *szParent )
	{
		Link->szParent=(char*) malloc( strlen( szParent) +1 );
		strcpy( Link->szParent, szParent );
	}
	else Link->szParent=LDT_EMPTY_STR;

	Link->szFile = szFile;
	Link->Type = Type;
	Link->dwFlags = dwFlags;
	Link->pObject = pObject;
	Link->pParent = pParent;
	Link->wUnsolvedRefs = 0; /* MP - postprocess */

	return Link;
}

/*------------------------------------------------------------------------------
	DESC.	:	destroys a link record
	CREATED	:	98/05/21   
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/

void LDT_Delete_Link( LDT_tdst_Link *Link )
{
	if( Link->szName != LDT_EMPTY_STR ) free( Link->szName );
	if( Link->szParent != LDT_EMPTY_STR ) free( Link->szParent );
	free(Link);
}

/*------------------------------------------------------------------------------
	DESC.	:	deletes an existing array
	CREATED	:	98/05/19        
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_Compare_Positions(const void** _pA, const void** _pB)
{
	LDT_tdst_Position* A = (LDT_tdst_Position*) *_pA;
	LDT_tdst_Position* B = (LDT_tdst_Position*) *_pB;
	int Result = A->Type - B->Type ;
	if(Result == 0)
	{
		Result = stricmp( A->szParent, B->szParent);
		if(Result == 0)
			Result = stricmp( A->szName, B->szName );
	}
	if( Result<0 ) return -1;
	return Result;
}

/*------------------------------------------------------------------------------
	DESC.	:	creates a section position record
	CREATED	:	98/05/19        
	AUTHOR	:	Mircea Petrescu
	MODIFIED:	Catalin Cocos 98/05/22
------------------------------------------------------------------------------*/

LDT_tdst_Position *LDT_Create_Position(	char *szName,
										char *szParent,
										LDT_tdst_CallbackEntry*	Type,
										unsigned short uwFlags,
										unsigned long ulPos,
										unsigned long	nLine, 
										int iLevel
)
{
	LDT_tdst_Position *pos= (LDT_tdst_Position *) malloc( sizeof( LDT_tdst_Position ) );
	if( szName )
	{
		pos->szName=(char*) malloc( strlen( szName ) +1 );
		strcpy( pos->szName, szName );
	}
	else pos->szName=LDT_EMPTY_STR;
	if( szParent )
	{
		pos->szParent=(char*) malloc( strlen( szParent) +1 );
		strcpy( pos->szParent, szParent );
	}
	else pos->szParent=LDT_EMPTY_STR;

	pos->Type = Type;
	pos->uwFlags=uwFlags;
	pos->dwPos=ulPos;
	pos->nLine = nLine;
	pos->iLevel=iLevel;
	return pos;
}

/*------------------------------------------------------------------------------
	DESC.	:	destroys a section position record
	CREATED	:	98/05/19        
	AUTHOR	:	Mircea Petrescu
------------------------------------------------------------------------------*/

void LDT_Delete_Position( LDT_tdst_Position *pos )
{
	if( pos->szName != LDT_EMPTY_STR ) free( pos->szName );
	if( pos->szParent != LDT_EMPTY_STR ) free( pos->szParent );
	free( pos );
}




/*------------------------------------------------------------------------------
	DESC.	:	Compares two names
	CREATED	:	98/05/22
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_Compare_Strings(const void** _pA, const void** _pB)
{
	int Result = stricmp( (char*)(*_pA), (char*)(*_pB) );
	if( Result<0 ) return -1;
	return Result;
}

/* Reference - related */


/*------------------------------------------------------------------------------
	DESC.	:	Adds a reference in the reference structure
	CREATED	:	98/05/22
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
void AddReference( LDT_tdst_Link* pLink )
{
	int idx, position;
	LDT_tdst_DSArray* pRefs;
	idx = LDT_DSAr_Index( &g_ArRefs, (LDT_tdst_Refs*) pLink, &position );
	if( idx<0 )
	{
		LDT_tdst_Refs* pNewRef = (LDT_tdst_Refs*) malloc( sizeof(LDT_tdst_Refs));
		pNewRef->p_NextReferencedFile = NULL; /* this is the last file referenced */
		pNewRef->szFile = pLink->szFile;
		pRefs = &(pNewRef->Links);
		LDT_DSAr_Init( pRefs );
		LDT_DSAr_SetSortMethod( pRefs, LDT_Compare_Links );
		LDT_DSAr_SetGranularity( pRefs, 50 );
		
		LDT_DSAr_InsertAt( &g_ArRefs, idx = position, pNewRef );

		if(g_pFileToProcess) g_pLastFileQueued->p_NextReferencedFile = pNewRef; /* insert the reference in the list of files to load... */
		else g_pFileToProcess = pNewRef;
		g_pLastFileQueued = pNewRef;

#ifdef LDT_MULTITHREADED
		EnterCriticalSection( &GReadExclusion );
		if(!g_pFileToRead)
			g_pFileToRead = pNewRef;
		LeaveCriticalSection( &GReadExclusion );
		pNewRef->pFile = NULL;
		if(GFileLimit)
			ReleaseSemaphore( GFileLimit, 1, NULL ); /* a new file is available for reading */
#endif
	}
	pRefs = &( ((LDT_tdst_Refs*)g_ArRefs.pData[idx])->Links); /* the reference collection */
	if( pLink->dwFlags & LDT_LF_FILE )
	{
		/* the whole file is to be added */
		LDT_DSAr_RemoveAll( pRefs );		/* remove all the existing references */
		LDT_DSAr_Add( pRefs, pLink );		/* add the unique reference to the file */
	}
	else
	{
		if(LDT_DSAr_GetSize( pRefs ) && (((LDT_tdst_Link*)pRefs->pData[0])->dwFlags & LDT_LF_FILE ) )
			return;
		LDT_DSAr_SAdd( pRefs, pLink, 0, NULL );
	}
}


/*------------------------------------------------------------------------------
	DESC.	:	Compares two refs
	CREATED	:	98/05/22
	AUTHOR	:	Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_Compare_Refs(const void** _pA, const void** _pB)
{
	int Result =  ((LDT_tdst_Refs*)*_pA)->szFile - ((LDT_tdst_Refs*)*_pB)->szFile;
	if( Result<0 ) return -1;
	return Result;
}

