/**********************************************************************
 *
 * FileSystem.cpp
 * Copyright (C) 2003  UNESCO
 *
 * A component of the Greenstone digital library software
 * from the New Zealand Digital Library Project at the
 * University of Waikato, New Zealand.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *********************************************************************/

////////////////////////////////////////////////////////////////////////////////////
//
// FileSystem -- The generic file pointer routines are a collection of member
//               functions used to wrap  the underlying file system.


#include "errno.h"
#include "stdafx.h"
#include "FileSystem.h"


using namespace FileSystem;
//---------------------------------------------------------------------
// FileSystem::FPTR *Create(const _TCHAR *fname)
//
// Create a new file and truncate existing files.
// Returns a null value if the file cannot be
// created.
//---------------------------------------------------------------------
DLL_CODE_API FileSystem::FPTR *FileSystem::Create(const char *fname)
{
#if defined(__64_BIT_FILE_SYSTEM__)
	return FS64Create(fname);
	
#elif defined(__WIN32__) && defined(__NTFS__)
	FPTR *stream = new FPTR;
	if (!stream)
		return 0;
	stream->fptr = CreateFile(fname, 
		GENERIC_READ | GENERIC_WRITE, 
		FILE_SHARE_READ | FILE_SHARE_WRITE, 
		NULL, // No security
		CREATE_ALWAYS, 
		FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
		NULL // No attr. template 
		);
	
	if (stream->fptr == INVALID_HANDLE_VALUE)
	{
		delete stream;
		return 0;
	}
	return stream;
	
#else // Use the 32-bit stdio version by default
	FPTR *stream = new FPTR;
	if (!stream)
		return 0;
	stream->fptr = fopen(fname, "w+b");
	if (stream->fptr == 0)
	{
		delete stream; // 01/30/2002: Prevent memory leak
		return 0;
	}
	return stream;
#endif
}

//---------------------------------------------------------------------
// FileSystem::FPTR *Open(const _TCHAR *fname, FileSystemAccessMode mode)
//
// Open an existing file. The "mode" variable determines if the file
// is opened for read only or read/write access. Returns a null value
// if the specified file cannot be opened. NOTE: This version of the
// open functions will only accept: FILE_READONLY and FILE_READWRITE
// access modes.
//---------------------------------------------------------------------
DLL_CODE_API FileSystem::FPTR *FileSystem::Open(const char *fname, FileSystem::AccessMode mode)
{
#if defined(__64_BIT_FILE_SYSTEM__)
	return FSOpen(fname, mode);
	
#elif defined(__WIN32__) && defined(__NTFS__)
	FPTR *stream = new FPTR;
	if (!stream)
		return 0;
	if (mode == FILE_READONLY)
	{
		// Open for read only access
		stream->fptr = CreateFile(fname, 
			GENERIC_READ,
			FILE_SHARE_READ | FILE_SHARE_WRITE, 
			NULL, // No security
			OPEN_EXISTING, 
			FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, 
			NULL // No attr. template 
			);
		if (stream->fptr == INVALID_HANDLE_VALUE)
		{
			delete stream;
			return 0;
		}
	}
	else 
	{ // Open for read/write access
		stream->fptr = CreateFile(fname, 
			GENERIC_READ | GENERIC_WRITE, 
			FILE_SHARE_READ | FILE_SHARE_WRITE, 
			NULL, // No security 
			OPEN_EXISTING, 
			FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, 
			NULL // No attr. template 
			);
		
		if (stream->fptr == INVALID_HANDLE_VALUE)
		{
			delete stream;
			return 0;
		}
	}
	return stream;
	
#else // Use the 32-bit stdio version by default
	FPTR *stream = new FPTR;
	if (!stream)
		return 0;
	
	if (mode == FILE_READONLY)
	{
		// Open for read only access
		stream->fptr = fopen(fname, "rb");
		if (stream->fptr == 0)
		{
			delete stream; // 01/30/2002: Prevent memory leak
			return 0;
		}
	}
	else 
	{ // Open for read/write access
		stream->fptr = fopen(fname, "r+b");
		if (stream->fptr == 0)
		{
			delete stream; // 01/30/2002: Prevent memory leak
			return 0;
		}
	}
	return stream;
#endif
}

//---------------------------------------------------------------------
// int Close(FileSystem::FPTR *stream)
//
// Close an open file. Returns a non-zero value to  indicate an error 
// condition or zero if successful.
//---------------------------------------------------------------------
DLL_CODE_API int FileSystem::Close(FPTR *stream)
{
	if (!stream)
		return 0; // The file stream is not open
	
#if defined(__64_BIT_FILE_SYSTEM__)
	return FS64Close(stream);
	
#elif defined(__WIN32__) && defined(__NTFS__)
	if (!CloseHandle(stream->fptr))
		return 1;
	delete stream;
	stream = 0;
	return 0;
	
#else // Use the 32-bit stdio version by default
	if (fclose(stream->fptr) != 0)
		return 1;
	delete stream;
	stream = 0; // 02/02/2002: Set pointer to zero after releasing it
	return 0;
#endif
}

//-----------------------------------------------------------------------------
// int Flush(FileSystem::FPTR *stream)
//
// Flush the any open disk buffers. Returns a non-zero value to indicate
// an error // condition or zero if successful. 
//
// ATTENTION: ****MS VC++*** YOU SHOULD LINK YOUR APPLICATION WITH COMMODE.OBJ 
// file. COMMODE.OBJ changes the global commit flag such that 
// calling fflush() or _flushall() commits the file buffer to disk.
// The Windows NT operating system provides the WIN32 API FlushFileBuffers().
// The _commit() function included with the Visual C++ 32-bit Edition 
// CRT calls FlushFileBuffers to write buffered data to disk. 
//-----------------------------------------------------------------------------
DLL_CODE_API int FileSystem::Flush(FPTR *stream)
{
	if (!stream)
		return 0; // The file stream is not open
	
#if defined(__64_BIT_FILE_SYSTEM__)
	return FS64Flush(stream);
	
#elif defined(__WIN32__) && defined(__NTFS__)
	if (!FlushFileBuffers(stream->fptr))
		return 1;
	return 0;
	
#else // Use the 32-bit stdio version by default
	int ret = fflush(stream->fptr);
	if (ret != 0)
	{
		TRACE("\nerrno=%d\n\n\n", errno);
		return 1;       // Error
	}
	else
		return 0;
#endif
}

//---------------------------------------------------------------------
// int Read(FileSystem::FPTR *stream, void *buf, isis::uint32_t bytes)
//
// Read a specified number of bytes from the current file position
// into a memory buffer. Returns the total number if bytes read from the
// file.
//---------------------------------------------------------------------
DLL_CODE_API isis::ulong32_t FileSystem::Read(FPTR *stream, void *buf, 
											  isis::ulong32_t bytes)
{
	if (!stream)
		return (isis::ulong32_t)0; // The file stream is not open
	
#if defined(__64_BIT_FILE_SYSTEM__)
	return FS64Read(stream, buf, bytes);
	
#elif defined(__WIN32__) && defined(__NTFS__)
	DWORD bytes_read; 
	if (!ReadFile(stream->fptr, (LPVOID)buf, (DWORD)bytes, &bytes_read, NULL))
	{
		return (isis::ulong32_t)0;
	}
	return (isis::ulong32_t)bytes_read;
	
#else // Use the 32-bit stdio version by default
	return (isis::ulong32_t)fread((unsigned char *)buf, 1, bytes, stream->fptr);
#endif
}
//---------------------------------------------------------------------
// int Write(FileSystem::FPTR *stream, const void *buf, isis::uint32_t bytes) 
//
// Write a specific number of bytes from a memory buffer to the
// current file position. Returns the total number if bytes written
// to the file.
//---------------------------------------------------------------------
DLL_CODE_API isis::ulong32_t FileSystem::Write(FPTR *stream, const void *buf, 
				                   isis::ulong32_t bytes) 
{
	if (!stream)
		return (isis::ulong32_t)0; // The file stream is not open
	
#if defined(__64_BIT_FILE_SYSTEM__)
	return FS64Write(stream, buf, bytes);
	
#elif defined(__WIN32__) && defined(__NTFS__)
	DWORD bytes_moved; 
	if (!WriteFile(stream->fptr, (LPVOID)buf, (DWORD)bytes, &bytes_moved, NULL))
	{
		return (isis::ulong32_t)0;
	}
	return (isis::ulong32_t)bytes_moved;
	
#else // Use the 32-bit stdio version by default
	return (isis::ulong32_t)fwrite((const unsigned char *)buf, 1, bytes, stream->fptr);
#endif
}
//---------------------------------------------------------------------------
// isis::int32_t Seek(FileSystem::FPTR *stream, isis::int32_t offset,  FileSystemSeekMode mode)
//
// Seek to the specified offset starting at the beginning (SEEK_SET),
// end (SEEK_END) or current offset (SEEK_CUR). Returns a negative 1
// to indicate an error condition or the current stream position if
// successful.
//----------------------------------------------------------------------------
DLL_CODE_API FAU_t FileSystem::Seek(FPTR *stream, FAU_t offset, FileSystem::SeekMode mode)
{
	if (!stream)
		return (FAU_t) - 1; // The file stream is not open
	
#if defined(__64_BIT_FILE_SYSTEM__)
	return FS64Seek(stream, offset, mode);
	
#elif defined(__WIN32__) && defined(__NTFS__)
	if (mode == FILE_SEEK_BEG)
	{
		return (FAU_t)SetFilePointer(stream->fptr, offset, NULL, FILE_BEGIN);
	}
	else if (mode == FILE_SEEK_CUR)
	{
		return (FAU_t)SetFilePointer(stream->fptr, offset, NULL, FILE_CURRENT);
	}
	else if (mode == FILE_SEEK_END)
	{
		return (FAU_t)SetFilePointer(stream->fptr, offset, NULL, FILE_END);
	}
	else 
	{ // An invalid seek mode was specified
		return (FAU_t) - 1;
	}
	
#else // Use the 32-bit stdio version by default
	if (mode == FILE_SEEK_BEG)
	{
		return fseek(stream->fptr, offset, SEEK_SET);
	}
	else if (mode == FILE_SEEK_CUR)
	{
		return fseek(stream->fptr, offset, SEEK_CUR);
	}
	else if (mode == FILE_SEEK_END)
	{
		return fseek(stream->fptr, offset, SEEK_CUR);
	}
	else 
	{ // An invalid seek mode was specified
		return (FAU_t) - 1;
	}
#endif
}
//----------------------------------------------------------------------------
// isis::int32_t Tell(FileSystem::FPTR *stream)
//
//----------------------------------------------------------------------------
DLL_CODE_API FAU_t FileSystem::Tell(FPTR *stream)
{
	if (!stream)
		return (FAU_t) - 1; // The file stream is not open
	
#if defined(__64_BIT_FILE_SYSTEM__)
	return FS64Tell(stream);
	
#elif defined(__WIN32__) && defined(__NTFS__)
	return (__LWORD__)SetFilePointer(stream->fptr, 0, NULL, FILE_CURRENT);
	
#else // Use the 32-bit stdio version by default
	return ftell(stream->fptr);
#endif
}

//----------------------------------------------------------------------------
// int Exists(const char *fname)
//
// Returns true if the specified file exists.
//----------------------------------------------------------------------------
DLL_CODE_API int FileSystem::Exists(const char *fname)
// Returns true if the specified file exists.
{
#if defined(__64_BIT_FILE_SYSTEM__)
	return FS64Exists(fname);
	
#elif defined(__WIN32__) && defined(__NTFS__)
	HANDLE hFile = CreateFile(fname, 
		GENERIC_READ,
		FILE_SHARE_READ | FILE_SHARE_WRITE, 
		NULL, // No security
		OPEN_EXISTING, 
		FILE_ATTRIBUTE_NORMAL, 
		NULL // No attr. template
		);
	
	if (hFile == INVALID_HANDLE_VALUE)
	{
		return 0;
	}
	
	CloseHandle(hFile);
	return 1;
	
#else // Use the 32-bit stdio version by default
	struct stat buf;
	return stat(fname, &buf) ==(FAU_t)0;
#endif
}



//----------------------------------------------------------------------------
// FAU_t FileSize(const _TCHAR *fname)
//
// Returns the length of the specified file or negative 1 to indicate an
// error condition.
//----------------------------------------------------------------------------
DLL_CODE_API FAU_t FileSystem::FileSize(const char *fname)
{
#if defined(__64_BIT_FILE_SYSTEM__)
	return FS64FileSize(fname);
	
#elif defined(__WIN32__) && defined(__NTFS__)
	HANDLE hFile = CreateFile(fname, 
		GENERIC_READ,
		FILE_SHARE_READ | FILE_SHARE_WRITE, 
		NULL, // No security
		OPEN_EXISTING, 
		FILE_ATTRIBUTE_NORMAL, 
		NULL // No attr. template 
		);
	
	// Could not open the specified file
	if (hFile == INVALID_HANDLE_VALUE)
	{
		return (FAU_t) - 1;
	}
	
	BY_HANDLE_FILE_INFORMATION info;
	
	if (GetFileInformationByHandle(hFile, &info) == 0)
	{
		return (FAU_t) - 1;
	}
	CloseHandle(hFile);
	return (FAU_t)info.nFileSizeLow;;
	
#else // Use the 32-bit stdio version by default
	struct stat buf;
	int result = stat(fname, &buf);
	if (result != 0)
		return -1;
	return buf.st_size;
#endif
}

//  #include <io.h>
//  #include <share.h>


//  int FileSystem::CanOpenForWriting(const _TCHAR *fname)
//  {
//  	// Try to open for writing
//  	int fd = _tsopen(fname, _O_WRONLY | O_BINARY, _SH_DENYWR);
//  	if (fd == -1)
//  		return 0;
//  	_close(fd);
//  	return 1;
//  }

//  int FileSystem::CanOpenReadOnly(const _TCHAR *fname)
//  {
//  	// Try to open for read only
//  	int fd = _tsopen(fname, _O_RDONLY | O_BINARY, _SH_DENYNO);
//  	if (fd == -1)
//  		return 0;
//  	_close(fd);
//  	return 1;
//  }

//----------------------------------------------------------------------------
// int IsReadOnly(const _TCHAR* fname)
//
// Returns true if the specified file is read only.
//----------------------------------------------------------------------------
//  int FileSystem::IsReadOnly(const _TCHAR *fname)
//  {
//  	struct _stat buf;
//  	_tstat(fname, &buf);
//  	if (buf.st_mode & S_IWRITE)
//  		return 0;
//  	return 1;
//  }

