#include "Gos.pch"
#include "File.h"
#include "ClassInfo.h"


GosFile *(*GosFile::Read)( _TCHAR *, DWORD , DWORD , void * ) = File::Read;
GosFile *(*GosFile::Open)( const TCHAR * ) = File::Open;
GosFile *(*GosFile::BackgroundRead)( _TCHAR *, DWORD, DWORD, void * ) = File::BackgroundRead;
GosFile *(*GosFile::MemoryMap)( _TCHAR * ) = File::MemoryMap;
bool (*GosFile::Exists)( const TCHAR * ) = File::Exists;

void (*GosFile::Shutdown)() = File::Shutdown;
void (*GosFile::GetFullPathName)( TCHAR *, const TCHAR * ) = File::GetFullPathName;

GosClassInfo *	File::pClassInfo = GosClassInfo::RegisterClass(_T("GosFile"));

#define VALIDATE_THIS

HANDLE File::hBackgroundThread = 0;
File * File::pFirstBackground = NULL;
File * File::pLastBackground = NULL;
HANDLE File::hFileEvent;
bool File::killFileThread = false;
CRITICAL_SECTION File::CritSectBackgroundFile = {0};

void File::ReadData()
{
	VALIDATE_THIS;
	HANDLE hFile = CreateFile( m_FileName, GENERIC_READ, FILE_SHARE_READ,NULL, OPEN_EXISTING, NULL, NULL );
	if( hFile==INVALID_HANDLE_VALUE )
	{
		STOP(("File missing"));
	}
	DWORD size = GetFileSize( hFile, NULL );
	DWORD sizeRead;
	if( size > m_nOffset )
		size -= m_nOffset;
	if( m_nMaxSize < size )
		size = m_nMaxSize;
	if( !m_fPreAllocated )
		m_pData = GosHeap::Current()->Malloc( size );
	ReadFile( hFile, m_pData, size, &sizeRead, NULL );
	m_nSizeBytes = sizeRead;
	m_fReady = true;
	CloseHandle( hFile );
}

static DWORD WINAPI Gos2::FileBackgroundThread( LPVOID ThreadParam )
{
	return File::BackgroundThread();
}

GosFile *File::Read( const TCHAR *pFileName, DWORD Offset, DWORD MaxSize, void *Buffer )
{
	LOG_API("File::Read");

	File *pFile = new File();

	_tcscpy( pFile->m_FileName, pFileName );
	pFile->m_AccessMode = Normal;
	pFile->m_nOffset = Offset;
	pFile->m_nMaxSize = MaxSize;
	pFile->m_pData = Buffer;
	pFile->m_fPreAllocated = ( Buffer != NULL );
	pFile->m_nSizeBytes = 0;
	pFile->ReadData();
	return pFile;
}

GosFile *File::Open( const TCHAR *pFileName )
{
	/* Not implemented yet */
	pFileName;
	_asm int 3;
	return (GosFile *)NULL;
}

void File::Write( void *Buffer, DWORD Size )
{
	/* Not implemented yet */
	Buffer;
	Size;
	_asm int 3;
}

GosFile *File::BackgroundRead( const TCHAR *pFileName, DWORD Offset, DWORD MaxSize, void *Buffer )
{
	LOG_API("File::BackgroundRead");

	File *pFile = new File();

	_tcscpy( pFile->m_FileName, pFileName );
	pFile->m_AccessMode = Background;
	pFile->m_nOffset = Offset;
	pFile->m_nMaxSize = MaxSize;
	pFile->m_pData = Buffer;
	pFile->m_fPreAllocated = ( Buffer != NULL );
	pFile->m_nSizeBytes = 0;

	if( !hFileEvent )
		hFileEvent = CreateEvent( NULL, FALSE, FALSE, NULL );

	if( !hBackgroundThread )
	{
		DWORD ID;
		killFileThread = false;
		hBackgroundThread = CreateThread( NULL, 0, FileBackgroundThread, 0, 0, &ID );
	}

	pFile->m_pNextBackground = NULL;
	EnterCriticalSection( &CritSectBackgroundFile );
	if( pLastBackground )
	{
		pLastBackground->m_pNextBackground = pFile;
	}
	else
	{
		pFirstBackground = pFile;
	}
	pLastBackground = pFile;
	LeaveCriticalSection( &CritSectBackgroundFile );
	SetEvent( hFileEvent );

	return pFile;
}

GosFile *File::MemoryMap( const TCHAR *pFileName )
{
	LOG_API("File::MemoryMap");

	File *pFile = new File();

	_tcscpy( pFile->m_FileName, pFileName );
	pFile->m_AccessMode = Background;
	pFile->m_fPreAllocated = false;
	pFile->m_nSizeBytes = 0;

	pFile->m_hFile = CreateFile( pFile->m_FileName, GENERIC_READ, FILE_SHARE_READ,NULL, OPEN_EXISTING, NULL, NULL );
	if( pFile->m_hFile==INVALID_HANDLE_VALUE )
	{
	}
	pFile->m_nSizeBytes=GetFileSize( pFile->m_hFile, NULL );
	//
	// Map file into memory
	//
	pFile->m_hFileMapping=CreateFileMapping( pFile->m_hFile, NULL, PAGE_READONLY, 0, 0, NULL );
	if( pFile->m_hFileMapping==NULL )
	{
		STOP(( _T("Could not map file '%s'"),pFile->m_FileName ));
	}
	//
	// Get pointer to base
	//
	pFile->m_pData = MapViewOfFile( pFile->m_hFileMapping, FILE_MAP_READ, 0, 0, 0 );
	if( pFile->m_pData==NULL )
	{
		STOP(( _T("Could not map file '%s'"),pFile->m_FileName ));
	}

	return pFile;

}

File::File() : GosObj( pClassInfo )
{
}

File::~File()
{
	LOG_API("File::~File");
	VALIDATE_THIS;

	switch( m_AccessMode )
	{
	case Background:
		if( !Ready() )
			STOP(( "Closing a file not yet read" ));
		// .. continue
	case Normal:
		if( m_pData && !m_fPreAllocated )
			GosHeap::Free( m_pData );
		break;
	case MemoryMapped:
		UnmapViewOfFile( m_pData );
		CloseHandle( m_hFileMapping );
		CloseHandle( m_hFile );
		break;
	}
}

DWORD File::BackgroundThread()
{
	File *pFile;
	while ( !killFileThread )
	{
		WaitForSingleObject( hFileEvent, INFINITE);
		if( killFileThread )
			continue;
		if( !pFirstBackground )
			continue;
		EnterCriticalSection( &CritSectBackgroundFile );
		pFile = pFirstBackground;
		pFirstBackground = pFirstBackground->m_pNextBackground;
		if( !pFirstBackground )
			pLastBackground = NULL;
		LeaveCriticalSection( &CritSectBackgroundFile );
		pFile->ReadData();
	} 
	return 0;
}

void File::Shutdown()
{
	// need to kill background thread if present
}

void File::GetFullPathName( TCHAR* FullPath, const TCHAR *pFileName )
{
	_tcscpy( FullPath, pFileName );
}

bool File::Exists( const TCHAR *pFileName )
{
	return false;
}