//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A higher level link library for general use in the game and tools.
//
//===========================================================================//


#ifndef FILEUTILS_H
#define FILEUTILS_H

#if defined( _WIN32 )
#pragma once
#endif

#ifndef TIER2_H
#include "tier2/tier2.h"
#endif

#ifndef FILESYSTEM_H
#include "filesystem.h"
#endif

#include "tier0/platform.h"

// Builds a directory which is a subdirectory of the current mod
void GetModSubdirectory( const char *pSubDir, char *pBuf, int nBufLen );

// Builds a directory which is a subdirectory of the current mod's *content*
void GetModContentSubdirectory( const char *pSubDir, char *pBuf, int nBufLen );

// Generates a filename under the 'game' subdirectory given a subdirectory of 'content'
void ComputeModFilename( const char *pContentFileName, char *pBuf, size_t nBufLen );

// Generates a filename under the 'content' subdirectory given a subdirectory of 'game'
void ComputeModContentFilename( const char *pGameFileName, char *pBuf, size_t nBufLen );

// Builds a list of all files under a directory with a particular extension
void AddFilesToList( CUtlVector< CUtlString > &list, const char *pDirectory, const char *pPath, const char *pExtension );

// Returns the search path as a list of paths
void GetSearchPath( CUtlVector< CUtlString > &path, const char *pPathID );

// Given file name generate a full path using the following rules.
// 1. if its full path already return
// 2. if its a relative path try to find it under the path id
// 3. if find fails treat relative path as relative to the current dir
bool GenerateFullPath( const char *pFileName, char const *pPathID, char *pBuf, int nBufLen );


// Generates a .360 file if it doesn't exist or is out of sync with the pc source file
#define UOC_FAIL		-1
#define UOC_NOT_CREATED	0
#define UOC_CREATED		1
typedef bool ( *CreateCallback_t )( const char *pSourceName, const char *pTargetName, const char *pPathID, void *pExtraData );
int UpdateOrCreate( const char *pSourceName, char *pTargetName, int targetLen, const char *pPathID, CreateCallback_t pfnCreate, bool bForce = false, void *pExtraData = NULL );

char *CreateX360Filename( const char *pSourceName, char *pTargetName, int targetLen );

FORCEINLINE const char *AdjustFileExtensionForPlatform( const char *pSourceName, char *pTargetName, int targetLen )
{
#ifdef PLATFORM_X360
	return CreateX360Filename( pSourceName, pTargetName, targetLen );
#else
	return pSourceName;
#endif
}

// simple file classes. File I/O mode (text/binary, read/write) is based upon the subclass chosen.
// classes with the word Required on them abort with a message if the file can't be opened.
// destructores close the file handle, or it can be explicitly closed with the Close() method.

class CBaseFile
{
public:
	FileHandle_t m_FileHandle;

	CBaseFile(void)
	{
		m_FileHandle = FILESYSTEM_INVALID_HANDLE;
	}

	~CBaseFile( void )
	{
		Close();
	}

	FileHandle_t Handle( void ) const
	{
		return m_FileHandle;
	}

	void Close( void )
	{
		if ( m_FileHandle != FILESYSTEM_INVALID_HANDLE )
			g_pFullFileSystem->Close( m_FileHandle );
		m_FileHandle = FILESYSTEM_INVALID_HANDLE;
	}

	void Open( char const *fname, char const *modes )
	{
		Close();
		m_FileHandle = g_pFullFileSystem->Open( fname, modes );
	}

	char *ReadLine( char *pOutput, int maxChars )
	{
		return g_pFullFileSystem->ReadLine( pOutput, maxChars, m_FileHandle );
	}

	// read every line of the file into a vector of strings
	void ReadLines( CUtlStringList &sList, int nMaxLineLength = 2048 );

	int Read( void* pOutput, int size )
	{
		return g_pFullFileSystem->Read( pOutput, size, m_FileHandle );
	}

	void MustRead( void* pOutput, int size )
	{
		int ret=Read( pOutput, size );
		if (ret != size )
			Error("failed to read %d bytes\n", size );
	}
	
	int Write( void const* pInput, int size)
	{
		return g_pFullFileSystem->Write( pInput, size, m_FileHandle );
	}


	// {Get|Put}{Int|Float} read and write ints and floats from a file in x86 order, swapping on
	// input for big-endian systems.
	void PutInt( int n )
	{
		int n1=LittleDWord( n );
		Write(&n1, sizeof( n1 ) );
	}

	int GetInt( void )
	{
		int ret;
		MustRead( &ret, sizeof( ret ));
		return LittleDWord( ret );
	}

	float GetFloat( void )
	{
		float ret;
		MustRead( &ret, sizeof( ret ));
		LittleFloat( &ret, &ret );
		return ret;
	}
	void PutFloat( float f )
	{
		LittleFloat( &f, &f );
		Write( &f, sizeof( f ) );
	}

	bool IsOk( void )
	{
		return ( m_FileHandle != FILESYSTEM_INVALID_HANDLE) &&
			( g_pFullFileSystem->IsOk( m_FileHandle ) );
	}

	void Seek( int pos, FileSystemSeek_t nSeekType = FILESYSTEM_SEEK_HEAD )
	{
		g_pFullFileSystem->Seek( m_FileHandle, pos, nSeekType );
	}

	unsigned int Tell()
	{
		return g_pFullFileSystem->Tell( m_FileHandle );
	}

	unsigned int Size( void )
	{
		Assert( IsOk() );
		return g_pFullFileSystem->Size( m_FileHandle );
	}

	void ReadFile( CUtlBuffer &dataBuf );
};

class COutputFile : public CBaseFile
{
public:
	void Open( char const *pFname )
	{
		CBaseFile::Open( pFname, "wb" );
	}

	COutputFile( char const *pFname ) : CBaseFile()
	{
		Open( pFname );
	}

	COutputFile( void ) : CBaseFile()
	{
	}
};

class COutputTextFile : public CBaseFile
{
public:
	void Open( char const *pFname )
	{
		CBaseFile::Open( pFname, "w" );
	}

	COutputTextFile( char const *pFname ) : CBaseFile()
	{
		Open( pFname );
	}

	COutputTextFile( void ) : CBaseFile()
	{
	}
};

class CAppendTextFile : public CBaseFile
{
public:
	void Open( char const *pFname )
	{
		CBaseFile::Open( pFname, "a+" );
	}

	CAppendTextFile( char const *pFname ) : CBaseFile()
	{
		Open( pFname );
	}

	CAppendTextFile( void ) : CBaseFile()
	{
	}
};

class CInputFile : public CBaseFile
{
public:
	void Open( char const *pFname )
	{
		CBaseFile::Open( pFname, "rb" );
	}

	CInputFile( char const *pFname ) : CBaseFile()
	{
		Open( pFname );
	}
	CInputFile( void ) : CBaseFile()
	{
	}
};

class CInputTextFile : public CBaseFile
{
public:
	void Open( char const *pFname )
	{
		CBaseFile::Open( pFname, "r" );
	}

	CInputTextFile( char const *pFname ) : CBaseFile()
	{
		Open( pFname );
	}
	CInputTextFile( void ) : CBaseFile()
	{
	}
	

};

class CRequiredInputTextFile : public CBaseFile
{
public:
	void Open( char const *pFname )
	{
		CBaseFile::Open( pFname, "r" );
		if ( ! IsOk() )
		{
			Error("error opening required file %s\n", pFname );
		}
	}

	CRequiredInputTextFile( char const *pFname ) : CBaseFile()
	{
		Open( pFname );
	}
	CRequiredInputTextFile( void ) : CBaseFile()
	{
	}
};

class CRequiredInputFile : public CBaseFile
{
public:
	void Open( char const *pFname )
	{
		CBaseFile::Open( pFname, "rb" );
		if ( ! IsOk() )
		{
			Error("error opening required file %s\n", pFname );
		}
	}

	CRequiredInputFile( char const *pFname ) : CBaseFile()
	{
		Open( pFname );
	}
	CRequiredInputFile( void ) : CBaseFile()
	{
	}
};

#endif // FILEUTILS_H