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

		IShTokenStream.h

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

		IShTokenStream
		IShIOStream
		IExStreamIO

		Author:	Ray Dream
		Date:	3/2/98

		Revision History

		when		who			what & why
		==========	==========	======================================================
		02/01/99	del			Added GetShortArray, PutShortArray

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

#ifndef __ISHTOKENSTREAM__
#define __ISHTOKENSTREAM__

#if CP_PRAGMA_ONCE
#pragma once
#endif

#include "MCCompObj.h"
#include "COMTypes.h"
#include "MCPoint.h"
#include "MCRect.h"
#include "MCString.h"
#include "Vector3Fwd.h"
#include "Vector2Fwd.h"
#include "MCArray.h"
#include "MCCountedPtr.h"
#include "MCToken.h"


template<class T> class TMCArray;
template<class T> class TMCCountedPtrArray;
class TMCiostream;
class TMCColorRGB;
struct IStreamContext;
struct IShTokenStream;


//******************************* Globally Unique Ids ***********************************

MCDEFINE_GUID_H(IID_IShTokenStream);
MCDEFINE_GUID_H(IID_IExStreamIO);

#ifndef SHIOSTREAM_DEFINED
#define SHIOSTREAM_DEFINED
MCDEFINE_GUID_H(IID_IShIOStream);
#endif


//***************************************************************************************
//  Utilities classes that are used to write arrays

struct ITokenStreamArrayReader
{
	virtual void		MCCOMAPI SetElemCount(int32 elemCount) = 0;
	virtual void		MCCOMAPI ReadElem(IShTokenStream* stream, int32 index) = 0;
};

struct ITokenStreamArrayWriter
{
	virtual int32		MCCOMAPI GetElemCount() = 0;
	virtual void		MCCOMAPI WriteElem(IShTokenStream* stream, int32 index) = 0;
};


template<class Array>
class TTokenStreamArrayReader : public ITokenStreamArrayReader
{
public:
	TTokenStreamArrayReader(Array& array) : fArray(array) {}

	virtual void		MCCOMAPI SetElemCount(int32 elemCount) { fArray.SetElemCount(elemCount); }
	virtual void		MCCOMAPI ReadElem(IShTokenStream* stream, int32 index) { (*stream) >> fArray[index]; }

protected:
	Array&			fArray;
};

template<class Array>
class TTokenStreamArrayWriter : public ITokenStreamArrayWriter
{
public:
	TTokenStreamArrayWriter(const Array& array) : fArray(array) {}

	virtual int32		MCCOMAPI GetElemCount() { return fArray.GetElemCount(); }
	virtual void		MCCOMAPI WriteElem(IShTokenStream* stream, int32 index) { (*stream) << fArray[index]; }

protected:
	const Array&	fArray;
};

template<class T>
class TTokenStreamCountedPtrArrayReader : public ITokenStreamArrayReader
{
public:
	TTokenStreamCountedPtrArrayReader(TMCCountedPtrArray<T>& array) : fArray(array) {}

	virtual void		MCCOMAPI SetElemCount(int32 elemCount) { fArray.SetElemCount(elemCount); }
	virtual void		MCCOMAPI ReadElem(IShTokenStream* stream, int32 index)
	{
		TMCCountedPtr<T> elem;
		(*stream) >> &elem;
		fArray.SetElem(index, elem);
	}

protected:
	TMCCountedPtrArray<T>&		fArray;
};

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

/// \addtogroup ShellInterface
/// \{

/// Interface for token streams.  See MCToken.h for token definitions used.  Used to read/write tokenized data files (as are 
/// standard for Carrara) as in the following simple example, which reads in the position of a vertex:
/// \code
/// MCCOMErr ReadVertex(IShTokenStream* inStream,int8 *token, I3DExVertex* vertex)
/// {
///		ThrowIfNil(vertex);
///		stream->GetNextToken(token);
///		if (token[0] != tBEGIN)
///		{
///			return MC_ERROR_FILE_NOT_FOUND;
///		}
///		stream->GetNextToken(token);
///		int32 key;
///		TVector3 position = TVector3::kZero;
///		while (!stream->IsEndToken(token))
///		{
///			stream->CompactAttribute(token,&key);
///			switch (key)
///			{
///				case 'posi':
///					stream->GetPoint3D(&(position.x), &(position.y), &(position.z))
///					break;
///				default:
///					stream->SkipTokenData();
///					break;
///			}
///			stream->GetNextToken(token);
///		}
///		vertex->SetPosition(position);
///	}
/// \endcode
///
/// Before reading or writing a new file, call InitTokenMgr() to initialize various variables (such as indentation size).
///
/// To read and write binary data in a tokenized text file, we must encode them as text.  BeginBufferAsBinHex(), 
/// PutBufferAsBinHex(), GetBufferAsBinHex(), and EndPutBufferAsBinHex() handle this functionality.  
///
struct IShTokenStream: public IMCUnknown
{
	// new API

	// template functions
	template <class Array>
	void PutArrayAttribute(int32 keyword, const Array& array, const boolean multiLine= false)
	{
		TTokenStreamArrayWriter<Array> helper(array);
		PutGenericArrayAttribute(keyword, helper, multiLine);
	}

	template <class Array>
	void GetArrayToken(Array& array, const boolean multiLine= false)
	{
		TTokenStreamArrayReader<Array> helper(array);
		GetGenericArrayToken(helper, multiLine);
	}

	template <class T>
	void GetCountedPtrArrayToken(TMCCountedPtrArray<T>& array, const boolean multiLine= false)
	{
		TTokenStreamCountedPtrArrayReader<T> helper(array);
		GetGenericArrayToken(helper, multiLine);
	}

	// Get
	virtual void 		MCCOMAPI GetBeginToken() = 0;
	virtual void		MCCOMAPI GetEndToken() = 0;
	virtual MCCOMErr	MCCOMAPI GetNextToken(int8* token) = 0;
	virtual int32 		MCCOMAPI CompactAttribute(const int8* attrName) = 0;
	virtual boolean		MCCOMAPI IsEndToken(int8* atoken) = 0;
	virtual boolean		MCCOMAPI IsBeginToken(int8* token) = 0;
	virtual int32		MCCOMAPI GetKeyword() = 0;
	virtual void		MCCOMAPI SkipTokenData() = 0;
	virtual void 		MCCOMAPI SkipTokenData(boolean beginTokenWasRead) = 0;

	virtual int8 		MCCOMAPI GetInt8Token() = 0;
	virtual int16 		MCCOMAPI GetInt16Token() = 0;
	virtual int32 		MCCOMAPI GetInt32Token() = 0;
	virtual uint8 		MCCOMAPI GetUInt8Token() = 0;
	virtual uint16 		MCCOMAPI GetUInt16Token() = 0;
	virtual uint32 		MCCOMAPI GetUInt32Token() = 0;
	virtual real32 		MCCOMAPI GetReal32Token() = 0;
	virtual real64 		MCCOMAPI GetReal64Token() = 0;
	virtual int32 		MCCOMAPI GetFourCharConstToken() = 0;
	virtual uint32 		MCCOMAPI GetEnumToken() = 0;
	virtual boolean 	MCCOMAPI GetBooleanToken() = 0;
	virtual TMCPoint 	MCCOMAPI GetPointToken() = 0;
	virtual TMCRect 	MCCOMAPI GetRectToken() = 0;
	virtual void 		MCCOMAPI GetInt16ArrayToken(int32 nb, int16* valArray) = 0;
	virtual void 		MCCOMAPI GetInt32ArrayToken(int32 nb, int32* valArray) = 0;
	virtual void		MCCOMAPI GetReal32ArrayToken(int32 nb, real32* valArray) = 0;
	virtual TMCString255	MCCOMAPI GetStringToken() = 0;
	virtual void		MCCOMAPI GetRGBColorToken(TMCColorRGB& color) = 0;
	virtual void		MCCOMAPI GetVector3Token(TVector3& vector) = 0;
	virtual void 		MCCOMAPI GetInt16ArrayToken(TMCArray<int16>& valArray) = 0;
	virtual void 		MCCOMAPI GetInt32ArrayToken(TMCArray<int32>& valArray) = 0;
	virtual void 		MCCOMAPI GetReal32ArrayToken(TMCArray<real32>& valArray) = 0;
	virtual void		MCCOMAPI GetVector3ArrayToken(TMCArray<TVector3>& valArray)=0;
	virtual void		MCCOMAPI GetVector2ArrayToken(TMCArray<TVector2>& valArray)=0;
	virtual void		MCCOMAPI GetBooleanArrayToken(TMCArray<boolean>& valArray)=0;
	virtual void		MCCOMAPI GetGenericArrayToken(ITokenStreamArrayReader& array, const boolean multiLine=false) = 0;

	// Put
	virtual void		MCCOMAPI PutBeginToken(int32 keyword = 0) = 0;
	virtual void 		MCCOMAPI PutEndToken() = 0;
	virtual void 		MCCOMAPI PutFourCharConstAttribute(int32 keyword, int32 token) = 0;
	virtual void		MCCOMAPI PutInt8Attribute(int32 keyword, int8 value) = 0;
	virtual void		MCCOMAPI PutInt16Attribute(int32 keyword, int16 value) = 0;
	virtual void		MCCOMAPI PutInt32Attribute(int32 keyword, int32 value) = 0;
	virtual void		MCCOMAPI PutUInt8Attribute(int32 keyword, uint8 value) = 0;
	virtual void		MCCOMAPI PutUInt16Attribute(int32 keyword, uint16 value) = 0;
	virtual void		MCCOMAPI PutUInt32Attribute(int32 keyword, uint32 value) = 0;
	virtual void 		MCCOMAPI PutReal32Attribute(int32 keyword, real32 value) = 0;
	virtual void 		MCCOMAPI PutReal64Attribute(int32 keyword, real64 value) = 0;
	virtual void 		MCCOMAPI PutEnumAttribute(int32 keyword, uint32 value) = 0;
	virtual void 		MCCOMAPI PutBooleanAttribute(int32 keyword, boolean value) = 0;
	virtual void 		MCCOMAPI PutPointAttribute(int32 keyword, const TMCPoint& inPoint) = 0;
	virtual void 		MCCOMAPI PutRectAttribute(int32 keyword, const TMCRect& inRect) = 0;
	virtual void 		MCCOMAPI PutInt32ArrayAttribute(int32 keyword, int32 nb, int32* valArray) = 0;
	virtual void 		MCCOMAPI PutInt16ArrayAttribute(int32 keyword, int32 nb, int16* valArray) = 0;
	virtual void		MCCOMAPI PutReal32ArrayAttribute(int32 keyword, int32 nb, const real32* valArray) = 0; //if keyword is 0, its not written
	virtual void 		MCCOMAPI PutStringAttribute(int32 keyword, const TMCString& str) = 0;
	virtual void		MCCOMAPI PutRGBColorAttribute(IDType keyword, const TMCColorRGB& color) = 0;
	virtual void		MCCOMAPI PutVector3Attribute(IDType keyword, const TVector3& vector) = 0;
	virtual void 		MCCOMAPI PutInt16ArrayAttribute(int32 keyword, const TMCArray<int16>& valArray) = 0;
	virtual void 		MCCOMAPI PutInt32ArrayAttribute(int32 keyword, const TMCArray<int32>& valArray) = 0;
	virtual void 		MCCOMAPI PutReal32ArrayAttribute(int32 keyword, const TMCArray<real32>& valArray) = 0;
	virtual void		MCCOMAPI PutVector3ArrayAttribute(int32 keyword, const TMCArray<TVector3>& theArray) =0;
	virtual void		MCCOMAPI PutVector2ArrayAttribute(int32 keyword, const TMCArray<TVector2>& theArray) =0;
	virtual void		MCCOMAPI PutBooleanArrayAttribute(int32 keyword, const TMCArray<boolean>& theArray) =0;
	virtual void		MCCOMAPI PutGenericArrayAttribute(IDType keyword, ITokenStreamArrayWriter& array, const boolean multiLine=false) = 0;

	// DEPRECATED !!!!!

	//--------------------------
	// old API
	virtual MCCOMErr MCCOMAPI GetBegin			() = 0; 
	virtual MCCOMErr MCCOMAPI GetEnd			() = 0;
	virtual MCCOMErr MCCOMAPI GetKeyword		(int32* keyword) = 0;
	//virtual MCCOMErr MCCOMAPI GetNextToken		(int8* token) = 0;

	virtual MCCOMErr MCCOMAPI GetLong			(int32* value) = 0;
	virtual MCCOMErr MCCOMAPI GetQuickFix		(real* value) = 0;
	virtual MCCOMErr MCCOMAPI GetPoint2D		(real* x, real* y) = 0;
	virtual MCCOMErr MCCOMAPI GetPoint3D		(real* x,real* y,real* z) = 0;
	virtual MCCOMErr MCCOMAPI GetShortArray		(int32 nb, int16 *valArray) = 0;
	virtual MCCOMErr MCCOMAPI GetLongArray		(int32 nb, int32* valArray) = 0;
	virtual MCCOMErr MCCOMAPI GetString			(int8 *astring) = 0;
	virtual MCCOMErr MCCOMAPI GetArray			(int32 nb, real* valArray) = 0;
	virtual MCCOMErr MCCOMAPI GetMat33			(real& m00, real& m01, real& m02,
												 real& m10, real& m11, real& m12,
												 real& m20, real& m21, real& m22) = 0;

	virtual real32	MCCOMAPI GetReal32() = 0;
	virtual void	MCCOMAPI GetRGBColor(TMCColorRGB& color) = 0;
	virtual void	MCCOMAPI GetVector3(TVector3& vector) = 0;

	virtual MCCOMErr MCCOMAPI CompactAttribute	(int8* atoken, int32* keyword) = 0;
	virtual MCCOMErr MCCOMAPI Indent			() = 0;

	virtual MCCOMErr MCCOMAPI PutKeyword		(int32 keyword) = 0;
	virtual MCCOMErr MCCOMAPI PutKeywordAndBegin(int32 keyword) = 0;
	virtual MCCOMErr MCCOMAPI PutEnd			() = 0;
	virtual MCCOMErr MCCOMAPI PutBegin			() = 0;

	virtual MCCOMErr MCCOMAPI PutLong			(int32 along) = 0;
	virtual MCCOMErr MCCOMAPI PutQuickFix		(real avalue) = 0;
	virtual MCCOMErr MCCOMAPI PutPoint2D		(real x,real y) = 0;
	virtual MCCOMErr MCCOMAPI PutPoint3D		(real x,real y, real z) = 0;
	virtual MCCOMErr MCCOMAPI PutShortArray		(int32 nb, const int16 *valArray) = 0;
	virtual MCCOMErr MCCOMAPI PutLongArray		(int32 nb, const int32* valArray) = 0;
	virtual MCCOMErr MCCOMAPI PutString			(const int8 *astring) = 0;
	virtual MCCOMErr MCCOMAPI PutArray			(int32 nb, const real* valArray) = 0;
	virtual MCCOMErr MCCOMAPI PutMat33			(real m00, real m01, real m02,
												 real m10, real m11, real m12,
												 real m20, real m21, real m22) = 0;


	// ****************************************************************************
	//								BeginBufferAsBinHex
	// 
	/// Initialise the saving of a binary buffer into a text stream. Should be called before PutBufferAsBinHex.
	/// 
	/// \param lastInfo  Pass this a 0.  This will be used by PutBufferAsBinHex and EndBufferAsBinHex.
	/// 
	virtual void MCCOMAPI 	BeginBufferAsBinHex		(uint32 &lastInfo) = 0;
	
	// ****************************************************************************
	//								PutBufferAsBinHex
	// 
	/// Save a binary buffer into a text stream. lastInfo should come from the last
	/// call to PutBufferAsBinHex or from BeginBufferAsBinHex.
	/// 
	/// \param data      pointer to the buffer to save
	/// \param count     number of bytes of data in the buffer
	/// \param lastInfo  From the last PutBufferAsBinHex or BeginBufferAsBinHex call
	/// 
	virtual void MCCOMAPI 	PutBufferAsBinHex		(int8* data,int32 count,uint32 &lastInfo) = 0;

	// ****************************************************************************
	//								GetBufferAsBinHex
	// 
	/// Load a binary buffer from a text stream. lastInfo should come from the last
	/// call to GetBufferAsBinHex or from BeginBufferAsBinHex.
	/// 
	/// \param data      pointer to the buffer to fill
	/// \param count     number of bytes to fill
	/// \param lastInfo  From the last GetBufferAsBinHex or BeginBufferAsBinHex call
	/// 
	virtual void MCCOMAPI	GetBufferAsBinHex(int8* data,int32 count, uint32 &lastInfo) = 0;


	// ****************************************************************************
	//								EndPutBufferAsBinHex
	// 
	/// Finish saving a binary buffer. lastInfo should come from the last call to
	/// PutBufferAsBinHex.
	/// 
	/// \param lastInfo  From the last PutBufferAsBinHex
	/// 
	virtual void MCCOMAPI 	EndPutBufferAsBinHex	(uint32 &lastInfo) = 0;

	//ugly hack to put/get arrays
	virtual TMCiostream& MCCOMAPI GetStream() = 0;

	// ****************************************************************************
	//								InitTokenMgr
	// 
	/// InitTokenMgr() should be called every time before reading or
	/// writing a new file. The parameter sets gTokenAsciiFormat (this
	/// controls ASCII translation). Other globals like the indentation level
	/// are reset to their default values too.
	///
	virtual MCCOMErr MCCOMAPI InitTokenMgr() = 0;

	// ****************************************************************************
	//								GetStreamContext
	// 
	/// Gets the stream context (see TMCiostream).
	///
	virtual IStreamContext*		MCCOMAPI	GetStreamContext() const = 0;

	// ****************************************************************************
	//								PutVector3ArrayAttribute
	// 
	/// Write an array of int16 (added in 6.1)
	///
	virtual void		MCCOMAPI PutUint16ArrayAttribute(int32 keyword, const TMCArray<uint16>& theArray) =0;

};

/// Helper object that allow you to read an object from a stream. The object must
/// implement the method:
/// boolean ReadAttribute(IShTokenStream* stream,IDType keyword).
///
template<class T> class TAttributeReader
{
public:
	TAttributeReader(T& object,IShTokenStream* stream) : fObject(object),fStream(stream)
	{
	}

	MCCOMErr Read()
	{
		fStream->GetBeginToken();

		int8 token[kMaxTokenSize];

		MCCOMErr result=fStream->GetNextToken(token);
		if (result) return result;

		while (!fStream->IsEndToken(token))
		{
			int32 keyword=0;
			fStream->CompactAttribute(token,&keyword);

			if (!fObject.ReadAttribute(fStream,keyword))
			{
				fStream->SkipTokenData();
			}
			result=fStream->GetNextToken(token);
			if (result) return result;
		};
		return MC_S_OK;
	}

	MCCOMErr ReadIndexed(IDType indexKeyword,int32& index)
	{
		fStream->GetBeginToken();

		int8 token[kMaxTokenSize];

		MCCOMErr result = fStream->GetNextToken(token);
		if (result) return result;

		while (!fStream->IsEndToken(token))
		{
			int32 keyword=0;
			fStream->CompactAttribute(token,&keyword);

			if (keyword == indexKeyword)
			{
				index = fStream->GetInt32Token();
			}
			else if (!fObject.ReadAttribute(fStream,keyword))
			{
				fStream->SkipTokenData();
			}
			result=fStream->GetNextToken(token);
			if (result) return result;
		};
		return MC_S_OK;
	}
protected:
	T&					fObject;
	IShTokenStream*		fStream;
};


/// \}
typedef void (*ReadAttributeProc)(int32 keyword, IShTokenStream* stream, void* privData); // Call-back to call if unknown attribute is encountered

#ifndef qDemoVersion //regular interface

/// \addtogroup ExternalInterface
/// \{

// ****************************************************************************
//								IExStreamIO
// 
/// Derive from StreamIO if your component needs to read/write data that are
/// not included in its parameter map.
/// 
struct IExStreamIO: public IMCUnknown
{
	// ****************************************************************************
	//								Read
	// 
	/// Read the extra data of a component from a stream. The data will be enclosed
	/// in a begin '{' token and an end '}' token. Even if no extra data is read, this
	/// function should at least read these two tokens.
	/// 
	/// \param stream       A token stream
	/// \param readUnknown  A callback function to call when an unknown token is encountered.
	/// \param privData     The private data that should be passed to the callback.
	/// 
	virtual MCCOMErr MCCOMAPI Read				(IShTokenStream* stream, ReadAttributeProc readUnknown, void* privData) = 0; // readUnknown should be called by extention if unknown keyword is encountered (instead of calling SkipoTokenData)
	
	// ****************************************************************************
	//								Write
	// 
	/// Writes extra data that are not stored in the parameter map of the component.
	/// Unlike Read (cf. above), this function should not write the enclosing
	/// tokens, but just the extra data.
	/// 
	/// \param stream  A tocken stream
	/// 
	virtual MCCOMErr MCCOMAPI Write				(IShTokenStream* stream) = 0;

	// ****************************************************************************
	//	   						FinishRead
	// 
	/// This function is called after everything in the component has been read. 
	/// It allows, for instance, postcomputation on the values that have been read.
	/// 
	/// \param streamContext	the context of the stream where the values have been read
	/// 
	virtual MCCOMErr MCCOMAPI FinishRead		(IStreamContext* streamContext) = 0;
};

/// \}

#else 	//(qDemoVersion) begin scrambled interface

struct IExStreamIO: public IMCUnknown
{
	virtual MCCOMErr MCCOMAPI Write				(IShTokenStream* stream) = 0;
	virtual MCCOMErr MCCOMAPI Read				(IShTokenStream* stream, ReadAttributeProc readUnknown, void* privData) = 0; // readUnknown should be called by extention if unknown keyword is encountered (instead of calling SkipoTokenData)
	virtual MCCOMErr MCCOMAPI FinishRead		(const IStreamContext* stream) = 0;
};

#endif	//(qDemoVersion) end scrambled

#endif
