/* ================================================================================== */
/* Copyright (c) 1998-1999 3Com Corporation or its subsidiaries. All rights reserved. */
/* ================================================================================== */

#include "EmulatorCommon.h"

#include <sys/stat.h>

#include "Platform_Files.h"
#include "ErrorHandling.h"		// this->Throw


// ===========================================================================
//		¥ FileReference
//
//		A FileReference is a lightweight reference to a file.  It can be
//		used to open files, and can be saved (for example, in a preferences
//		file) for later.
// ===========================================================================

/***********************************************************************
 *
 * FUNCTION:	FileReference::FileReference
 *
 * DESCRIPTION:	FileReference constructor.  Initializes the sole data
 *				member (a string to hold the file path) to be empty.
 *
 * PARAMETERS:	none.
 *
 * RETURNED:	nothing.
 *
 ***********************************************************************/

FileReference::FileReference(void) :
	fFilePath()
{
}


/***********************************************************************
 *
 * FUNCTION:	FileReference::FileReference
 *
 * DESCRIPTION:	FileReference copy constructor.
 *
 * PARAMETERS:	other - FileReference to clone.
 *
 * RETURNED:	nothing.
 *
 ***********************************************************************/

FileReference::FileReference(const FileReference& other) :
	fFilePath(other.fFilePath)
{
}


/***********************************************************************
 *
 * FUNCTION:	FileReference::FileReference
 *
 * DESCRIPTION:	FileReference constructor.  Creates a file reference to
 *				the file with the given path.
 *
 * PARAMETERS:	path - Full path to the file.
 *
 * RETURNED:	nothing.
 *
 ***********************************************************************/

FileReference::FileReference(const char* path) :
	fFilePath(path)
{
}


/***********************************************************************
 *
 * FUNCTION:	FileReference::FileReference
 *
 * DESCRIPTION:	FileReference constructor.  Creates a file reference to
 *				the file with the given path.
 *
 * PARAMETERS:	path - Full path to the file.
 *
 * RETURNED:	nothing.
 *
 ***********************************************************************/

FileReference::FileReference(const string& path) :
	fFilePath(path)
{
}


/***********************************************************************
 *
 * FUNCTION:	FileReference::FileReference
 *
 * DESCRIPTION:	FileReference constructor.  Creates a file reference to
 *				the file with the given path.
 *
 * PARAMETERS:	path - Full path to the file.
 *
 * RETURNED:	nothing.
 *
 ***********************************************************************/

FileReference::FileReference(const unsigned char*)
{
	// Shouldn't be calling this one on Unix...this is for the Mac
	// where Pascal strings are passed as unsigned char*'s.
}


/***********************************************************************
 *
 * FUNCTION:	FileReference::FileReference
 *
 * DESCRIPTION:	FileReference destructor.  Nothing special to do...
 *
 * PARAMETERS:	none.
 *
 * RETURNED:	nothing.
 *
 ***********************************************************************/

FileReference::~FileReference(void)
{
}


/***********************************************************************
 *
 * FUNCTION:	FileReference::operator=
 *
 * DESCRIPTION:	Assignment operator.  If "other" is not the same as
 *				the controlled object, copy the contents.
 *
 * PARAMETERS:	other - object to copy.
 *
 * RETURNED:	reference to self.
 *
 ***********************************************************************/

FileReference&
FileReference::operator=(const FileReference& other)
{
	if (&other != this) {
		fFilePath = other.fFilePath;
	}

	return *this;
}


/***********************************************************************
 *
 * FUNCTION:	FileReference::IsSpecified
 *
 * DESCRIPTION:	Returns whether or not the controlled object has been
 *				pointed to a (possibly non-existant) file, or if it's
 *				empty (that it, it was created with the default ctor).
 *
 * PARAMETERS:	none.
 *
 * RETURNED:	True if the object points to a file.
 *
 ***********************************************************************/

Bool
FileReference::IsSpecified(void) const
{
	return !fFilePath.empty();
}


/***********************************************************************
 *
 * FUNCTION:	FileReference::Exists
 *
 * DESCRIPTION:	Returns whether or not the controlled object points to
 *				an existing file.
 *
 * PARAMETERS:	none.
 *
 * RETURNED:	True if the referenced file exists.
 *
 ***********************************************************************/

Bool
FileReference::Exists(void) const
{
	struct stat buf;
	return 0 == stat(fFilePath.c_str(), &buf);
}


/***********************************************************************
 *
 * FUNCTION:	FileReference::GetFileName
 *
 * DESCRIPTION:	Returns the name of the referenced file.  Only the file
 *				*name* is returned, not the full path.
 *
 * PARAMETERS:	none.
 *
 * RETURNED:	A string containing the name.  If the file is not
 *				specified, an empty string is returned.  No checks are
 *				made to see if the file actually exists.
 *
 ***********************************************************************/

string
FileReference::GetFileName(void) const
{
	const char *slash = strrchr(fFilePath.c_str(), '/');
	if (slash) {
		return string(slash + 1);
	}
	return fFilePath;
}


/***********************************************************************
 *
 * FUNCTION:	FileReference::operator==
 * FUNCTION:	FileReference::operator!=
 * FUNCTION:	FileReference::operator>
 * FUNCTION:	FileReference::operator<
 *
 * DESCRIPTION:	Bogus operators for wiggy VC++ compiler which won't let
 *				us instantiate STL containers without them.
 *
 * PARAMETERS:	other - object to compare ourself to.
 *
 * RETURNED:	True if the requested condition is true.  Comparisons
 *				are based on the file's full path.
 *
 ***********************************************************************/

bool
FileReference::operator==(const FileReference& other) const
{
	return strcmp(fFilePath.c_str(), other.fFilePath.c_str()) == 0;
}


bool
FileReference::operator!=(const FileReference& other) const
{
	return strcmp(fFilePath.c_str(), other.fFilePath.c_str()) != 0;
}


bool
FileReference::operator>(const FileReference& other) const
{
	return strcmp(fFilePath.c_str(), other.fFilePath.c_str()) < 0;
}


bool
FileReference::operator<(const FileReference& other) const
{
	return strcmp(fFilePath.c_str(), other.fFilePath.c_str()) > 0;
}


/***********************************************************************
 *
 * FUNCTION:	FileReference::GetFilePath
 *
 * DESCRIPTION:	Returns the full path of the referenced file.
 *
 * PARAMETERS:	none.
 *
 * RETURNED:	The full path of the referenced file.  If the file is
 *				not specified, an empty string is returned.  No check
 *				made to see if the file actually exists.
 *
 *				Note that a pointer to actual object data is returned!
 *				The path is NOT returned in a new string object.
 *
 ***********************************************************************/

const char*
FileReference::GetFilePath(void) const
{
	return fFilePath.c_str();
}


// ===========================================================================
//		¥ FileHandle
//
//		A FileHandle is a lightweight reference to an open file.  The class's
//		constructor attempts to open (or create) the file based on the input
//		parameters.  The class's destructor closes the file.
//
//		Once a file is open, member functions can be used to operate on the
//		file (read from it, write to it, etc.).
//
//		FileHandles can be copied, but no reference counting is performed.
//		Thus, after the first FileHandle is deleted, copies of it will
//		contain invalid file references.
//		
// ===========================================================================

/***********************************************************************
 *
 * FUNCTION:	FileHandle::FileHandle
 *
 * DESCRIPTION:	FileHandle constructor.  Opens and/or creates the
 *				file according to the input parameters.
 *
 * PARAMETERS:	ref - reference to the file to create/open.
 *
 *				openMode - flags describing how to open/create the file
 *
 *				creator - creator value to assign to the file if it's
 *					created (only used on Mac).
 *
 *				fileType - file type value to assign to the file if it's
 *					created (only used on the Mac).
 *
 * RETURNED:	nothing.
 *
 *				If the operation fails, an exception is thrown.
 *
 ***********************************************************************/

FileHandle::FileHandle(const FileReference& ref,
								 long openMode,
								 uae_u32 creator,
								 uae_u32 fileType) :
	fStream(NULL)
{
	this->Open(ref, openMode, creator, fileType);
}


/***********************************************************************
 *
 * FUNCTION:	FileHandle::~FileHandle
 *
 * DESCRIPTION:	FileHandle destructor.  Closes the file.
 *
 * PARAMETERS:	none.
 *
 * RETURNED:	nothing.
 *
 *				If the operation fails, an exception is thrown.
 *
 ***********************************************************************/

FileHandle::~FileHandle(void)
{
	this->Close();
}


/***********************************************************************
 *
 * FUNCTION:	FileHandle::GetReference
 *
 * DESCRIPTION:	Returns the FileReference used to create/open the file.
 *
 * PARAMETERS:	none.
 *
 * RETURNED:	The file reference.
 *
 ***********************************************************************/

FileReference	
FileHandle::GetReference(void)
{
	return fFileRef;
}


/***********************************************************************
 *
 * FUNCTION:	FileHandle::IsOpen
 *
 * DESCRIPTION:	Returns whether or not the file is open.
 *
 * PARAMETERS:	none.
 *
 * RETURNED:	TRUE if the file is open, FALSE otherwise.
 *
 ***********************************************************************/

Bool
FileHandle::IsOpen(void)
{
	return fStream != NULL;
}


/***********************************************************************
 *
 * FUNCTION:	FileHandle::SetPos
 *
 * DESCRIPTION:	Set the read/write position within the file.
 *
 * PARAMETERS:	offset - stdio-style offset value.
 *
 *				seekMode - stdio-style mode value.
 *
 * RETURNED:	nothing.
 *
 *				If the operation fails, an exception is thrown.
 *
 ***********************************************************************/

void			
FileHandle::SetPos(long offset, SeekMode seekMode)
{
	if (fStream == NULL) {
		this->Throw(EBADF);
	}

	int whence;

	if (seekMode == kSeekSet) {
		whence = SEEK_SET;
	}
	else if (seekMode == kSeekEnd) {
		whence = SEEK_END;
	}
	else
	{
		whence = SEEK_CUR;
	}

	int err = fseek(fStream, offset, whence);
	if (err) {
		this->Throw(errno);
	}
}


/***********************************************************************
 *
 * FUNCTION:	FileHandle::GetPos
 *
 * DESCRIPTION:	Return the current read/write file position.
 *
 * PARAMETERS:	none.
 *
 * RETURNED:	The current stdio-style position.
 *
 *				If the operation fails, an exception is thrown.
 *
 ***********************************************************************/

uae_u32			
FileHandle::GetPos(void)
{
	if (fStream == NULL) {
		this->Throw(EBADF);
	}

	return ftell(fStream);
}


/***********************************************************************
 *
 * FUNCTION:	FileHandle::SetLength
 *
 * DESCRIPTION:	Set the length of the file.
 *
 * PARAMETERS:	length - the desired file length.
 *
 * RETURNED:	nothing.
 *
 *				If the operation fails, an exception is thrown.
 *
 ***********************************************************************/

void			
FileHandle::SetLength(uae_u32 length)
{
	// !!! Use BOOL SetEndOfFile (fHandle);
}


/***********************************************************************
 *
 * FUNCTION:	FileHandle::GetLength
 *
 * DESCRIPTION:	Returns the length of the file.
 *
 * PARAMETERS:	none.
 *
 * RETURNED:	The length of the file.
 *
 *				If the operation fails, an exception is thrown.
 *
 ***********************************************************************/

uae_u32			
FileHandle::GetLength(void)
{
	if (fStream == NULL) {
		this->Throw(EBADF);
	}

	long cur = ftell(fStream);
	fseek(fStream, 0, SEEK_END);
	uae_u32	length = ftell(fStream);
	fseek(fStream, cur, SEEK_SET);

	return length;
}


/***********************************************************************
 *
 * FUNCTION:	FileHandle::Read
 *
 * DESCRIPTION:	Read data from the file.
 *
 * PARAMETERS:	length - amount of data to read.
 *
 *				buffer - buffer into which the data is place.
 *
 * RETURNED:	nothing.
 *
 *				If the operation fails, an exception is thrown.
 *
 ***********************************************************************/

void			
FileHandle::Read(uae_u32 length, void* buffer)
{
	if (fStream == NULL) {
		this->Throw(EBADF);
	}
	if (!buffer) {
		this->Throw(EINVAL);
	}

	fread(buffer, 1, length, fStream);
	if (ferror(fStream)) {
		this->Throw(errno);
	}
}


/***********************************************************************
 *
 * FUNCTION:	FileHandle::Write
 *
 * DESCRIPTION:	Write data to the file.
 *
 * PARAMETERS:	length - amount of data to write.
 *
 *				buffer - buffer from which data is retrieved
 *
 * RETURNED:	nothing.
 *
 *				If the operation fails, an exception is thrown.
 *
 ***********************************************************************/

void			
FileHandle::Write(uae_u32 length, const void* buffer)
{
	if (fStream == NULL) {
		this->Throw(EBADF);
	}
	if (!buffer) {
		this->Throw(EINVAL);
	}

	fwrite(buffer, 1, length, fStream);
	if (ferror(fStream)) {
		this->Throw(errno);
	}
}


/***********************************************************************
 *
 * FUNCTION:	FileHandle::Open [protected]
 *
 * DESCRIPTION:	Opens and/or creates the given file based on the given
 *				parameters.  This function is called from the ctor in
 *				order to do all the work.
 *
 * PARAMETERS:	ref - reference to the file to create/open.
 *
 *				openMode - flags describing how to open/create the file
 *
 *				creator - creator value to assign to the file if it's
 *					created (only used on Mac).
 *
 *				fileType - file type value to assign to the file if it's
 *					created (only used on the Mac).
 *
 * RETURNED:	nothing.
 *
 *				If the operation fails, an exception is thrown.
 *
 ***********************************************************************/

void			
FileHandle::Open(const FileReference& ref,
								 long openMode,
								 uae_u32 creator,
								 uae_u32 fileType)
{
	(void)creator;
	(void)fileType;
	
	// Determine how the file should be opened (open existing, create new, etc.)
	// Currently we only support:
	//		kOpenExisting | kOpenRead
	//		kCreateAlways | kOpenReadWrite

	const char *mode;

	switch (openMode & kOpenTypeMask) {
		case kOpenExisting:		mode = "rb";		break;
		case kCreateAlways:		mode = "w+b";		break;
		default:
		{
			assert(0);
			this->Throw(EINVAL);
		}
	}

	// Open/create the file.
	
	fStream = fopen(ref.GetFilePath(), mode);

	// Check for errors.

	if (fStream == NULL) {
		this->Throw(errno);
	}
}


/***********************************************************************
 *
 * FUNCTION:	FileHandle::Close [protected]
 *
 * DESCRIPTION:	Closes the file.  Called from the dtor to do all the
 *				work.
 *
 * PARAMETERS:	none.
 *
 * RETURNED:	nothing.
 *
 *				If the operation fails, an exception is thrown.
 *
 ***********************************************************************/

void			
FileHandle::Close(void)
{
	if (fStream == NULL) {
		this->Throw(EINVAL);
	}

	if (fclose(fStream)) {
		fStream = NULL;
		this->Throw(errno);
	}

	fStream = NULL;
}


/***********************************************************************
 *
 * FUNCTION:	FileHandle::Throw [protected]
 *
 * DESCRIPTION:	Bottleneck function for throwing an exception.  Makes
 *				sure that the file's name is installed as an error
 *				message parameter and then throws the exception.
 *
 * PARAMETERS:	err - ErrCode to throw.
 *
 * RETURNED:	never.
 *
 ***********************************************************************/

void			
FileHandle::Throw(ErrCode err)
{
	this->SetFileNameParameter();

	Errors::Throw(err);
}


/***********************************************************************
 *
 * FUNCTION:	FileHandle::SetFileNameParameter [protected]
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	none.
 *
 * RETURNED:	nothing.
 *
 ***********************************************************************/

void			
FileHandle::SetFileNameParameter(void)
{
	string	name = this->GetReference().GetFileName();
	Errors::SetParameter("%filename", name);
}

