
//**************************************************************************/
// Copyright (c) 2008 Autodesk, Inc.
// All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk license
// agreement provided at the time of installation or download, or which
// otherwise accompanies this software in either electronic or hard copy form.
//
//**************************************************************************/
// DESCRIPTION:
// CREATED: October 2008
//**************************************************************************/

namespace mudbox {

typedef Node *NodePointer;

/// \brief Streams are used to read information from a file, or to write it to a file.
///
/// In a Mudbox plug-in, you would typically use the Stream class when you override the
/// Serialize method of a custom node that you have derived from the Node class.
/// When you save or load a scene, Mudbox will call that method and pass you a 
/// stream object, which you can then use.
///
/// Typically, a stream object is used like this:
/// \code
///		void myNode::Serialize( Stream& s )
///		{
///			myParentClass::Serialize(s);
///			s == myVariable1 == myVariable2 == myVariable3;
///		}
/// \endcode
/// Here, the "==" operator will store the variables in the stream (if the stream
/// is writing), or read them from the stream (if it is reading).  So you can use
/// the same code for both reading and writing.  Since the "==" operator returns
/// a stream reference, you can serialize multiple variables on the same line if you
/// wish to do so, as shown above.
class MBDLL_DECL Stream : public Node
{
	DECLARE_CLASS;
protected:
	/// You cannot create a Stream object directly. Call CreateInstance<Stream>(), then use Stream::Open to specify parameters.
	Stream( void );
	
public:
	enum { chunk = 0x800000 };

	/// Destructor
	virtual ~Stream( void );

	/// Returns the name of the file.
	virtual QString FileName( void ) const;

	/// \brief Returns \b true if the stream has a valid version number.
	///
	/// When Mudbox creates a new stream for writing, it gives the stream its version number.
	/// Similarly, if a stream is opened for reading, it has a version number.  This method
	/// checks to see if the number is valid.
	///
	/// The version number can be invalid if there is a problem with the file, or if
	/// the file was saved in a newer version of Mudbox, and you are trying to read it
	/// into an older version.
	virtual bool IsValid( void );

	/// \brief (Deprecated) Checks if the file is older than a specified version. Returns \b true if it is.
	///
	/// Typically this method is used during \a Serialize to handle changes to the file
	/// format between versions of the software.
	/// This function is deprecated. Use the class specific one with the same name instead.
	virtual bool IsOlderThan(
		int iVersion	///< [in] The version number you want to compare
		) const;

	/// \brief (Deprecated) Checks if the file is newer than a specified version.
	///
	/// Returns \b true if it is, \b false if it is not.
	/// Typically this method is used during \b Serialize() to handle changes to the file
	/// format between versions of the software.
	/// This function is deprecated. Use the class specific one with the same name instead.
	virtual bool IsNewerThan(
							 int iVersion	///< [in] The version number you want to compare
							 ) const;
	
	/// \bried Returns the version for a class inside the current stream.
	/// 
	/// This function returns the version number which was used for a class when the stream was created.
	/// If the stream is a storing stream, then this number equals to the current version of the class.
	virtual int ClassVersion( const class ClassDesc *pClass ) const;
	
	/// \brief Checks if the file is older than a specified version for the given class. Returns \b true if it is.
	///
	/// Typically this method is used during \a Serialize to handle changes to the file
	/// format between versions of the software.
	///
	/// Normally, you call this function within your Serialize implementation, something like this:
	/// \code
	/// if ( s.IsOlderThan( 5, this );
	/// \endcode
	/// In this example the specified version number (5) is a local version number to your class, while the second parameter
	/// just identifies the class where you are calling the function. See the macro IMPLEMENT_SCLASS for more details.
	template <typename Type>
	bool IsOlderThan(
					 int iVersion,	///< [in] The version number you want to compare
					 Type *pClassInstance ///< [in] An instance of the class which should be tested
					 ) const { return ClassVersion( Type::StaticClass() ) < iVersion; };
	
	/// \brief Checks if the file is newer than a specified version for the given class.
	///
	/// Returns \b true if it is, \b false if it is not.
	/// Typically this method is used during \b Serialize() to handle changes to the file
	/// format between versions of the software.
	///
	/// Normally, you call this function within your Serialize implementation, something like this:
	/// \code
	/// if ( s.IsNewerThan( 4, this );
	/// \endcode
	/// In this example the specified version number (4) is a local version number to your class, while the second parameter
	/// just identifies the class where you are calling the function. See the macro IMPLEMENT_SCLASS for more details.
	template <typename Type>
	bool IsNewerThan(
					 int iVersion,	///< [in] The version number you want to compare
					 Type *pClassInstance ///< [in] An instance of the class which should be tested
					 ) const { return ClassVersion( Type::StaticClass() ) > iVersion; };

	/// Returns \b true if the stream is in storing mode (i.e. writing a file), and
	/// \b false if it is in reading mode.
	virtual bool IsStoring( void ) const;

	/// Use this method to inform Mudbox that there is an error in the stream you
	/// are trying to read or write.
	virtual void SetError( bool bError );

	/// Returns the version number of the stream.
	virtual int Version( void ) const;

	/// Returns the current position in the file. See also SetCurrentPosition().
	virtual uint64 CurrentPosition( void ) const;

	/// \brief Sets the current position in the file.
	///
	/// Further calls to the stream will 
	/// serialize from the new location.  You can use this to skip over data in 
	/// the file that you don't currently need.
	virtual void SetCurrentPosition(
		uint64 iPosition		///< [in] The byte address to jump to
		);

	/// \brief Call this function from your Serialize implementation, if you detect that the file content is corrupt.
	///
	/// This function will throw an exception, so don't expect it to return. This call basically cancels the serialization of the current node and will try to
	/// step to the next one.
	virtual void ReportCorruption( void );
	
	/// \brief Opens the file attached to this stream.
	///
	/// After creating a Stream object, this is the first function you normally call, which initializes the stream.
	virtual void Open(
		const QString &sFileName,		///< [in] The name of the file to be opened
		bool bStoring = false,		///< [in] Set this to \a true if you are storing data (i.e. writing the file), and \a false otherwise
		int iProjectedFileSize = 0,		///< [in] If you know how big your file will be, you can optionally specify this argument to help display the status bar correctly
		bool bProgressBar = true		///< [in] If \a true, a progress bar will be displayed while reading and writing the file.
	);
	
	/// Re-opens the file attached to this stream.
	virtual void Reopen(
		bool bStoring = false	///< [in] If \b true, open the file for writing.  If \b false, open it for reading.
		);

	/// \brief Closes the file attached to this stream. Returns true if the operation succeed.
	///
	/// This function is automatically called when the stream object is deleted. You can call this function directly before destructing the stream object, if 
	/// you want to know about any corruption during writing, in which case this function returns false.
	virtual bool Close( void );

	/// Returns true if you are reading the file, and you have reached the end of it.
	virtual bool Eof( void );

	/// \brief This function reads the content of the stream into the memory, when the stream is opened for read.
	///
	/// This function is called automatically when the first pointer is serialized from the stream. You can directly call this function right after opening the stream, 
	/// if you would like to know if the stream is loaded properly, or corruption was detected.
	virtual bool ReadContents( void );

	/// \brief Reads data directly from the stream into a buffer you provide.
	///
	/// This method returns the number of bytes that were successfully
	/// transferred to the buffer. (This number can be different if there
	/// is an error, or if you reach the end of the file-- IS THIS TRUE?)
	virtual size_t Read(
		void *pBuffer,	///< [in] A pointer to a block of memory to hold the data
		size_t iSize	///< [in] The number of bytes to be read
		);

	/// \brief Writes data to the stream from a buffer you provide.
	///
	/// Returns the number of bytes written.
	virtual size_t Write(
		const void *pBuffer,	///< [in] A pointer to a buffer of data you want to write
		size_t iSize			///< [in] The number of bytes you want to write
		);

	/// Reads a single integer from the stream, and returns it.
	inline int ReadInt( void ) { int i; operator >>( i ); return i; };

	/// Takes the entire contents of the stream and writes it into the specified
	/// file.
	virtual void ReadToFile(
		const char *sFile	///< [in] The name of the file that you want to store the stream data in
		);

	/// Fills the stream with the content of the specified file.
	virtual void WriteFromFile(
		const char *sFile	///< [in] The name of the file that you want the stream to get its data from.
		);

	/// Reads data from the stream to any data type.
	template < typename type >
	inline Stream &operator >>( type &v ) { MB_VERIFY_EQ( Read( &v, sizeof(type) ), sizeof(type) ); return *this; };

	/// Stores any data type into the stream.
	template < typename type >
	inline Stream &operator <<( const type &v ) { MB_VERIFY_EQ( Write( &v, sizeof(type) ), sizeof(type) ); return *this; };

	/// Read/Writes node pointers.
	virtual Node *ReadPointer( void );
	virtual void WritePointer( Node *pPointer );

	/// \brief Reads a pointer from the file.
	///
	/// This function will serialize any pointer whose target is an object whose class was derived from the Node class. Other kind of pointers (like int*) cannot be serialized.
	template < typename type >
	inline Stream &operator >>( type *&p ) { p = dynamic_cast<type *>( ReadPointer() ); return *this; }; 

	/// \brief Writes a pointer into the stream.
	///
	/// This function writes a pointer to the stream, if the pointer target is an object whose class was derived from the Node class. Other kind of pointers 
	/// cannot be serialized.
	template < typename type >
	inline Stream &operator <<( type *&p ) { WritePointer( p ); return *this; };

	/// Reads data from the stream to any Store (array) type.
	template < typename type >
	inline Stream &operator >>( Store<type> &s );

	/// Writes any store type (array) to the stream.
	template < typename type >
	inline Stream &operator <<( const Store<type> &s );

	/// Read/Writes QString.
	Stream &operator >>( QString &s );
	Stream &operator <<( const QString &s );

	/// \brief Reads data from the stream to a QVector object. 
	///
	/// This function only works for arrays which contain simple objects only, which means the object has no pointers.
	template < typename type >
	inline Stream &operator >>( QVector<type> &s )
	{
		int iSize = ReadInt();
		s.resize( iSize );
		Read( s.data(), iSize*sizeof(type) );
		return *this;
	};

	/// \brief Writes any store type (array) to the stream.
	///
	/// This function only works for arrays which contain simple objects only, which means the object has no pointers.
	template < typename type >
	inline Stream &operator <<( const QVector<type> &s )
	{
		(*this) << s.size();
		Write( s.data(), s.size()*sizeof(type) );
		return *this;
	};
};

/// Serializes any data type to/from the stream.
template < typename type >
inline Stream &operator ==( Stream &s, type &v ) { if ( s.IsStoring() ) s << v; else s >> v; return s; };

template < typename type >
inline Stream &operator ==( Stream &s, const type &v ) { if ( s.IsStoring() ) s << v; else s >> *((type*)&v); return s; };

template < typename type >
void Store<type>::Serialize( class Stream &s )
{
	if ( !s.IsStoring() )
		SetItemCount( s.ReadInt() );
	else
		s << m_iSize;
	for ( unsigned int i = 0; i < m_iSize; i++ )
		s == operator[]( i );
};

template < typename type >
inline Stream &Stream::operator <<( const Store<type> &o ) { (*this) << o.ItemCount(); Write( &(o[0]), sizeof(type)*o.ItemCount() ); return *this; };
template < typename type >
inline Stream &Stream::operator >>( Store<type> &o ) { unsigned int iSize; (*this) >> iSize; if ( o.SetItemCount( iSize ) && iSize ) Read( &(o[0]), sizeof(type)*iSize ); return *this; };

template < typename type >
inline void AttributeInstance<type>::Serialize( Stream &s ) { s == m_cValue; };
template < typename type >
inline void AttributePointer<type>::Serialize( Stream &s )
{
	if ( s.IsStoring() )
		s.WritePointer( AttributeInstance<type *>::Value() );
	else
		SetValue( dynamic_cast<type *>( s.ReadPointer() ) );
};

}; // end of namespace mudbox

