//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//

#include "vmpi_filesystem_internal.h"
#include "tier1/utlbuffer.h"


bool g_bDisableFileAccess = false;


CBaseVMPIFileSystem *g_pBaseVMPIFileSystem = NULL;
IFileSystem *g_pOriginalPassThruFileSystem = NULL;

void* GetVMPIFileSystem()
{
	return (IBaseFileSystem*)g_pBaseVMPIFileSystem;
}

EXPOSE_INTERFACE_FN( GetVMPIFileSystem, IBaseFileSystem, BASEFILESYSTEM_INTERFACE_VERSION )


IFileSystem* VMPI_FileSystem_Init( int maxMemoryUsage, IFileSystem *pPassThru )
{
	Assert( g_bUseMPI );
	Assert( !g_pBaseVMPIFileSystem );
	g_pOriginalPassThruFileSystem = pPassThru;

	if ( g_bMPIMaster )
	{
		extern CBaseVMPIFileSystem* CreateMasterVMPIFileSystem( int maxMemoryUsage, IFileSystem *pPassThru );
		CreateMasterVMPIFileSystem( maxMemoryUsage, pPassThru );
	}
	else
	{
		extern CBaseVMPIFileSystem* CreateWorkerVMPIFileSystem();
		CreateWorkerVMPIFileSystem();
	}
	
	// The Create function should have set this. Normally, we'd set g_pBaseVMPIFileSystem right here, but 
	// the create functions may want to receive some messages, in which case they need to set g_pBaseVMPIFileSystem
	// so the packets get routed appropriately.
	Assert( g_pBaseVMPIFileSystem );
	return g_pBaseVMPIFileSystem;
}


IFileSystem* VMPI_FileSystem_Term()
{
	if ( g_pBaseVMPIFileSystem )
	{
		g_pBaseVMPIFileSystem->Release();
		g_pBaseVMPIFileSystem = NULL;

		if ( g_iVMPIVerboseLevel >= 1 )
		{
			if ( g_bMPIMaster )
				Msg( "Multicast send: %dk\n", (g_nMulticastBytesSent + 511) / 1024 );
			else
				Msg( "Multicast recv: %dk\n", (g_nMulticastBytesReceived + 511) / 1024 );
		}
	}
	
	IFileSystem *pRet = g_pOriginalPassThruFileSystem;
	g_pOriginalPassThruFileSystem = NULL;
	return pRet;
}


void VMPI_FileSystem_DisableFileAccess()
{
	g_bDisableFileAccess = true;
}


CreateInterfaceFn VMPI_FileSystem_GetFactory()
{
	return Sys_GetFactoryThis();
}


void VMPI_FileSystem_CreateVirtualFile( const char *pFilename, const void *pData, unsigned long fileLength )
{
	g_pBaseVMPIFileSystem->CreateVirtualFile( pFilename, pData, fileLength );
}


// Register our packet ID.
bool FileSystemRecv( MessageBuffer *pBuf, int iSource, int iPacketID )
{
	if ( g_pBaseVMPIFileSystem )
		return g_pBaseVMPIFileSystem->HandleFileSystemPacket( pBuf, iSource, iPacketID );
	else
		return false;
}


CDispatchReg g_DispatchReg_FileSystem( VMPI_PACKETID_FILESYSTEM, FileSystemRecv );



// ------------------------------------------------------------------------------------------------------------------------ //
// CVMPIFile_Memory implementation.
// ------------------------------------------------------------------------------------------------------------------------ //

void CVMPIFile_Memory::Init( const char *pData, long len, char chMode /* = 'b' */ )
{
	m_pData = pData;
	m_DataLen = len;
	m_iCurPos = 0;
	m_chMode = chMode;
}

void CVMPIFile_Memory::Close()
{
	delete this;
}

void CVMPIFile_Memory::Seek( int pos, FileSystemSeek_t seekType )
{
	if ( seekType == FILESYSTEM_SEEK_HEAD )
		m_iCurPos = pos;
	else if ( seekType == FILESYSTEM_SEEK_CURRENT )
		m_iCurPos += pos;
	else
		m_iCurPos = m_DataLen - pos;
}

unsigned int CVMPIFile_Memory::Tell()
{
	return m_iCurPos; 
}

unsigned int CVMPIFile_Memory::Size() 
{
	return m_DataLen; 
}

void CVMPIFile_Memory::Flush()
{
}

int CVMPIFile_Memory::Read( void* pOutput, int size ) 
{ 
	Assert( m_iCurPos >= 0 );
	int nToRead = min( (int)(m_DataLen - m_iCurPos), size );
	
	if ( m_chMode != 't' )
	{
		memcpy( pOutput, &m_pData[m_iCurPos], nToRead );
		m_iCurPos += nToRead;

		return nToRead;
	}
	else
	{
		int iRead = 0;
		const char *pData = m_pData + m_iCurPos;
		int len = m_DataLen - m_iCurPos;
		
		// Perform crlf translation
		while ( const char *crlf = ( const char * ) memchr( pData, '\r', len ) )
		{
			int canCopy = min( size, crlf - pData );
			memcpy( pOutput, pData, canCopy );
			
			m_iCurPos += canCopy;
			pData += canCopy;
			len -= canCopy;
			
			iRead += canCopy;
			( char * & ) pOutput += canCopy;
			size -= canCopy;

			if ( size && len )
			{
				if ( ( len > 1 ) && ( pData[1] == '\n' ) )
				{
					++ m_iCurPos;
					++ pData;
					-- len;
				}

				* ( char * & ) pOutput = *pData;
				
				++ m_iCurPos;
				++ pData;
				-- len;

				++ iRead;
				++ ( char * & ) pOutput;
				-- size;
			}
			else
				break;
		}
		
		if ( size && len )
		{
			// No crlf characters left
			int canCopy = min( size, len );
			memcpy( pOutput, pData, canCopy );

			m_iCurPos += canCopy;
			pData += canCopy;
			len -= canCopy;

			iRead += canCopy;
			( char * & ) pOutput += canCopy;
			size -= canCopy;
		}

		return iRead;
	}
}

int CVMPIFile_Memory::Write( void const* pInput, int size )
{
	Assert( false ); return 0; 
}


// ------------------------------------------------------------------------------------------------------------------------ //
// CBaseVMPIFileSystem implementation.
// ------------------------------------------------------------------------------------------------------------------------ //

CBaseVMPIFileSystem::~CBaseVMPIFileSystem()
{
}


void CBaseVMPIFileSystem::Release()
{
	delete this;
}


void CBaseVMPIFileSystem::Close( FileHandle_t file )
{
	if ( file )
		((IVMPIFile*)file)->Close();
}

int CBaseVMPIFileSystem::Read( void* pOutput, int size, FileHandle_t file )
{
	return ((IVMPIFile*)file)->Read( pOutput, size );
}

int CBaseVMPIFileSystem::Write( void const* pInput, int size, FileHandle_t file )
{
	return ((IVMPIFile*)file)->Write( pInput, size );
}

void CBaseVMPIFileSystem::Seek( FileHandle_t file, int pos, FileSystemSeek_t seekType )
{
	((IVMPIFile*)file)->Seek( pos, seekType );
}

unsigned int CBaseVMPIFileSystem::Tell( FileHandle_t file )
{
	return ((IVMPIFile*)file)->Tell();
}

unsigned int CBaseVMPIFileSystem::Size( FileHandle_t file )
{
	return ((IVMPIFile*)file)->Size();
}

unsigned int CBaseVMPIFileSystem::Size( const char *pFilename, const char *pathID = 0 )
{
	FileHandle_t hFile = Open( pFilename, "rb", NULL );
	if ( hFile == FILESYSTEM_INVALID_HANDLE )
	{
		return 0;
	}
	else
	{
		unsigned int ret = Size( hFile );
		Close( hFile );
		return ret;
	}
}

bool CBaseVMPIFileSystem::FileExists( const char *pFileName, const char *pPathID )
{
	FileHandle_t hFile = Open( pFileName, "rb", NULL );
	if ( hFile )
	{
		Close( hFile );
		return true;
	}
	else
	{
		return false;
	}
}

void CBaseVMPIFileSystem::Flush( FileHandle_t file )
{
	((IVMPIFile*)file)->Flush();
}	

bool CBaseVMPIFileSystem::Precache( const char* pFileName, const char *pPathID )
{
	return false;
} 


//-----------------------------------------------------------------------------
// NOTE: This is an exact copy of code in BaseFileSystem.cpp which
// has to be here because they want to call
// the implementation of Open/Size/Read/Write in CBaseVMPIFileSystem
//-----------------------------------------------------------------------------
bool CBaseVMPIFileSystem::ReadFile( const char *pFileName, const char *pPath, CUtlBuffer &buf, int nMaxBytes, int nStartingByte, FSAllocFunc_t pfnAlloc )
{
	const char *pReadFlags = "rb";
	if ( buf.IsText() && !buf.ContainsCRLF() )
	{
		pReadFlags = "rt";
	}

	FileHandle_t fp = Open( pFileName, buf.IsText() ? "rt" : "rb", pPath );
	if ( !fp )
		return false;

	int nBytesToRead = Size( fp );
	if ( nMaxBytes > 0 )
	{
		nBytesToRead = min( nMaxBytes, nBytesToRead );
	}
	buf.EnsureCapacity( nBytesToRead + buf.TellPut() );

	if ( nStartingByte != 0 )
	{
		Seek( fp, nStartingByte, FILESYSTEM_SEEK_HEAD );
	}

	int nBytesRead = Read( buf.PeekPut(), nBytesToRead, fp );
	buf.SeekPut( CUtlBuffer::SEEK_CURRENT, nBytesRead );

	Close( fp );
	return (nBytesRead != 0);
}

bool CBaseVMPIFileSystem::WriteFile( const char *pFileName, const char *pPath, CUtlBuffer &buf )
{
	const char *pWriteFlags = "wb";
	if ( buf.IsText() && !buf.ContainsCRLF() )
	{
		pWriteFlags = "wt";
	}

	FileHandle_t fp = Open( pFileName, buf.IsText() ? "wt" : "wb", pPath );
	if ( !fp )
		return false;

	int nBytesWritten = Write( buf.Base(), buf.TellPut(), fp );

	Close( fp );
	return (nBytesWritten != 0);
}