/****************************************************************************************


		MCStream.h


		Copyright: (c) 2001 Eovia Corp. All rights reserved.


		Author:	Pierre
		Date:	12/12/97

		Revision History

		when		who			what & why
		==========	==========	======================================================
		10/05/99	EricB		Changed kEOFChar to match with keofErr
		9/30/99		Ian			Init(): Removed the const from IMCFile parameter, it
								can't be const since we are modifying the file.
		9/21/99		cph			Remove TMCFile dependency
		8/17/99		Emil		Upgraded SetInfo/GetInfo
		2/22/99		Ian			Made fFile a TMCCountedPtr.
		02/16/99	Mathieu		isWhite() now member of class, fSeparators should be assigned in the constructor
		10/22/98	Eml			Removed Thierry's mods
		10/21/98	Thierry		correct << and >> for int8 and uint8 (it was a bin write !)
		7/5/98		Ian			Yanked MCOSConfig.h, it wasn't needed.
		5/8/98		Ian			Added virtual operator>> and << for TMCString.
		4/30/98		Ian			Converted more system types to MCCore types float -> real32 etc.
		4/16/98		psq			replaced HRESULT by MCErr
		4/16/98		Ian			Made operator>>s virtual.
		3/24/98		Joshua		Added Stream Contexts
		3/24/98		Ian			Added casts to fix warnings in: operator>>(uint8& v),
								operator>>(uint16& v), and operator!().

*****************************************************************************************/

#ifndef __MCSTREAM__
#define __MCSTREAM__

#include "MCOSConfig.h"
#include "MCBasicTypes.h"
#include "MCCountedPtr.h"
#include "IMCFile.h"

#include "ITranscoder.h"

//iostream throw TMCException codes
#define kUnexpectedChar 17001
#define kOutOfBounds 17002
#define kEOF 17003
#define kUnimplementedStreamFeature 17004
#define kBadDataBuffer 17005

class  TMCiostreamContext;
class  TMCDynamicString;

struct TMCiostreamInfoStruct;

/*****************************************************************************\
*                                                                             *
*  TMCiostream                                                                *
*                                                                             *
\*****************************************************************************/

/// \addtogroup MCCore
/// \{

/// Stream context interface
///
struct IStreamContext : public IMCUnknown
{
};

/// Base class for all the core classes used to read from or write into something 
/// (text file, string, binary file, socket...)
///
class TMCiostream : public TMCObject
{
public:
	TMCiostream();
	virtual ~TMCiostream();
public:
	enum open_mode
	{
		in			= IMCFile::eRead,
		out			= IMCFile::eWrite,
		current		= 0x0020,
		nocreate	= 0x0040
	};

	///Direction of seek
	enum seek_dir
	{
		beg			= IMCFile::eBegin, 
		cur			= IMCFile::eCurrent, 
		end			= IMCFile::eEnd
	};
	enum io_state
	{
		goodbit  = 0,
		eofbit   = 1,
		failbit  = 2
	};	

	virtual int32	tell					()=0;
	virtual void	seek					(int32 off, seek_dir dir=beg)=0;
			int32	tellg					() {return tell();}
			int32	tellp					() {return tell();}
 			void	seekp					(int32 off, seek_dir dir=beg) {seek(off, dir);}
 			void	seekg					(int32 off, seek_dir dir=beg) {seek(off, dir);}
	
	virtual void	read					(int8* s,int32 n)=0;
			void	read					(uint8* s,int32 n) {read((int8*)s,n);}
	virtual	void	get						(int8& c) {read(&c,1);}
			void	get						(int8* pstr,int nCount,int8 delim='\n');
			void	getline					(int8* pstr,int nCount,int8 delim='\n');
			void	getLine					(TMCDynamicString& outLine, int8 delim='\n');

	virtual void	putback					(int8 c)=0;							///< can only be used on (some) input streams
			int32	gcount					() const {return fgcount;}
	
	virtual void	write					(const int8* s, int32 n)=0;
			void	put						(int8 c) {write(&c,1);}
	
			int		precision				(int precision);
			int		precision				()	{return fPrecision;}
			int		width					(int width);
			int		width					()	{return fWidth;}
	
	virtual TMCiostream& operator>>			(int8* p) {readstring(p); return (*this);}
	virtual TMCiostream& operator>>			(uint8* p) {readstring((int8*)p); return (*this);}
	
	//those 2 functions read characters not integers
	virtual TMCiostream& operator>>			(int8& v) {v=myipfx(); return (*this);}
	virtual TMCiostream& operator>>			(uint8& v) {v=(uint8)myipfx(); return (*this);}

	virtual TMCiostream& operator>>			(int32& v) {readlong(v); return (*this);}
	virtual TMCiostream& operator>>			(uint32& v) {readunsignedlong(v); return (*this);}
	virtual TMCiostream& operator>>			(int16& v) {int32 l; readlong(l); v=(int16)l; return (*this);}
	virtual TMCiostream& operator>>			(uint16& v) {uint32 l; readunsignedlong(l); v=(uint16)l; return (*this);}
	virtual TMCiostream& operator>>			(real32& v) {double d; readdouble(d); v=(float)d; return (*this);}
	virtual TMCiostream& operator>>			(real64& v) {readdouble(v); return (*this);}
	virtual TMCiostream& operator>>			(TMCString& ioStr);
	virtual TMCiostream& operator>>			(TMCiostream& (*f)(TMCiostream&)) {return (*f)(*this);}

	virtual TMCiostream& operator<<			(const int8* p) {writestring(p); return (*this);}
	virtual TMCiostream& operator<<			(const uint8* v) {writestring((const int8*)v); return (*this);}

	//those 2 functions write characters not integers
	virtual TMCiostream& operator<<			(int8 v) {write(&v,1); return (*this);}
	virtual TMCiostream& operator<<			(uint8 v) {write((int8*)&v,1); return (*this);}

	virtual TMCiostream& operator<<			(int32 v) {writelong(v); return (*this);}
	virtual TMCiostream& operator<<			(uint32 v) {writeunsignedlong(v); return (*this);}
	virtual TMCiostream& operator<<			(int16 v) {writelong(v); return (*this);}
	virtual TMCiostream& operator<<			(uint16 v) {writeunsignedlong(v); return (*this);}
	virtual TMCiostream& operator<<			(int64 v) {writelong(v); return (*this);}
	virtual TMCiostream& operator<<			(uint64 v) {writeunsignedlong(v); return (*this);}
	virtual TMCiostream& operator<<			(real32 v) {writefloat(v); return (*this);}
	virtual TMCiostream& operator<<			(real64 v) {writedouble(v); return (*this);}
	virtual TMCiostream& operator<<			(const TMCString& outStr){writeQuotedString(outStr); return *this;}
	virtual TMCiostream& operator<<			(TMCiostream& (*f)(TMCiostream&)) {return (*f)(*this);};
	
			int			 rdstate			() const {return (int)fState;}
			void		 clear				(int state = 0) {fState=state;}
			int			 good				() const {return (fState==0);}
			int			 eof				() const {return (int)(fState & eofbit);}
			int			 fail				() const {return (int)(fState & failbit);} 
			boolean		 operator!			() const {return fail() ? true : false;}
						 operator void*		() const {if (fail()) return 0; else return (void*)this;}

	virtual	void		 readFileRef		(int8* p);
	virtual void		 writeFileRef		(const int8* p);
	virtual void		 GetPathRelativeFolder(TMCString& outString);
			void		 readQuotedString	(TMCString& outString);

			// -Description:
			//		reads a quoted string on a single line
			// -Parameters:
			//		outString : where to store the result (set to lenght 0 at the begining)
			//		stringSize: the maximum number of characters that can be put in the string (255 for a TMCString255, -1 for a dynamic one)
			// -Returns:
			//		false in case of error (missing quote or line break before the end of the string)
			//		true if the string was put in outString
			boolean		 ReadSingleLineQuotedString(TMCString& outString, uint32 stringSize);

			void		 writeQuotedString	(const TMCString& string);
			int32		 GetStreamSize		();

			//SetInfo and GetInfo allow you to store private data into a stream.
			//infoID identifies the data
			//all info will be copied in SetInfo and deleted when the stream is destroyed
			void		SetInfo				(IDType infoID, void* info, uint32 size);
			void*		GetInfo				(IDType infoID);

	virtual TMCiostreamContext*	GetContext	() {return nil;}

	virtual IStreamContext*		GetStreamContext() { return fStreamContext; }
	virtual void				SetStreamContext(IStreamContext* context) { fStreamContext = context; }

	virtual void		WriteLocalizedString(const TMCString& string) { writeQuotedString(string); }
	virtual boolean		ReadLocalizedString(TMCString& string);
	virtual void		ReadLocalizedDynamicString(TMCDynamicString& string);

	//Reads a string into a buffer and do not overflow it!
	//returns false if something went wrong (eg string too big for buffer)
			boolean		ReadString			(int8* p, int32 bufferSize);

	//Gets/Sets the encoding of the stream
			ITranscoder*	GetTranscoder() { return fEncoding; }
			void			SetTranscoder(TranscoderType type);
	//Checks for / Writes the identifier of the encoding
			void	WriteIdentifier();
			void	CheckIdentifier();

protected:
			void		readstring			(int8* p);
			void		readlong			(int32& v);
			void		readunsignedlong	(uint32& v);
			void		readdouble			(double& v);

			void		writestring			(const int8* p);
			void		writelong			(int32 v);
			void		writeunsignedlong	(uint32 v);
			void		writefloat			(float v);
			void		writedouble			(double v);
			
			void		streamfail			(int32 err);

			int32		fFlags;
			int32		flags				() const {return fFlags;}
			int32		setf				(int32 setbits, int32 field);
			int32		setf				(int32 setbits);
			int32		unsetf				(int32 unsetbits);
			
			int32		fState;
			void		changestate			(int state) {fState|=state;}
	
			IMCFile::EFilePermission fMode; // in/out mode
			int			fPrecision;
			int			fWidth;
			int32		fgcount;
			int8*		fSeparators;

			TMCiostreamInfoStruct*	fInfos;
			uint32					fInfosCount;

			TMCCountedPtr<ITranscoder>	fEncoding;
			uint8						fIDOffset;	
private:
			boolean		isWhite				(int8);
			int8		myipfx				();
			uint32		myreadunsignedlong	(int8& c);
			int8		mygetline			(int8* pstr,int nCount,int8 delim='\n');

	TMCCountedPtr<IStreamContext>		fStreamContext;


	friend TMCiostream& dec					(TMCiostream& s);
	friend TMCiostream& hex					(TMCiostream& s);
};

/// \}



TMCiostream& endl							(TMCiostream& s);
TMCiostream& ends							(TMCiostream& s);

TMCiostream& dec							(TMCiostream& s);
TMCiostream& hex							(TMCiostream& s);

inline boolean	TMCiostream::isWhite		(int8 c)
{
	uint16 i = 0;
	while (fSeparators[i]) if (c == fSeparators[i++]) return true;
	return false;
}
	

/// \addtogroup MCCore
/// \{

/// Reads/writes from/into a memory buffer.
class  TMCstrstream : public TMCiostream 
{
public:
				TMCstrstream				(TranscoderType type); //dynamic strstream
				TMCstrstream				(const TMCDynamicString& str, TranscoderType type); //input dynamic strstream
				TMCstrstream				(int8* string, TranscoderType type); //zero terminated
				TMCstrstream				(int8* start, int32 size, TranscoderType type, int mode=TMCiostream::in);
				~TMCstrstream				();
	
			int8*	str						();
	const	int8*	Cstr					() {return fBuffer;}
			int32	pcount					() const;
	
	virtual int32	tell					();
	virtual void	seek					(int32 off, seek_dir dir=beg);
	virtual void	putback					(int8 c);
	virtual void	read					(int8* s,int32 n);
	virtual void	write					(const int8* s, int32 n);

	virtual	void	get						(int8& c);

	int32	GetSize					();
	
protected:
	virtual void	FlushBuffer				();

	int8*		fBuffer;
	int32		fBufferSize;
	int32		fBufferAllocatedSize;
	int8*		fCur;
	int8*		fOwnBuffer;
};    

/// \}


#define kDefaultBufferSize 0x7f00
#define kEOFChar (keofErr)

/// \addtogroup MCCore
/// \{

/// Reads/writes from/into a local file
///
class TMCfstream : public TMCiostream 
{
public:
	TMCfstream			();
	TMCfstream			(const int8* fullPathName, TranscoderType type, int mode=TMCiostream::in, int32 bufSize = kDefaultBufferSize, boolean* createdNewFile = nil);

	///If you use TMCiostream::current for mode, the mode will be taken from inFile, in which case you'd better have set the correct permission on it...
	TMCfstream			(IMCFile& inFile, int mode, TranscoderType type, int32 bufSize = kDefaultBufferSize, boolean* createdNewFile = nil);
	~TMCfstream			();

	virtual		MCErr		Init			(const int8* fullPathName, TranscoderType type, int mode=TMCiostream::in, int32 bufSize = kDefaultBufferSize, boolean* createdNewFile = nil);
	virtual		MCErr		Init			(IMCFile& file, int32 bufSize,boolean* createdNewFile, TranscoderType type);

	virtual		int32		tell			();
	virtual		void		seek			(int32 off, seek_dir dir=beg);
	virtual		void		putback			(int8 c);
	virtual		void		read			(int8* s,int32 n);
	virtual		void		write			(const int8* s, int32 n);
				void		append			(int32 n);

				void	flush				();
				void	synch				();		//eml : must be used if file is changed without using the stream
				void	SetInfo				(int32 creator, int32 type);
				int32	GetFileSize			();
	virtual		void	readFileRef			(int8* p);
	virtual		void	writeFileRef		(const int8* p);
	virtual void		GetPathRelativeFolder(TMCString& outString);

	virtual TMCiostreamContext*	GetContext	();

	const IMCFile*		GetFile				()		{ return fFile; }

public:
	static void			GetFileRef(const int8* itsFullPathName, int8* itsFileRef, const int8* currentPath);
	static void			GetFullPathNameFromRef(const int8* itsFileRef, int8* itsFullPathName, const int8* currentPath);

protected:
				TMCCountedPtr<IMCFile>	fFile;
				int8*					fBuffer;
				uint32					fBufferSize;
				uint32					fActualBufSize;
				uint32					fBufferPos;		//current pos within buffer
				uint32					fBufferOffset;	//offset in file of buffer
				uint32					fFileSize;
				boolean					fBufferModified;
				boolean					fFirstBufferLoaded;
	
				void	close				();
				void	LoadBuffer			(int32 pos);
				void	LoadPrevBuffer		();
				void	LoadNextBuffer		();
				void	DumpBuffer			();
};    

/// \}

TMCfstream& flush(TMCfstream& s);


/// \addtogroup MCCore
/// \{

/// Counts how many characters would be written in a stream.
/// putback() and read() are not implemented
class  countstream: public TMCiostream 
{
public:
			countstream();
			~countstream();
		
			int32	count					();

	virtual int32	tell					();
	virtual void	seek					(int32 off, seek_dir dir=beg);
	virtual void	putback					(int8 c);
	virtual void	read					(int8* s,int32 n);
	virtual void	write					(const int8* s, int32 n);

protected:
			int32	fCount;   
			int32	fPos;
};    

/// \}



/*-----------------------------------------------------------

Stream Contexts are used to store the state of a stream so
that the stream can be recreated later at the exact same
state.

-----------------------------------------------------------*/

/// Undocumented
///
class TMCiostreamContext : public TMCObject
{
public:
	enum {kFileNameChange=1, kPermissionsChange=2};

	virtual					~TMCiostreamContext () {};
	virtual TMCiostream*	CreateStream		()=0;
	virtual void			Change				(void* from, void* to, short changeType)=0;
};

/// Undocumented
///
class TMCfstreamContext : public TMCiostreamContext
{
public:
	TMCfstreamContext						(const char* fullPathName, int mode, int32 bufSize, int32 pos);
	~TMCfstreamContext						();

	virtual TMCiostream*	CreateStream	();
	virtual void			Change			(void* from, void* to, short changeType);

protected:
	int8*			fFileName;
	int				fMode;
	int32			fBufferSize;
	int32			fPos;
};

#endif
