/*
 =======================================================================================
  Name        :SaveMngr.cpp
 
  Author      :Vincent Lhullier             Date :11/07/97
 
  Description :manage save of data before modification
 	Create a copy of GameData tree with all file that would be modified. That will allow
	someone to recuperate previous version if save generate some problem in data.
 =======================================================================================
  Modification -> Author :                  Date :
  Description :
 =======================================================================================
 */

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

#ifdef ACTIVE_EDITOR

#include <io.h>
#include "SaveMngr.h"

#include "acp_base.h"
#include "itf/CPAProj.hpp"

#include "DPT.h"
#include "SCR.h"
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

/*
 =======================================================================================
	STRUCTURE
 =======================================================================================
 */
typedef	struct SAVE_tdstFile_
{
	char *szName;
	char cNew;
} SAVE_tdstFile;


/*
 =======================================================================================
	GLOBALS
 =======================================================================================
 */
/*
 * indicate next number of version to use to save files
 * Warning : to be valid you must call SAVE_fn_vAnalyseCurrentUsedVersion before using
 *			this number
 */
static long				SAVE_g_lNextVersionNumber;
static BOOL				SAVE_g_bContinue;
static BOOL				SAVE_g_bGenerateIsActive = 2;
static char				SAVE_g_szDirName[ _MAX_PATH ];

static SAVE_tdstFile	*SAVE_gs_d_stFile;
static long				SAVE_gs_lNumberOfFiles;

static char				*SAVE_g_szGameData;
static long				SAVE_g_lGameDataLength;

/*
 =======================================================================================
 =======================================================================================
	FUNCTIONS
 =======================================================================================
 =======================================================================================
 */

/*
 =======================================================================================
	Directory Functions
 =======================================================================================
 */

/*
 ----------------------------------------------------------------------------------------
  Don't call this function directly, it is used by SAVE_fn_bDeleteTree 
  Description   : delete recursively a directory
  Returns (BOOL ) true if success
 ----------------------------------------------------------------------------------------
 */
BOOL fn_bDeleteTree( void )
{
	WIN32_FIND_DATA stFindData;
	HANDLE			hFind;
	char			*p_szEndDirName;

	p_szEndDirName = SAVE_g_szDirName + strlen( SAVE_g_szDirName );
	strcpy( p_szEndDirName, "\\*.*" );
	if( (hFind = FindFirstFile(SAVE_g_szDirName, &stFindData )) == INVALID_HANDLE_VALUE)
		return TRUE;
	*p_szEndDirName = 0;

	do
	{
		*p_szEndDirName = '\\';
		strcpy( p_szEndDirName + 1, stFindData.cFileName );

		if ( stFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
		{
			if ( *stFindData.cFileName != '.')
			{
				if ( !fn_bDeleteTree() )
					return FALSE;
			}
		}
		else
		{
			if (_access( SAVE_g_szDirName, 2) == -1 )
				_chmod( SAVE_g_szDirName, _S_IWRITE );

			if ( !DeleteFile( SAVE_g_szDirName ) )
				return FALSE;
		}
		*p_szEndDirName = 0;
	} while(FindNextFile( hFind, &stFindData ));

	FindClose( hFind );

	return RemoveDirectory( SAVE_g_szDirName );
}


/*
 ----------------------------------------------------------------------------------------
  Description   : delete completely a directory
  szDirName           -> name of dir to delete
  Returns (BOOL ) true if success
 ----------------------------------------------------------------------------------------
 */
BOOL SAVE_fn_bDeleteTree( char *szDirName )
{
	strcpy( SAVE_g_szDirName, szDirName );
	return fn_bDeleteTree();
}


/*
 ----------------------------------------------------------------------------------------
  Description   : create a directory
  szDirName           -> name of directory
  Returns (BOOL ) true if sucess
 ----------------------------------------------------------------------------------------
 */
BOOL SAVE_fn_bCreateDirectory( char *szDirName )
{
	if ( (!CreateDirectory( szDirName, NULL )) && (GetLastError() != ERROR_ALREADY_EXISTS) )
	{
		char *p_szCur;

		p_szCur = strchr( szDirName, '\\');
		while (p_szCur != NULL)
		{
			*p_szCur = 0;
			if ((!CreateDirectory (szDirName, NULL)) && (GetLastError() != ERROR_ALREADY_EXISTS) )
			{
				char szMessage[ 512 ];
				sprintf( szMessage, "Can't create local path %s", szDirName );
				SAVE_fn_vErrorMessage(  szMessage );
				return FALSE;
			}
			*p_szCur++ = '\\';
			p_szCur = strchr( p_szCur, '\\');
		};

		if ((!CreateDirectory (szDirName, NULL)) && (GetLastError() != ERROR_ALREADY_EXISTS) )
		{
			char szMessage[ 512 ];
			sprintf( szMessage, "Can't create local path %s", szDirName );
			SAVE_fn_vErrorMessage(  szMessage );
			return FALSE;
		}
	}
	return TRUE;
}

/*
 =======================================================================================
	Error functions 
 =======================================================================================
 */

/*
 ----------------------------------------------------------------------------------------
  Description   : display an error message
  _szMessage          -> message to display
 ----------------------------------------------------------------------------------------
 */
void SAVE_fn_vErrorMessage( char *_szMessage )
{
	char szError[1024];
	char *p_szError = szError;

	p_szError += sprintf( p_szError, "Error saving current version before saving new data\n" );
	p_szError += sprintf( p_szError, "You will not be able to get local previous version\n" );
	p_szError += sprintf( p_szError, "---------------------------------------------------\n" );
	p_szError += sprintf( p_szError, "%s\n\n", _szMessage );
	p_szError += sprintf( p_szError, "   Save anyway ?" );
	SAVE_g_bContinue = AfxMessageBox( szError, MB_ICONSTOP | MB_YESNO );
}

/*
 =======================================================================================
	Version functions 
 =======================================================================================
 */

/*
 ----------------------------------------------------------------------------------------
  Description   : 
  _szSavedDirectoryName	-> name of directory from which function extract version number
  Returns (long ) version number if it's a valid directory, -1 else
 ----------------------------------------------------------------------------------------
 */
long SAVE_fn_lGetVersionNumberFromDirExt( char *_szSavedDirectoryName )
{
	char	*p_szExt;
	long	lVersionNumber = -1;

	p_szExt = strchr( _szSavedDirectoryName, '.' );
	if (p_szExt != NULL)
	{
		if ( strlen( ++p_szExt ) == 3)
		{
			lVersionNumber = 0;
			while (*p_szExt != 0)
			{
				if ( !isdigit (*p_szExt) )
					return -1;
				lVersionNumber = lVersionNumber * 10 + (*p_szExt++ - '0');
			}
		}
	}
	return lVersionNumber;
}


/*
 ----------------------------------------------------------------------------------------
  Description   : return name of directory used to store data for a given version
  lVersion            -> version number
  szVersionDirName    -> to put dir name (assumed to be long enough )
 ----------------------------------------------------------------------------------------
 */
void SAVE_fn_vGetVersionDirName( long _lVersion, char *_szVersionDirName )
{
	sprintf( _szVersionDirName, "%s.%03d", fn_szGetGameDataPath(), _lVersion );
}


/*
 ----------------------------------------------------------------------------------------
  Description   : delete a version directory
  _lVersion           -> number of version 
  Returns (BOOL ) true if success
 ----------------------------------------------------------------------------------------
 */
BOOL SAVE_fn_bDeleteVersion( long _lVersion )
{
	char szVersionName[ _MAX_PATH ];
	SAVE_fn_vGetVersionDirName( _lVersion, szVersionName );
	if (!SAVE_fn_bDeleteTree( szVersionName ))
	{
		char szMessage[100];
		sprintf( szMessage, "Can't delete old %s version directory", szVersionName );
		SAVE_fn_vErrorMessage( szMessage );
		return FALSE;
	}
	return TRUE;
}


/*
 ----------------------------------------------------------------------------------------
  Description   : rename a version
  _lOldVersion        -> old version number
  _lNewVersion        -> new version number
  Returns (BOOL )
 ----------------------------------------------------------------------------------------
 */
BOOL SAVE_fn_bRenameVersion( long _lOldVersion, long _lNewVersion )
{
	char szOldVersion[ _MAX_PATH ];
	char szNewVersion[ _MAX_PATH ];
	
	SAVE_fn_vGetVersionDirName( _lOldVersion, szOldVersion );
	SAVE_fn_vGetVersionDirName( _lNewVersion, szNewVersion );

	if ( !MoveFile( szOldVersion, szNewVersion) )
	{
		char szMessage[100];
		sprintf( szMessage, "Can't rename old %s version to new version %s ", szOldVersion, szNewVersion );
		SAVE_fn_vErrorMessage( szMessage );
		return FALSE;
	}
	return TRUE;
}


/*
 ----------------------------------------------------------------------------------------
  Description   : analyse the current version that are on disk
  a_cVersion	-> array of 1000 char that will be filled 
					each case is a flag that indicate if a version number is used or not
  return (long) number of used version
 ----------------------------------------------------------------------------------------
 */
long SAVE_fn_lGetUsedVersion( char *a_cVersion )
{
	WIN32_FIND_DATA stFindData;
	HANDLE			hFind;
	char			szFilter[ _MAX_PATH ];
	long			lNumberOfVersions = 0;
	long			lVersion;

	/*
	 * set all version as unused
	 */
	memset ( a_cVersion , 0, 1000 );

	sprintf( szFilter, "%s.*", fn_szGetGameDataPath() );

	if( (hFind = FindFirstFile(szFilter, &stFindData )) == INVALID_HANDLE_VALUE)
		return 0;

	do
	{
		if ( stFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
		{
			lVersion = SAVE_fn_lGetVersionNumberFromDirExt( stFindData.cFileName );
			if (lVersion != -1)
			{
				a_cVersion[ lVersion ] = 1;
				lNumberOfVersions++ ;
			}
		}
	} while(FindNextFile( hFind, &stFindData ));

	FindClose( hFind );

	return lNumberOfVersions;
}


/*
 ----------------------------------------------------------------------------------------
  Description   : analyse the current version that are on disk
 ----------------------------------------------------------------------------------------
 */
BOOL SAVE_fn_bAnalyseCurrentUsedVersion( void )
{
	char a_cVersion[1000];
	long lNumberOfVersions;
	long lVersion;
	long lVersionIndex;
	long lLastUnusedVersion = -1;


	/* 
	 * get used versions
	 */
	lNumberOfVersions = SAVE_fn_lGetUsedVersion( a_cVersion );
	if (lNumberOfVersions == 0)
	{
		SAVE_g_lNextVersionNumber = 0;
		return TRUE;
	}

	/*
	 * check for hole in version number
	 */
	lVersion = 0;
	for (lVersionIndex = 0; lVersion < lNumberOfVersions; lVersionIndex ++)
	{
		if (a_cVersion[ lVersionIndex ])
		{
			lVersion ++;
		}
		else
		{
			lLastUnusedVersion = lVersionIndex;
		}
	}

	if (lLastUnusedVersion == -1)
	{
		SAVE_g_lNextVersionNumber  = lNumberOfVersions;
	}
	else
	{
		for (lVersionIndex = 0; lVersionIndex < lLastUnusedVersion; lVersionIndex++)
		{
			if (a_cVersion[ lVersionIndex ])
			{
				if (!SAVE_fn_bDeleteVersion( lVersionIndex ))
				{
					return FALSE;
				}
			}
		}
		lVersion = 0;
		for (lVersionIndex = lLastUnusedVersion + 1; a_cVersion[ lVersionIndex ]; lVersionIndex ++)
		{
			if (!SAVE_fn_bRenameVersion( lVersionIndex, lVersion ++ ))
			{
				return FALSE;
			}
		}
		SAVE_g_lNextVersionNumber  = lVersion;
	}

	return TRUE;
}

/*
 =======================================================================================
	modified File functions
 =======================================================================================
 */

/*
 ----------------------------------------------------------------------------------------
  Description   : Add Gamedata dir behind name if not already here and check if file
					exist
  szFile              -> name to check
  Returns (BOOL ) true if file exist
 ----------------------------------------------------------------------------------------
 */
BOOL SAVE_fn_bGetGameDataFile( char *szFile )
{
	if (strnicmp( szFile, SAVE_g_szGameData, SAVE_g_lGameDataLength ) != 0)
	{
		memmove( szFile + SAVE_g_lGameDataLength + 1, szFile, strlen( szFile ));
		memcpy ( szFile, SAVE_g_szGameData, SAVE_g_lGameDataLength );
		*(szFile + SAVE_g_lGameDataLength) = '\\';
	}
	return ( _access( szFile, 0) == 0);
}


/*
 ----------------------------------------------------------------------------------------
  Description   : add file in array of file to save. check if file doesn't still exist
  szFile              -> name to add
 ----------------------------------------------------------------------------------------
 */
void SAVE_fn_vAddFile( char *_szFile, char _cNew )
{
	long lFile;
	/*
	 * check if file doesn't till exist in list
	 */
	for (lFile = 0; lFile < SAVE_gs_lNumberOfFiles; lFile++)
	{
		if (stricmp( _szFile, SAVE_gs_d_stFile[ lFile ].szName ) == 0 )
			return;
	}
	/*
	 * Add file
	 */
	SAVE_gs_d_stFile[ SAVE_gs_lNumberOfFiles ].szName = (char *) malloc( strlen( _szFile ) + 1 );
	strcpy( SAVE_gs_d_stFile[ SAVE_gs_lNumberOfFiles ].szName, _szFile );
	SAVE_gs_d_stFile[ SAVE_gs_lNumberOfFiles ].cNew = _cNew;
	SAVE_gs_lNumberOfFiles++;
}


/*
 ----------------------------------------------------------------------------------------
  Description   : delete file list
 ----------------------------------------------------------------------------------------
 */
void SAVE_fn_vFreeFileList( void )
{
	long lFile;

	if ( SAVE_gs_d_stFile != NULL)
	{
		for (lFile = 0; lFile < SAVE_gs_lNumberOfFiles; lFile++)
			free ( SAVE_gs_d_stFile[ lFile ].szName );

		free ( SAVE_gs_d_stFile );
		SAVE_gs_d_stFile = NULL;
	}
}


/*
 ----------------------------------------------------------------------------------------
  Description   : read all notification to buil list of files that would be modified
  return (long ) : number of file in list
 ----------------------------------------------------------------------------------------
 */
long SAVE_fn_lGetNotifiedFileList( void )
{
	unsigned int				uiPos;
	SCR_tdst_Ntfy_Description	*p_stNtfy;
	char						szFile[ _MAX_PATH ];
	char						*p_szEndFile;
	long						lNbMaxFiles;

	/*
	 * get number max of files and allocate array to store filenames
	 */
	lNbMaxFiles = SCR_g_st_Ntfy_Array.uiNumValues;
	SAVE_gs_lNumberOfFiles = 0;
	SAVE_gs_d_stFile = (SAVE_tdstFile *) malloc( (lNbMaxFiles + 1) * sizeof( SAVE_tdstFile ) );
	
	/*
	 * add eventually modification file
	 */
	strcpy( szFile, "modiflst.txt" );
	SAVE_fn_vAddFile( szFile, SAVE_fn_bGetGameDataFile( szFile ) ? 0 : 1 );

	/*
	 * parse all notifications
	 */
	for (uiPos = 0; uiPos < (unsigned short) lNbMaxFiles; uiPos ++)
	{
		SCR_M_DyAr_GetNextElement(SCR_tdst_Ntfy_Description, uiPos, p_stNtfy, SCR_g_st_Ntfy_Array);
		
		if (p_stNtfy == NULL)
			break;

		strcpy( szFile, p_stNtfy->a_szSectionName );
		p_szEndFile = strchr( szFile, '^' );
		if (p_szEndFile != NULL)
			*p_szEndFile = 0;

		SAVE_fn_vAddFile( szFile, SAVE_fn_bGetGameDataFile( szFile ) ? 0 : 1 );
	}

	return SAVE_gs_lNumberOfFiles;
}


/*
 ----------------------------------------------------------------------------------------
  Description   : create new tree with saved file
 ----------------------------------------------------------------------------------------
 */
BOOL SAVE_fn_bCreateNewVersion( void )
{
	/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
	long lFile;
	char szNewFile[ _MAX_PATH ];
	char *p_szNewFile;
	char *p_szEndPath;
	FILE *hpFile;
	/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
	
	SAVE_fn_vGetVersionDirName( SAVE_g_lNextVersionNumber, szNewFile );
	p_szNewFile = szNewFile + strlen( szNewFile );
	*p_szNewFile++ = '\\';

	/*
	 * copy file that exist and are going to be modified
	 */
	for (lFile = 0; lFile < SAVE_gs_lNumberOfFiles; lFile ++)
	{
		if ( SAVE_gs_d_stFile[lFile].cNew == 0)
		{
			strcpy( p_szNewFile, SAVE_gs_d_stFile[ lFile ].szName + SAVE_g_lGameDataLength + 1 );
			p_szEndPath = strrchr( szNewFile, '\\' );
			*p_szEndPath = 0;
			if ( !SAVE_fn_bCreateDirectory( szNewFile ) )
				return FALSE;
			*p_szEndPath = '\\';
			if ( !CopyFile( SAVE_gs_d_stFile[ lFile ].szName, szNewFile, FALSE ) )
			{
				//char szMessage[ 512 ];
				//sprintf( szMessage, "Can't copy %s to %s", SAVE_g_d_szFile[ lFile ], szNewFile );
				return FALSE;
			}
		}
	}

	/*
	 * create a file with all file that are going to be created
	 */
	strcpy( p_szNewFile, "delete.lst" );
	if ((hpFile = fopen( szNewFile, "wt" )) == NULL)
		return FALSE;

	for (lFile = 0; lFile < SAVE_gs_lNumberOfFiles; lFile ++)
	{
		if ( SAVE_gs_d_stFile[lFile].cNew )
			fprintf( hpFile, "%s\n", SAVE_gs_d_stFile[ lFile ].szName + SAVE_g_lGameDataLength + 1 );
	}
	fclose( hpFile );
	
	return TRUE;
}


/*
 =======================================================================================
	principal function 
 =======================================================================================
 */

/*
 ----------------------------------------------------------------------------------------
  Description   : make a copy of file that will be deleted.
 ----------------------------------------------------------------------------------------
 */
BOOL SAVE_fn_bGenerateACopyOfFilesBeforeModification( void )
{
	SAVE_g_bContinue = TRUE;

	if (SAVE_g_bGenerateIsActive == 2)
	{
		CString csFileName;
		csFileName = M_GetMainApp()->m_csEditorDataPath + C_szInterfaceIniFile;
		SAVE_g_bGenerateIsActive = GetPrivateProfileInt ("SecuritySave", "Active", 1, (char*)(LPCSTR) csFileName);
	}

	if ( !SAVE_g_bGenerateIsActive )
		return TRUE;

	if (SCR_g_st_Ntfy_Array.uiNumValues == 0)
		return TRUE;

	/*
	 * init
	 */
	SAVE_g_szGameData = fn_szGetGameDataPath();
	SAVE_g_lGameDataLength = strlen( SAVE_g_szGameData );

	if (SAVE_fn_bAnalyseCurrentUsedVersion())
	{
		if (SAVE_fn_lGetNotifiedFileList())
		{
			SAVE_fn_bCreateNewVersion();
			SAVE_fn_vFreeFileList();
		}
	}
	return SAVE_g_bContinue;
}

#endif /*ACTIVE_EDITOR*/