/*
 =======================================================================================
  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 "acp_base.h"
//#include "itf/CPAProj.hpp"

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

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

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


char *fn_szGetGameDataPath( void )
{
	return "GameData";
}

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



/*
 ----------------------------------------------------------------------------------------
  Description   : move directory (source and target dir names are stored in global var
				SAVE_g_szDirName and SAVE_g_szTargetDirName )
  Returns (BOOL ) true if success
 ----------------------------------------------------------------------------------------
 */
BOOL fn_bMoveDirectory( void )
{
	WIN32_FIND_DATA stFindData;
	HANDLE			hFind;
	char			*p_szEndSource;
	char			*p_szEndTarget;

	if ( _access( SAVE_g_szTargetDirName, 0) != 0)
		return MoveFile( SAVE_g_szDirName , SAVE_g_szTargetDirName );

	p_szEndSource = SAVE_g_szDirName + strlen( SAVE_g_szDirName );
	p_szEndTarget = SAVE_g_szTargetDirName + strlen( SAVE_g_szTargetDirName );

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

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

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

				if ( !DeleteFile( SAVE_g_szTargetDirName ) )
					return FALSE;
			}

			if ( !MoveFile(  SAVE_g_szDirName, SAVE_g_szTargetDirName ) )
				return FALSE;
		}
		*p_szEndSource = *p_szEndTarget = 0;
	} while(FindNextFile( hFind, &stFindData ));

	FindClose( hFind );

	return RemoveDirectory( SAVE_g_szDirName );
}

/*
 ----------------------------------------------------------------------------------------
  Description   : move a directory into another one
  szSourceDir         -> source directory
  szTargetDir         -> target directory
  Returns (BOOL ) true if success
 ----------------------------------------------------------------------------------------
 */
BOOL SAVE_fn_bMoveDirectory( char *szSourceDir, char *szTargetDir )
{
	strcpy( SAVE_g_szTargetDirName, szTargetDir );
	strcpy( SAVE_g_szDirName, szSourceDir );

	return fn_bMoveDirectory();
}

/*
 =======================================================================================
	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, SYSTEMTIME *a_stTime )
{
	WIN32_FIND_DATA stFindData;
	HANDLE			hFind;
	char			szFilter[ _MAX_PATH ];
	long			lNumberOfVersions = 0;
	long			lVersion;
	FILETIME		stLocalFileTime;

	/*
	 * 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;
				if (a_stTime != NULL)
				{
					FileTimeToLocalFileTime( &stFindData.ftCreationTime, &stLocalFileTime );
					FileTimeToSystemTime( &stLocalFileTime, &a_stTime[ lVersion ] );
				}
				lNumberOfVersions++ ;
			}
		}
	} while(FindNextFile( hFind, &stFindData ));

	FindClose( hFind );

	return lNumberOfVersions;
}


/*
 ----------------------------------------------------------------------------------------
  Description   : analyse the current version that are on disk
 ----------------------------------------------------------------------------------------
 */
long SAVE_fn_lAnalyseCurrentUsedVersion( char *a_cVersion, SYSTEMTIME *a_stTime )
{
	long lNumberOfVersions;
	long lVersion;
	long lVersionIndex;
	long lLastUnusedVersion = -1;

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

	/*
	 * 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)
		return lNumberOfVersions;
	
	for (lVersionIndex = 0; lVersionIndex < lLastUnusedVersion; lVersionIndex++)
	{
		if (a_cVersion[ lVersionIndex ])
		{
			if (!SAVE_fn_bDeleteVersion( lVersionIndex ))
				return 0;
		}
	}

	lVersion = 0;
	for (lVersionIndex = lLastUnusedVersion + 1; a_cVersion[ lVersionIndex ]; lVersionIndex ++)
	{
		if (!SAVE_fn_bRenameVersion( lVersionIndex, lVersion ))
			return 0;
		a_cVersion[lVersion] = 1;
		if (a_stTime != NULL)
			a_stTime[lVersion] = a_stTime[lVersionIndex];
		lVersion ++;
	}
	return lVersion;
}
