/*
 **************************************************************************************************
	*	CPA_Ed_1	team *	
	*   File  Management *
 **************************************************************************************************
*/

#include "Stdinc.h"


#include "File.h"
#include "Types.h"


#ifdef LDT_USE_SINGLE_FILE_BUFFER

char* GBuff = NULL;
unsigned long GBuffSize = 0;
#define GBuffMargin (1024*50) /* 50k above the file size */

#endif

/*
 *  OPEN a file  for reading
 *  --------------------------------------------------------------
 *  _pInfoMemFile   :   the value returned 
 *  _Name			:   Name of the file.
 *  Return			:   Error code(zero for none)
 *  --------------------------------------------------------------
 *  Conditions  :   If the file does not exist or cannot be found, the fopen call fails.
 *  --------------------------------------------------------------
 *  Comments    :	If succes, the pointer value not NULL
 *				    A null pointer value indicates an error.
 *					If some errors occur, then all space are free
 */
//#define OVERLAPPED_
#ifdef OVERLAPPED_
#define OVERLAPPED_NO_BUFFERING
long	f_OpenMem(LDT_tdst_MemFile **_pInfoMemFile,char* _Name)
{
	long Err = LDT_EI_Err_None;

	*_pInfoMemFile= (LDT_tdst_MemFile *)malloc(sizeof(LDT_tdst_MemFile));

	if(*_pInfoMemFile)
	{
		HANDLE File;

		(*_pInfoMemFile)->sPathLength = 0;
		(*_pInfoMemFile)->pName= (char *)malloc(( strlen(_Name)+1 ));
		strcpy((*_pInfoMemFile)->pName , _Name);
		(*_pInfoMemFile)->pBuffer = NULL;

#ifdef OVERLAPPED_NO_BUFFERING
		File = CreateFile( _Name, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING, NULL);
#else
		File = CreateFile( _Name, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
#endif
		if(File != INVALID_HANDLE_VALUE)
		{
			(*_pInfoMemFile)->Size = GetFileSize( File, NULL);
			if((*_pInfoMemFile)->Size)
			{
				DWORD BoundSize, VolumeSectorsize, SectorsPerCluster, NumberOfFreeClusters, TotalNumberOfClusters ;
				char Drive[4] = "C:\\";
				if(_Name[1] == ':') Drive[0] = _Name[0];
				GetDiskFreeSpace(_Name[1] == ':'? Drive: NULL, &SectorsPerCluster, &VolumeSectorsize, &NumberOfFreeClusters, &TotalNumberOfClusters );
				
#ifdef OVERLAPPED_NO_BUFFERING
				BoundSize = (*_pInfoMemFile)->Size + 5 + VolumeSectorsize;
				BoundSize -= BoundSize%VolumeSectorsize;
				(*_pInfoMemFile)->pBuffer= (char*)VirtualAlloc( NULL, BoundSize, MEM_COMMIT, PAGE_READWRITE );
#else
				BoundSize = (*_pInfoMemFile)->Size + 5;
				(*_pInfoMemFile)->pBuffer= (char*)malloc( BoundSize );
#endif
				if((*_pInfoMemFile)->pBuffer)
				{
					OVERLAPPED overlapped;
					overlapped.Offset = overlapped.OffsetHigh = 0;
					overlapped.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
					ReadFile( File, (*_pInfoMemFile)->pBuffer, BoundSize, NULL, &overlapped);
					WaitForSingleObject( overlapped.hEvent, INFINITE);
					CloseHandle(overlapped.hEvent);

					(*_pInfoMemFile)->Pos = 0;
					strcpy((*_pInfoMemFile)->pBuffer+(*_pInfoMemFile)->Size , "\n\"\n\n");
				}
				else Err = LDT_EI_Err_Malloc;
			}
			else Err = LDT_EI_Err_SeekFile;
			CloseHandle(File);
		}
		else Err = LDT_EI_Err_OpenFile;
	}
	else Err = LDT_EI_Err_Malloc;
	
	if( Err != LDT_EI_Err_None && *_pInfoMemFile )
	{
		free((*_pInfoMemFile)->pName);
		#ifndef LDT_USE_SINGLE_FILE_BUFFER
		if((*_pInfoMemFile)->pBuffer) free((*_pInfoMemFile)->pBuffer);
		#endif
		
		free((*_pInfoMemFile));
		*_pInfoMemFile = NULL;
	}
	return Err;
}

#else

long	f_OpenMem(LDT_tdst_MemFile **_pInfoMemFile,char* _Name)
{
	long Err = LDT_EI_Err_None;
	FILE *pFile;			/* Pointer of the file */

	*_pInfoMemFile= (LDT_tdst_MemFile *)malloc(sizeof(LDT_tdst_MemFile));

	if(*_pInfoMemFile)
	{
		(*_pInfoMemFile)->sPathLength = 0;
		(*_pInfoMemFile)->pName= (char *)malloc(( strlen(_Name)+1 ));
		strcpy((*_pInfoMemFile)->pName , _Name);

		(*_pInfoMemFile)->pBuffer = NULL;
		pFile = fopen(_Name, "rb");
		if(pFile)
		{
			(*_pInfoMemFile)->Size = _filelength (_fileno(pFile));
			if((*_pInfoMemFile)->Size)
			{
#ifdef LDT_USE_SINGLE_FILE_BUFFER
				if(GBuffSize < (*_pInfoMemFile)->Size)
				{
					if(GBuff) free(GBuff);
					GBuff = malloc( GBuffSize = (*_pInfoMemFile)->Size + 5 + GBuffMargin);
				}
				(*_pInfoMemFile)->pBuffer = GBuff;
#else
				(*_pInfoMemFile)->pBuffer= (char*) malloc(((*_pInfoMemFile)->Size + 5 ));
#endif
				if((*_pInfoMemFile)->pBuffer)
				{
					if(fread((*_pInfoMemFile)->pBuffer,(*_pInfoMemFile)->Size,1,pFile))
					{
						(*_pInfoMemFile)->Pos = 0;
						strcpy((*_pInfoMemFile)->pBuffer+(*_pInfoMemFile)->Size , "\n\"\n\n");
					}
					else Err = LDT_EI_Err_ReadFile;
				}
				else Err = LDT_EI_Err_Malloc;
			}
			else Err = LDT_EI_Err_SeekFile;
			fclose(pFile);
		}
		else Err = LDT_EI_Err_OpenFile;
	}
	else Err = LDT_EI_Err_Malloc;
	
	if( Err != LDT_EI_Err_None && *_pInfoMemFile )
	{
		free((*_pInfoMemFile)->pName);
		
		#ifndef LDT_USE_SINGLE_FILE_BUFFER
		if((*_pInfoMemFile)->pBuffer) free((*_pInfoMemFile)->pBuffer);
		#endif
		
		free((*_pInfoMemFile));
		*_pInfoMemFile = NULL;
	}
	return Err;
}

#endif


/*
 *  READ a piece of file  from memory
 *  --------------------------------------------------------------
 *  _PointerDest	: Address of the destination
 *  _Size			: size of data to move (in bytes)
 *  _pInfoMemFile	: Source file address in memory
 *  Return			: error code if <0 or the Size of data moved(for none error)  
 *  --------------------------------------------------------------
 *  Conditions  :   
 *  --------------------------------------------------------------
 *  Comments    :	If succes, the return value are the Size of data moved
 *				    A value<0 indicate an error	
 */
long	f_ReadMem(char * _PointerDest,size_t _Size,LDT_tdst_MemFile *_pInfoMemFile ) 
{
	long Err;

	/*if( (_pInfoMemFile->Pos < _pInfoMemFile->Size) && */
	if (_pInfoMemFile->Size -_pInfoMemFile->Pos >= _Size) 
	{
		if(_PointerDest == memmove(_PointerDest,_pInfoMemFile->pBuffer+_pInfoMemFile->Pos,_Size))
		{
			_pInfoMemFile->Pos += _Size;
			return _Size;
		}
		else
			Err = -LDT_EI_Err_MemMove;
	}
	else
		Err = -LDT_EI_Err_NotEnoughDataForMove;

	return Err;
}

/*
 *  CLOSE a file (in memory and stream)
 *  --------------------------------------------------------------
 *  _pInfoMemFile	: Source file address in memory
 *  Return			: error code  
 *  --------------------------------------------------------------
 *  Conditions  :   close an open stream
 *  --------------------------------------------------------------
 *  Comments    :	fclose returns 0 if the stream is successfully closed
 *						   return EOF to indicate an error..
 *					Return value must be int 
*/
long	f_CloseMem(LDT_tdst_MemFile *_pInfoMemFile)
{
#ifdef LDT_MULTITHREADED
#ifdef OVERLAPPED_NO_BUFFERING
		VirtualFree( _pInfoMemFile->pBuffer, 0, MEM_RELEASE);
#else
		free( _pInfoMemFile->pBuffer );
#endif
#else
#ifndef LDT_USE_SINGLE_FILE_BUFFER
				free(_pInfoMemFile->pBuffer);
#endif
#endif
				free(_pInfoMemFile->pName);
				free(_pInfoMemFile);
		return 	LDT_EI_Err_None;
}

/*
 *  SEEK a file in memory to a given position.
 *  --------------------------------------------------------------
 *  _File       :   File to seek.
 *  _Offset     :   Offset to seek.
 *  _Origin     :   SEEK_SET or SEEK_END.
 *  Return		:   error code
 *  --------------------------------------------------------------
 *  Conditions  :   The argument origin must be one of the following constant:
 *					-SEEK_CUR Current position of file pointer
 *					-SEEK_END End of file
 *					-SEEK_SET Beginning of file
 *  --------------------------------------------------------------
 *  Comments    :	If successful, fseek returns 0
 *					Return value must be long
 */
long f_SeekMem(LDT_tdst_MemFile *_pInfoMemFile ,long _Offset,int _Origin)
{
	long Err;

	
	Err = LDT_EI_Err_None;
	switch (_Origin)
	{
		case SEEK_SET:
			if(_Offset>=0 &&  _pInfoMemFile->Size >= (size_t)_Offset)
				_pInfoMemFile->Pos = _Offset;
			else
				Err = LDT_EI_Err_SeekInMem;
			break;
  		case SEEK_CUR:
			if(_Offset>0 && ((_pInfoMemFile->Size-_pInfoMemFile->Pos) >= (size_t)_Offset) )
				_pInfoMemFile->Pos += _Offset;
			else if( _Offset<0 && (_pInfoMemFile->Pos >= (size_t)(-_Offset)))
				_pInfoMemFile->Pos += _Offset;
			else
				Err = LDT_EI_Err_SeekInMem;
			break;
  		case SEEK_END:
			if(_Offset <= 0 && (_pInfoMemFile->Size >= (size_t)(-_Offset)))
				_pInfoMemFile->Pos = _Offset+_pInfoMemFile->Size;
			else
				Err = LDT_EI_Err_SeekInMem;
			break;
		default:
			 Err = LDT_EI_Err_SeekInMem;
	}

	return Err;
}


long f_TellMem(LDT_tdst_MemFile *_pInfoMemFile )
{
	return _pInfoMemFile->Pos;
}
