/*
 =======================================================================================
  Name        :ModifLst.cpp
 
  Author      :vincent lhullier				Date :23/07/97
 
  Description :manage list of modifications
 =======================================================================================
  Modification -> Author :                  Date :
  Description :
 =======================================================================================
 */

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
#include "stdafx.h"

#include "IniData.h"
#include "ModifLst.h"
#include "SCR.h"
#include "dlgmodif.h"
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

/*
 =======================================================================================
	Globals 
 =======================================================================================
 */
tdstModif	*g_p_stLastModif = NULL;
tdstModif	*g_p_stFirstModif = NULL;
long		g_lNumberOfModifs = 0;
long		g_lNumberOfChildModifs = 0;
long		g_lNumberOfFatherModifs = 0;

/*
 =======================================================================================
	Functions to decompose modif name
 =======================================================================================
 */


/*
 ----------------------------------------------------------------------------------------
  Description   : extract from a modif name the filename and short filename
  _p_stModif          -> modif
  szFileName          -> string to store filename
  szShortFileName     -> string to store short name
  Returns (BOOL)	TRUE if section modified is the file, FALSE if it's a section in the
						file
 ----------------------------------------------------------------------------------------
 */
BOOL fn_bGetModifiedFile( tdstModif *_p_stModif, char *_szFileName, char *_szShortFileName )
{
	char *p_szEnd;
	BOOL bResult;

	strcpy( _szFileName, _p_stModif->szName );
	p_szEnd = strchr( _szFileName, '^' );
	if (p_szEnd != NULL)
		*p_szEnd = 0;
	bResult = ( p_szEnd == NULL );

	p_szEnd = strrchr( _szFileName, '\\');
	if (p_szEnd == NULL)
		p_szEnd = _szFileName;
	else
		p_szEnd++;
	strcpy( _szShortFileName, p_szEnd );

	return bResult;
}


/*
 ----------------------------------------------------------------------------------------
  Description   : find in modif first child modif of given father modif
  _p_stFather         -> father of modif searched
  Returns (tdstModif ) a child modif
 ----------------------------------------------------------------------------------------
 */
tdstModif *fn_p_stGetChildModif( tdstModif *_p_stFather )
{
	tdstModif *p_stChild = g_p_stFirstModif;
	while (p_stChild != NULL)
	{
		if (p_stChild->p_stFather == _p_stFather)
			return p_stChild;
		p_stChild = p_stChild->p_stNext;
	}
	return NULL;
}

/*
 ----------------------------------------------------------------------------------------
  Description   : return number of children of a section modification
  _p_stFather         -> father
  Returns (long ) number of children
 ----------------------------------------------------------------------------------------
 */
long fn_lNumberOfChildModif( tdstModif *_p_stFather )
{
	long lNumber = 0;
	tdstModif *p_stChild = g_p_stFirstModif;
	tdstModif *p_stFather;
	
	while (p_stChild != NULL)
	{
		p_stFather = p_stChild->p_stFather;
		while (p_stFather != NULL)
		{
			if (p_stFather == _p_stFather )
			{
				lNumber++;
				break;
			}
			p_stFather = p_stFather->p_stFather;
		}
		p_stChild = p_stChild->p_stNext;
	}
	return lNumber;
}

/*
 ----------------------------------------------------------------------------------------
  Description   : delete a modification on a section and restore all modification on 
					subsection of this section
 ----------------------------------------------------------------------------------------
 */
void fn_vRestoreSubModif(tdstModif *_p_stModif )
{
	tdstModif	*p_stSubModif = g_p_stFirstModif;

	while (p_stSubModif != NULL)
	{
		if (p_stSubModif->p_stFather == _p_stModif)
		{
			p_stSubModif->p_stFather = NULL;
			g_lNumberOfChildModifs--;
			g_lNumberOfFatherModifs++;
		}
		p_stSubModif = p_stSubModif->p_stNext;
	}
	//fn_lDeleteModif( _p_stModif, FALSE );
}

/*
 ---------------------------------------------------------------------------------------
  delete a modif from list
 ---------------------------------------------------------------------------------------
 */
long fn_lDeleteModif( tdstModif *_p_stModif, BOOL _bDeleteChild )
{
	tdstModif	*p_stChild;
	long		lDeleted = 0;

	if (_p_stModif == NULL)
		return 0;

	// free all child modification
	if (_bDeleteChild)
		while ( (p_stChild = fn_p_stGetChildModif( _p_stModif )) != NULL)
			lDeleted += fn_lDeleteModif( p_stChild, _bDeleteChild );

	//deallocate memory for name
	free (_p_stModif->szName );
	// keep link between modification
	if (_p_stModif == g_p_stFirstModif )
		g_p_stFirstModif = _p_stModif->p_stNext;
	if (_p_stModif == g_p_stLastModif)
		g_p_stLastModif = _p_stModif->p_stPrevious;

	if (_p_stModif->p_stPrevious != NULL)
		_p_stModif->p_stPrevious->p_stNext = _p_stModif->p_stNext;
	if (_p_stModif->p_stNext != NULL)
		_p_stModif->p_stNext->p_stPrevious = _p_stModif->p_stPrevious;
	//deallocate memory for modif
	if (_p_stModif->p_stFather == NULL)
		g_lNumberOfFatherModifs--;
	else
		g_lNumberOfChildModifs--;
	g_lNumberOfModifs--;

	free( _p_stModif );
	return lDeleted + 1;
}

/*
 ---------------------------------------------------------------------------------------
  delete all modif in list
 ---------------------------------------------------------------------------------------
 */
void fn_vDeleteAllModifs( void )
{
	tdstModif *p_stModif = g_p_stFirstModif;
	while (p_stModif != NULL)
	{
		fn_lDeleteModif( p_stModif, FALSE );
		p_stModif = g_p_stFirstModif;
	}
	g_lNumberOfFatherModifs = g_lNumberOfChildModifs = g_lNumberOfModifs = 0;
}

/*
 ---------------------------------------------------------------------------------------
  search for a modification in the same file
 ---------------------------------------------------------------------------------------
 */
tdstModif *fnp_stGetModifInFile( char *_szFileName, BOOL _bChild, BOOL _bAll )
{
	tdstModif	*p_stModif = g_p_stFirstModif;
	size_t		xSize = strlen( _szFileName );

	while (p_stModif != NULL)
	{
		if (
				((_bChild) || (p_stModif->p_stFather == NULL)) &&
				((_bAll) || (!p_stModif->bConflict)) &&
				(strnicmp(p_stModif->szName, _szFileName, xSize ) == 0)
			)
			break;
		p_stModif = p_stModif->p_stNext;
	}
	return p_stModif;
}

/*
 ---------------------------------------------------------------------------------------
  delete all modif that are in a given file
  returns nuumber of modif deleted from list
 ---------------------------------------------------------------------------------------
 */
int fn_iDeleteAllModifForAFile( char *_szFileName )
{
	int iIndex = 0;

	tdstModif *p_stModif = fnp_stGetModifInFile( _szFileName, TRUE, TRUE );
	while (p_stModif != NULL)
	{
		iIndex ++;
		fn_lDeleteModif( p_stModif, TRUE );
		p_stModif = fnp_stGetModifInFile( _szFileName, TRUE, TRUE );
	}
	return iIndex;
}

/*
 ---------------------------------------------------------------------------------------
  Mark all modif that are in a given file
  returns number of modif deleted from list
 ---------------------------------------------------------------------------------------
 */
int fn_iMarkAllModifForAFile( char *_szFileName )
{
	int iIndex = 0;

	tdstModif *p_stModif = fnp_stGetModifInFile( _szFileName, TRUE, FALSE );
	while (p_stModif != NULL)
	{
		iIndex ++;
		p_stModif->bConflict = TRUE;
		p_stModif = fnp_stGetModifInFile( _szFileName, TRUE, FALSE );
	}
	return iIndex;
}


/*
 ----------------------------------------------------------------------------------------
  Description   : get first not marked modification
  Returns (tdstModif ) first not marked modification
 ----------------------------------------------------------------------------------------
 */
tdstModif *fn_p_stGetFirstNotMarkedModif( void )
{
	tdstModif *p_stModif;

	p_stModif = g_p_stFirstModif;
	while ( (p_stModif != NULL) && ((p_stModif->bConflict) || (p_stModif->p_stFather != NULL)) )
		p_stModif = p_stModif->p_stNext;

	return p_stModif;
}


/*
 ---------------------------------------------------------------------------------------
  delete modif file
 ---------------------------------------------------------------------------------------
 */
BOOL fn_bDeleteModifFile( void )
{
	char szTxtFullName[ MAX_PATH ];
	char szBakFullName[ MAX_PATH ];

	sprintf( szTxtFullName, "%s\\%s", g_stIniData.szLocalPath, g_stIniData.szModifFile );
	strcpy ( szBakFullName, szTxtFullName );
	strcpy( strrchr( szBakFullName, '.'), ".bak" );

	if (_access( szTxtFullName, 0) == 0)
	{
		if (_access( szBakFullName, 0) == 0)
			remove( szBakFullName );

		return MoveFile( szTxtFullName, szBakFullName);
	}

	return TRUE;
}

/*
 ---------------------------------------------------------------------------------------
  rewrite modif file with all modif that are always in file
 ---------------------------------------------------------------------------------------
 */
void fn_vWriteModifInFile( BOOL _bDelete )
{
	char		szFullName[ MAX_PATH ];
	FILE		*hpFile;
	tdstModif	*p_stModif = g_p_stFirstModif;
	tdstModif	*p_stDeleteModif;

	fn_bDeleteModifFile();

	sprintf( szFullName, "%s\\%s", g_stIniData.szLocalPath, g_stIniData.szModifFile );
	hpFile = fopen( szFullName, "wt" );

	while ( p_stModif != NULL )
	{
		fprintf( hpFile, "(%c)%s\n", *g_a_szModifName[ p_stModif->cType ], p_stModif->szName );
		p_stDeleteModif = p_stModif;
		p_stModif = p_stModif->p_stNext;
		if (_bDelete)
			fn_lDeleteModif (p_stDeleteModif, FALSE);
	}
	fclose( hpFile );
}

/*
 ---------------------------------------------------------------------------------------
  function that read file where Modification list was saved.
  returns number of modification
 ---------------------------------------------------------------------------------------
 */
int fn_iReadModificationFile( void )
{
	char		szFullName[ MAX_PATH ];
	char		szLine[1024];
	char		*p_szEnd;
	FILE		*hpFile;
	tdstModif	*p_stModif;
	char		*p_szLine;
	long		lCurrentLine = 0;

	/* emptied list before reading file */
	fn_vDeleteAllModifs();

	/* building full name of modif file */
	sprintf( szFullName, "%s\\%s", g_stIniData.szLocalPath, g_stIniData.szModifFile );	

	/* open file */
	if ( (hpFile = fopen( szFullName, "rt" ) ) == NULL )
		return 0;

	/* for each line */
	while (fgets(szLine, 1023, hpFile) != NULL)
	{
		lCurrentLine++;
		/* go to last character, swap \r\n char */
		p_szEnd = szLine + strlen(szLine) - 1;
		while (
				(p_szEnd != szLine - 1) &&
				(
					(isspace(*p_szEnd)) ||
					(*p_szEnd == '\n') ||
					(*p_szEnd == '\r')
				)
			  )
				*p_szEnd--;
		*(++p_szEnd) = 0;

		if (strlen(szLine) == 0)
			continue;

		/* create a new modif */
		p_stModif = (tdstModif *) malloc (sizeof(tdstModif));
		p_stModif->lLine = lCurrentLine;
		p_stModif->bConflict = FALSE;
		p_stModif->p_stFather = NULL;
		
		if ( *(p_szLine = szLine) == '(' )
		{
			switch( *(p_szLine + 1) )
			{
				case C_ModifChar_cAdd:		p_stModif->cType = C_ModifType_cAdd;		break;
				case C_ModifChar_cRebuild:	p_stModif->cType = C_ModifType_cRebuild;	break;
				case C_ModifChar_cModif:	p_stModif->cType = C_ModifType_cModif;		break;
				case C_ModifChar_cDelete:	p_stModif->cType = C_ModifType_cDelete;		break;
			}
			p_szLine += 3;
		}
		else
			p_stModif->cType = C_ModifType_cModif;

		if (strnicmp( p_szLine, "GameData\\", 9) == 0)
			p_szLine += 9;

		p_stModif->szName = (char *) malloc (strlen( p_szLine ) + 1);
		strcpy( p_stModif->szName, p_szLine );
		
		p_stModif->p_stNext = NULL;
		p_stModif->p_stPrevious = g_p_stLastModif;
		
		/* add it in modif list */
		if (g_p_stFirstModif == NULL)
			g_p_stFirstModif = p_stModif;
		else
			g_p_stLastModif->p_stNext = p_stModif;
		
		g_p_stLastModif = p_stModif;
		
		g_lNumberOfModifs ++;
	}
	g_lNumberOfFatherModifs = g_lNumberOfModifs;
	g_lNumberOfChildModifs = 0;
	/* close file */
	fclose( hpFile );

	/* clean modif list : delete all redondant modifications */
	fn_vCleanModifList();

	return g_lNumberOfModifs;
}

/*
 ---------------------------------------------------------------------------------------
  update modification file
  delete it, rewrite it with all modif that causes conflict
  display conflict if there's some
 ---------------------------------------------------------------------------------------
 */
void fn_vUpdateModifFile( void )
{
	/* delete modif file */
	fn_bDeleteModifFile();
	fn_vWriteModifInFile( FALSE );
}


/*
 ----------------------------------------------------------------------------------------
  Description   : delete all redondant modification such as a modified sub section when
					it's parent section is modified too
 ----------------------------------------------------------------------------------------
 */
char g_aa_cComposedType[5][5] =
{
	/*       Destroy    ,        Add        ,       Rebuild       ,       Modif       ,       Delete */
	{ C_ModifType_cError, C_ModifType_cAdd  , C_ModifType_cError  , C_ModifType_cError, C_ModifType_cError  }, /* Destroy */
	{ C_ModifType_cError, C_ModifType_cError, C_ModifType_cAdd    , C_ModifType_cAdd  , C_ModifType_cDestroy}, /* Add */
	{ C_ModifType_cError, C_ModifType_cError, C_ModifType_cRebuild, C_ModifType_cModif, C_ModifType_cDelete }, /* Rebuild */
	{ C_ModifType_cError, C_ModifType_cError, C_ModifType_cRebuild, C_ModifType_cModif, C_ModifType_cDelete }, /* Modif */
	{ C_ModifType_cError, C_ModifType_cModif, C_ModifType_cError  , C_ModifType_cError, C_ModifType_cError  }  /* Delete */
};

char *g_a_szModifName[5] =
{
	"Destroyed",
	"Added",
	"Rebuild",
	"Modified",
	"Deleted" 
};

void fn_vCleanModifList( void )
{
	tdstModif	*p_stModif = g_p_stFirstModif;
	tdstModif	*p_stOtherModif;
	char		cNextChar;
	char		cNewType;
	long		lNumberOfErrors = 0;
	char		**d_szError = NULL;
	long		lError;
	long		lCurrentFatherLength, lFatherLength;

	/*
	 * First : find all error and all double notification
	 */
	while ( p_stModif != NULL )
	{
		p_stOtherModif = p_stModif->p_stNext;
		while (p_stOtherModif != NULL)
		{
			if ( stricmp( p_stOtherModif->szName, p_stModif->szName ) == 0)
			{
				p_stOtherModif->bConflict = TRUE;
				p_stOtherModif->cPrevType = p_stModif->cType;
				cNewType = g_aa_cComposedType[ p_stModif->cType ][ p_stOtherModif->cType ];
				if (cNewType != C_ModifType_cError)
					p_stModif->cType = cNewType;
			}
			p_stOtherModif = p_stOtherModif->p_stNext;
		}
		do
		{
			p_stModif = p_stModif->p_stNext;
		} while ( (p_stModif != NULL) && (p_stModif->bConflict) );
	}

	/* count eventual errors */
	p_stModif = g_p_stFirstModif;
	while (p_stModif != NULL)
	{
		if (p_stModif->bConflict)
		{
			cNewType = g_aa_cComposedType[ p_stModif->cPrevType ][ p_stModif->cType ];
			if (cNewType == C_ModifType_cError)
				lNumberOfErrors++;
		}
		p_stModif = p_stModif->p_stNext;
	}

	if (lNumberOfErrors)
		d_szError = (char **) malloc( lNumberOfErrors * sizeof(char *) );

	/* get errors and delete eventual doublon */
	p_stModif = g_p_stFirstModif;
	lError = 0;
	while (p_stModif != NULL)
	{
		p_stOtherModif = p_stModif->p_stNext;
		if (p_stModif->bConflict)
		{
			cNewType = g_aa_cComposedType[ p_stModif->cPrevType ][ p_stModif->cType ];
			if (cNewType == C_ModifType_cError)
			{
				d_szError[ lError] = (char *) malloc ( strlen( p_stModif->szName) + 100 );
				sprintf( d_szError[lError] , "(Line %4d) %s section has been %s after being %s", p_stModif->lLine, p_stModif->szName, g_a_szModifName[ p_stModif->cType ], g_a_szModifName[ p_stModif->cPrevType ] );
				lError++;
			}
			fn_lDeleteModif( p_stModif, FALSE );
		}
		p_stModif = p_stOtherModif;
	}

	/* build section / sous section hierarchy */
	p_stModif = g_p_stFirstModif;
	while (p_stModif != NULL)
	{
		lCurrentFatherLength = 0;
		p_stOtherModif = g_p_stFirstModif;
		while (p_stOtherModif != NULL)
		{
			if (p_stOtherModif != p_stModif)
			{
				lFatherLength = strlen( p_stOtherModif->szName );
				if ( (lFatherLength > lCurrentFatherLength) && strnicmp( p_stOtherModif->szName, p_stModif->szName, lFatherLength ) == 0)
				{
					cNextChar = p_stModif->szName[lFatherLength];
					if (cNextChar == SCR_CC_c_Cfg_NameSeparator)
					{
						if (lCurrentFatherLength != 0)
						{
							g_lNumberOfFatherModifs--;
							g_lNumberOfChildModifs++;
						}
						lCurrentFatherLength = lFatherLength;	
						p_stModif->p_stFather = p_stOtherModif;
					}
				}
			}
			p_stOtherModif = p_stOtherModif->p_stNext;
		}
		p_stModif = p_stModif->p_stNext;
	}

	/* delete eventual destroyed modif */
	p_stModif = g_p_stFirstModif;
	while (p_stModif != NULL)
	{
		if (p_stModif->cType == C_ModifType_cDestroy)
		{
			fn_lDeleteModif( p_stModif, TRUE );
			p_stModif = g_p_stFirstModif;
		}
		else
			p_stModif = p_stModif->p_stNext;
	}

	/*
	 * display error if that happens
	 */
	if (lNumberOfErrors )
	{
		CModifListErrorDlg oErrorDlg( lNumberOfErrors, d_szError );
		oErrorDlg.DoModal();

		for ( lError = 0; lError < lNumberOfErrors; lError++)
			free ( d_szError[ lError ] );
		free ( d_szError );
	}
}
