/***********************************************************************\
Common class for accessing text files in all Convert programs
Copyright (C) 1993 David John Burrowes

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 1, 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.

The author, David John Burrowes, can be reached at:
	davidjohn@kira.net.netcom.com
	David John Burrowes
	1926 Ivy #10
	San Mateo, CA 94403-1367
\***********************************************************************/

#import <streams/streams.h>
#import <stdio.h>
#import <string.h>	// for strlen
#import "TextFile.h"


//	We are assuming our superclass has already imported this.  If not, you'll get an
//	error below, indicating that this wont work (see  LookAtNextCharacter
//	#import <streams/streams.h>

@implementation TextFile


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		GetHexByte
//	Parameters:	none
//	Returns: 	The byte corresponding to the hex values in the file
//	Stores:		error info
//				The byte corresponding to the hex values in the file
//	Description:
//		This reads in a pair of hex difits, and returns their integer form (i.e reading
//		'41' will be returned as the integer 65.
//	*WARNING*:
//		This assumes it is working as a subclass of a File object that is going to
//		have a NXstream open.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Byte) GetHexByte
{
	Integer		theInt = 0;
	Integer		num;
	
	[self ResetResults];
	if (FileIsOpen == NO)
		[self StoreErrorCode: ERR_FILENOTOPEN AndText: "Unable to read from a closed file"];
	else
	{
		num = NXScanf(TheFile, "%2x", &theInt);
		if (num != 1)
			[self StoreErrorCode: ERR_BADREAD AndText: "Unable to read a hex value"];
		else
			[self StoreErrorCode: ERR_OK AndText: "Got the byte just fine"];
	}
	[self StoreInteger: theInt];
	return theInt;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		GetNumber
//	Parameters:	none
//	Returns: 	The number read from the text file
//	Stores:		error info
//				The number read from the text file
//	Description:
//		This reads in a series of characters, and converts them to a signed integer
//		value.
//	*WARNING*:
//		This assumes it is working as a subclass of a File object that is going to
//		have a NXstream open.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Integer) GetNumber
{
	Integer		theInt = 0;
	Integer		num;
	
	[self ResetResults];
	if (FileIsOpen == NO)
		[self StoreErrorCode: ERR_FILENOTOPEN AndText: "Unable to read from a closed file"];
	else
	{
		num = NXScanf(TheFile, "%d", &theInt);
		if (num != 1)
			[self StoreErrorCode: ERR_BADREAD AndText: "Unable to read a hex value"];
		else
			[self StoreErrorCode: ERR_OK AndText: "Got the byte just fine"];
	}
	[self StoreInteger: theInt];
	return theInt;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		GetCharacter:
//	Parameters:	none
//	Returns: 	The character found
//	Stores:		error info
//				The character found
//	Description:
//		This merely reads a character in from the file and returns it, unless an error
//		occurrs.  Note that if the file is open, our error results are those which
//		Read:BytesInto: generates.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Character) GetCharacter
{
	Character	theBuffer = NullCharacter;
	
	[self ResetResults];
	if (FileIsOpen == NO)
		[self StoreErrorCode: ERR_FILENOTOPEN AndText: "Unable to read from a closed file"];
	else
	{
		[self Read: 1 BytesInto: &theBuffer];
		if ([self   GetErrorCode] != ERR_OK)
		{
			[self StoreErrorCode: ERR_BADREAD AndText: "Read error.  let's call it eof"];
			FileLocation = fileAtEOF;
		}
		else
			[self StoreErrorCode: ERR_OK AndText: "OK"];
	}
	
	[self StoreCharacter: theBuffer];
	return theBuffer;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		LookAtNextCharacter
//	Parameters:	none
//	Returns: 	The character found
//	Stores:		error info
//				The character found
//	Description:
//		This locates the next character waiting to be read, and returns it without
//		moving past it.
//	*WARNING*:
//		This assumes it is working as a subclass of a File object that is going to
//		have a NXstream open that can do ungetting!!!!!!
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Character) LookAtNextCharacter
{
	Character	theCharacter = NullCharacter;
	
	[self ResetResults];
	if (FileIsOpen == NO)
		[self StoreErrorCode: ERR_FILENOTOPEN AndText: "Unable to peek at a closed file"];
	else
	{
		if (NXAtEOS(TheFile) == YES)
		{
			[self StoreErrorCode: ERR_BADREAD AndText: "Read error.  let's call it eof"];
			FileLocation = fileAtEOF;
		}
		else
		{
			//
			//	NeXT doesn't document what error codes these generate, very well..
			//
			theCharacter =  NXGetc(TheFile);
			NXUngetc(TheFile);
			[self StoreErrorCode: ERR_OK AndText: "Read the character OK"];
		}
	}
	[self StoreCharacter: theCharacter];
	return theCharacter;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		UnGetCharacter
//	Parameters:	none
//	Returns: 	none
//	Stores:		error info
//	Description:
//		Thgis simply ungets whatever the last character got was.
//		(due to NeXT limitiations, you can't call this twice in a row to unget the last 2)
//	*WARNING*:
//		This assumes it is working as a subclass of a File object that is going to
//		have a NXstream open that can do ungetting!!!!!!
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- UnGetCharacter
{
	[self ResetResults];
	if (FileIsOpen == NO)
		[self StoreErrorCode: ERR_FILENOTOPEN AndText: "Unable to peek at a closed file"];
	else
	{
		if (NXAtEOS(TheFile) == YES)
		{
			[self StoreErrorCode: ERR_BADREAD AndText: "Read error.  let's call it eof"];
			FileLocation = fileAtEOF;
		}
		else
		{
			//
			//	NeXT doesn't document what error codes these generate, very well..
			//
			NXUngetc(TheFile);
			[self StoreErrorCode: ERR_OK AndText: "Ungot it ok"];
		}
	}
	return self;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		WriteText:
//	Parameters:	the line of text we are to write
//	Returns: 	self
//	Description:
//		Writes the content6s of theLine out litterally, with no additional fiddling with.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- WriteText: (CString) theLine
{
	[self Write: strlen(theLine) BytesFrom: (ByteString) theLine];
	return self;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		WriteTextLine:
//	Parameters:	the line of text we are to write
//	Returns: 	self
//	Description:
//		This writes out the speciied text, and follows it with a newline.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- WriteTextLine: (CString) theLine
{
	[self Write: strlen(theLine) BytesFrom: (ByteString) theLine];
	[self Write: 1 BytesFrom: (ByteString) "\n"];
	return self;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		WriteTextUsing:WithFormat:
//	Parameters:	A buffer provided by the caller, a format, and N arguments to be written
//	Returns: 	self
//	Stores:		nothing (bug)
//	Description:
//		This writes out a text string given variable arguments
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- WriteTextUsing: (CString) buffer WithFormat: (CString) format, ...;
{
	va_list parameter_list;
	
	va_start(parameter_list, format);

	vsprintf(buffer, format, parameter_list);  	// doc implies this does a va_end

	[self Write: strlen(buffer) BytesFrom: (ByteString) buffer];
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		AppendFrom:
//	Parameters:	Another file object (opened)
//	Returns: 	self
//	Stores:		nothing (bug)
//	Description:
//		This is a crude little routine that simply takes a file, assumes it is open and in
//		place, reads the whole file into memory, and then writs it out into itself.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- AppendFrom: sourceFile
{
	Integer size;
	ByteString	buffer;

	size = [sourceFile FileSize];
	buffer = NewByteString(size);
	[sourceFile   MoveTo: 0];
	[sourceFile Read: size BytesInto: buffer];
	[self Write: size BytesFrom: buffer];
	FreeByteString(buffer);
	return self;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		WriteInteger:
//	Parameters:
//		An integer
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This simply writes out an integer with a trailing space.  ooh ahh. 
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- WriteInteger: (Integer) theInt
{
	CString	temp	= NewCString(15);
	
	[self   WriteTextUsing: temp WithFormat: "%d ", theInt];
	
	FreeCString(temp);
	return self;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		WritePositiveInteger:
//	Parameters:
//		An integer
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This simply writes out a positive integer with a trailing space.  yawn.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- WritePositiveInteger: (PositiveInteger) theInt
{
	CString	temp	= NewCString(15);
	
	[self   WriteTextUsing: temp WithFormat: "%u ", theInt];
	
	FreeCString(temp);
	return self;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		WriteReal:
//	Parameters:	A real number
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This simply writes out a Real number with a trailing space..
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- WriteReal: (Real) theReal
{
	CString	temp	= NewCString(31);
	
	[self   WriteTextUsing: temp WithFormat: "%.15f ", theReal];
	
	FreeCString(temp);
	return self;
}


@end