/******************************************************************************
 * $Id: gfx_file.cc,v 1.23 2006/08/01 23:14:02 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	File functions.
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 ******************************************************************************/

#define GFX_FILE_CC 1
#include "gfx.hh"
#include "gfx_file.hh"
#include "gfx_misc.hh"
#include "gfx_shared_ptr.hh"
#include <sys/types.h>
#include <sys/stat.h>
#if GFX_ZLIB
#include <zlib.h>
#endif
namespace gfx {

/*****************************************************************************
 * @return Size of an opened file or -1 if error.
 *****************************************************************************/
long
FileSize( FILE* file )
{
	int fd = fileno( file );		// fileno() shouldn't ever fail
	struct stat s;
	if ( fstat( fd, &s ) == 0 )
		return s.st_size;
	else
		return -1;
}

/*****************************************************************************
 * Read a file into a C++ string.
 *****************************************************************************/
bool
ReadFile( const string& fname, string& obuf )
{
	obuf.erase();												// clear buffer
	FILE* file = fopen( fname.c_str(), "rb" );
	if ( file )
	{
		long fileSize = FileSize( file );						// returns -1 if error
		if ( fileSize > 0 )
		{
			char* buf = new char[ fileSize + 10 ];
			uint bytesRead = fread( buf, 1, fileSize, file );	// should return 0..fileSize
			obuf.assign( buf, bytesRead );						// assign bytes actually read
			delete[] buf;
		}
		fclose( file );
	}
	return obuf.size() > 0;
}

/*****************************************************************************
 * Read a file into a StringBuf object.
 * A StringBuf can be used with SharedPtr.
 *****************************************************************************/
bool
ReadFile( const string& fname, StringBuf& strbuf )
{
	return ReadFile( fname, strbuf.mBuf );
}

/*****************************************************************************
 * Decompress a gzip-compressed file (.gz) using zlib functions into a C++ string.
 * @param	fname
 * @param	obuf
 *			Output buffer.
 * @param	chunkLen
 *			Default arg.  For speed, pass expected size of decompressed buffer.
 * @remarks gzread() is smart enough to function like fread() without returning
 * an error in case a file isn't really gzip-compressed.
 *****************************************************************************/
#if GFX_ZLIB
bool
ReadFileGzip( const string& fname, string& obuf, const uint chunkLen )
{
	obuf.erase();
	gzFile file = gzopen( fname.c_str(), "rb" );
	if ( file )
	{
		// Decompress gzip file, iteratively appending
		// a decompressed chunk to output string.
		char* chunk = new char[chunkLen+10];
		int bytes;
		while ( (bytes = gzread( file, chunk, chunkLen )) > 0 ) // returns 0=EOF -1=error
			obuf.append( chunk, bytes );	// append chunk
		gzclose( file );
		delete[] chunk;
	}
	return obuf.size() > 0;
}
#endif

/*****************************************************************************
 * Delete a file.
 * DeleteFile() is a Win32 function.
 *****************************************************************************/
bool
RemoveFile( const string& fname )
{
	return remove( fname.c_str() ) == 0;
}

/*****************************************************************************
 * Remove suffix from a pathname.
 *****************************************************************************/
void
RemoveSuffix( string& fname/*IN/OUT*/, const string& suffix )
{
	// string::size_type should be used when comparing against string::npos.
	string::size_type idx = fname.find( suffix, 0 );
	if ( idx != string::npos ) fname.erase( idx, suffix.size() );
}

/*****************************************************************************
 * @return True if a pathname is absolute.
 *****************************************************************************/
bool
IfAbsPath( const string& path )
{
#if DOSFS
	ASSERT( path.size() >= 2 );
	return path[1] == ':' || path[1] == '\\'; // "C:\FOO" or "\FOO"
#else
	ASSERT( path.size() >= 1 );
	return path[0] == '/';
#endif
}

/*****************************************************************************
 * Fills a STL vector with filenames from a directory.
 * @verbatim
 * Example of usage:
 *	vector<string> files;
 *	if ( FilesInDir( "/tmp", files ) )
 *	{
 *		vector<string>::iterator itr;
 *		for ( itr = files.begin(); itr != files.end(); ++itr )
 *			cout << *itr << "\n";
 *	}
 * @endverbatim
 *****************************************************************************/
bool
FilesInDir( const string& dirname, vector<string>& vec /*OUT*/ )
{
	///////////////////////////// WIN32 ////////////////////////////////////////
#if defined(_WIN32)
	WIN32_FIND_DATA findData;
	HANDLE hfind;

	// Need wildcard at end of string.
	// Create string that ends with "\*".
	string dir = Unix2DosDir( dirname );
	if ( dir[ dir.length()-1 ] != '\\' ) // add backsplash if absent
		dir += '\\';
	dir += '*'; // wildcard

	// Find first file in dir.
	hfind = FindFirstFile( dir.c_str(), &findData );
	if ( hfind == INVALID_HANDLE_VALUE ) return false;
	vec.push_back( string(findData.cFileName) );

	// Find remaining files in dir.
	while ( FindNextFile( hfind, &findData ) )
	{
		vec.push_back( string(findData.cFileName) );
	}

	FindClose( hfind );
	return true;

	///////////////////////////// POSIX ////////////////////////////////////////
#else
	vec.clear();

	// Open directory.
	DIR* dir = opendir( dirname.c_str() );
	if ( dir == NULL ) return false;

	// For each file in directory.
	for (;;)
	{
		// Read next filename.
		struct dirent* file = readdir( dir );
		const char* name = file->d_name;
		if ( file == NULL )
			break;

		// Exclude parent and current directories.
		if (    STRNE( name, "."  )
             && STRNE( name, ".." ) )
			vec.push_back( name );
	}

	closedir( dir );
	return true;
#endif
}

/*****************************************************************************
 * @return CWD as a C++ string.
 *****************************************************************************/
void
Getcwd( string& out )
{
	static char tmp[GFX_MAX_PATH];
	GETCWD( tmp, sizeof(tmp) );
	out = tmp;
}

/*****************************************************************************
 * UNIX to MSDOS directory name conversion.
 *****************************************************************************/
#if _WIN32
char
Unix2DosDirChar( char c )
{
	return c == '/' ? '\\': c;
}
string
Unix2DosDir( const string& dirname )
{
	string s = dirname;
	std::transform( s.begin(), s.end(),
                    s.begin(),
                    Unix2DosDirChar );
	return s;
}
#endif // WIN32

} // namespace gfx
