/*
 =======================================================================================
  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"

#include <io.h>
#include <sys/stat.h>
#include "SaveMngr.h"

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

/*
 =======================================================================================
	CONSTANTS
 =======================================================================================
 */
#define C_szGameDataDir		"GameData"
#define C_szOldGameDataDir	"GameDat_"


/*
 =======================================================================================
	GLOBALS
 =======================================================================================
 */
static char SAVE_g_szDirName[ _MAX_PATH ];

/*
 =======================================================================================
 =======================================================================================
	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();
}


/*
 =======================================================================================
	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 deleting all existing version of saved data\n" );
	p_szError += sprintf( p_szError, "Data in %s.XXX directory will not be correct\n" );
	p_szError += sprintf( p_szError, "---------------------------------------------------\n" );
	p_szError += sprintf( p_szError, "%s\n\n", _szMessage );
	AfxMessageBox( szError, MB_ICONSTOP | MB_OK );
}

/*
 =======================================================================================
	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, BOOL _bOld )
{
	sprintf( _szVersionDirName, "%s\\..\\%s.%03d", g_stIniData.szLocalPath, _bOld ? C_szOldGameDataDir : C_szGameDataDir, _lVersion );
}


/*
 ----------------------------------------------------------------------------------------
  Description   : delete a version directory
  _lVersion           -> number of version 
  Returns (BOOL ) true if success
 ----------------------------------------------------------------------------------------
 */
BOOL SAVE_fn_bDeleteVersion( long _lVersion, BOOL _bOld )
{
	char szVersionName[ _MAX_PATH ];
	SAVE_fn_vGetVersionDirName( _lVersion, szVersionName, _bOld);
	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   : 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, BOOL _bOld )
{
	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\\..\\%s.*", g_stIniData.szLocalPath, _bOld ? C_szOldGameDataDir : C_szGameDataDir );

	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   : rename a version
  _lOldVersion        -> old version number
  _lNewVersion        -> new version number
  Returns (BOOL )
 ----------------------------------------------------------------------------------------
 */
BOOL SAVE_fn_bRenameVersionToOldVersion( long _lVersion )
{
	char szOldVersion[ _MAX_PATH ];
	char szVersion[ _MAX_PATH ];
	
	SAVE_fn_vGetVersionDirName( _lVersion, szVersion, FALSE );
	SAVE_fn_vGetVersionDirName( _lVersion, szOldVersion, TRUE );

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



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

/*
 ----------------------------------------------------------------------------------------
  Description   : delete all old versions and rename recent version
 ----------------------------------------------------------------------------------------
 */
BOOL SAVE_fn_bDeleteAllVersions( void )
{
	char a_cVersion[1000];
	long lNumberOfVersions;
	long lVersion;
	long lVersionIndex;

	/* 
	 * get old used versions
	 */
	lNumberOfVersions = SAVE_fn_lGetUsedVersion( a_cVersion, TRUE );

	/*
	 * remove all old versions
	 */
	for (lVersion = 0, lVersionIndex = 0; lVersion < lNumberOfVersions; lVersionIndex ++)
	{
		if (a_cVersion[ lVersionIndex ])
		{
			lVersion ++;
			if (!SAVE_fn_bDeleteVersion( lVersionIndex, TRUE ))
				return FALSE;
		}
	}

	/* 
	 * get used versions
	 */
	lNumberOfVersions = SAVE_fn_lGetUsedVersion( a_cVersion, FALSE );
	if (lNumberOfVersions == 0)
		return TRUE;

	/*
	 * check for hole in version number
	 */
	for (lVersion = 0, lVersionIndex = 0; lVersion < lNumberOfVersions; lVersionIndex ++)
	{
		if (a_cVersion[ lVersionIndex ])
		{
			lVersion ++;
			if (!SAVE_fn_bRenameVersionToOldVersion( lVersionIndex ))
				return FALSE;
		}
	}

	return TRUE;
}

