// DWBS ------------------------------------------------------------------ DWBS
//
// PLATFORM		: ALL
// PRODUCT		: BIT_DEMON
// VISIBILITY	: PUBLIC
//
// DWBS ------------------------------------------------------------------ DWBS

// PURPOSE: Provides functions for reading and interpreting a bdStreamBuf.

#ifndef BD_I_STREAM_H
#define BD_I_STREAM_H

#include <bdCore/bdMemory/bdMemory.h>
#include <bdCore/bdString/bdString.h>

#include <bdCore/bdStreams/bdStreambuf.h>

/// Provides member functions to assist in reading and interpreting
/// data from an bdStreambuf. The operator>> functions provide mechanisms
/// to read formatted data, interpreted from the sequence of characters
/// in the bdStreambuf. While most of the other functions are used to perform
/// unformatted input.
class bdIStream
{
	public:

		BD_DECLARE_NEW_AND_DELETE_OPERATORS

		/// Constructor.
		/// Initializes an input stream with a given bdStreambuf for reading.
		/// \param streambuf[in]	The bdStreamBuffer to use when constructing
		///							this object.
		bdIStream(bdStreambuf* streambuf);

		/// Check the status of this input stream and its bdStreambuf.
		/// \return
		/// -	True, if no errors have occured.
		/// -	False, if errors did occur.
		bdBool isOk() const;

		/// Attempt to read a signed character from the bdStreambuf.
		/// \param c[out]	The character that is read from the buffer.
		/// \return			A reference to \a this stream.
		/// \note			\a c will remain unchanged if isOk() returns false.
		bdIStream& operator>> (bdNChar8 &c);

		///// Attempt to read an unsigned character from the bdStreambuf.
		///// \param c[out]	The character that is read from the buffer.
		///// \return			A reference to \a this stream.
		///// \note			\a c will remain unchanged if isOk() returns false.
		//bdIStream& operator>> (bdUChar8 &c);

		/// Attempt to read a signed 16 bit integer from the bdStreambuf.
		///	\param	i[out]	The integer that is read from the buffer.
		/// \return			A reference to \a this stream.
		/// \note			\a i will remain unchanged if isOk() returns false.
		bdIStream& operator>> (bdInt16 &i);

		/// Attempt to read an unsigned 16 bit integer from the bdStreambuf.
		///	\param	u[out]	The unsigned integer that is read from the buffer.
		/// \return			A reference to \a this stream.
		/// \note			\a u will remain unchanged if isOk() returns false.
		bdIStream& operator>> (bdUInt16 &u);

		/// Attempt to read a signed 32 bit integer from the bdStreambuf.
		///	\param	i[out]	The integer that is read from the buffer.
		/// \return			A reference to \a this stream.
		/// \note			\a i will remain unchanged if isOk() returns false.
		bdIStream& operator>> (bdInt &i);

		/// Attempt to read an unsigned 32 bit integer from the bdStreambuf.
		///	\param	u[out]	The integer that is read from the buffer.
		/// \return			A reference to \a this stream.
		/// \note			\a u will remain unchanged if isOk() returns false.
		bdIStream& operator>> (bdUInt &u);

		/// Attempt to read a signed 64 bit integer from the bdStreambuf.
		///	\param	i[out]	The integer that is read from the buffer.
		/// \return			A reference to \a this stream.
		/// \note			\a i will remain unchanged if isOk() returns false.
		bdIStream& operator>> (bdInt64 &i);

		/// Attempt to read an unsigned 64 bit integer from the bdStreambuf.
		///	\param	u[out]	The integer that is read from the buffer.
		/// \return			A reference to \a this stream.
		/// \note			\a u will remain unchanged if isOk() returns false.
		bdIStream& operator>> (bdUInt64 &u);

		/// Attempt to read a 32 bit floating point value from the bdStreambuf.
		///	\param	f[out]	The float that is read from the buffer.
		/// \return			A reference to \a this stream.
		/// \note			\a f will remain unchanged if isOk() returns false.
		bdIStream& operator>> (bdFloat32 &f);

		/// Attempt to read a 64 bit floating point value from the bdStreambuf.
		///	\param	f[out]	The float that is read from the buffer.
		/// \return			A reference to \a this stream.
		/// \note			\a f will remain unchanged if isOk() returns false.
		bdIStream& operator>> (bdFloat64 &f);

		/// Attempt to read a string from the bdStreambuf.
		/// Any leading white space is skipped and the string is deemed to
		/// terminate on the first white space character or '\\0' after any
		/// initial white space.
		/// \param	s[in]	A pointer to the area in memory that the string in
		///					the buffer will be written to.
		/// \return			A reference to \a this stream.
		/// \note			Ensure \a *s is large enough to hold the expected
		///					string plus a null terminating '\\0'. Alternatively
		///					 use operator>> (bdString &s).
		/// \note			\a *s will remain unchanged if isOk() returns false.
		bdIStream& operator>> (bdNChar8 *s);

		///// Attempt to read a string from the bdStreambuf.
		///// Any leading white space is skipped and the string is deemed to
		///// terminate on the first white space character or '\\0' after any
		///// initial white space.
		///// \param	s[out]	A pointer to the area in memory that the string in
		/////					the buffer will be written to.
		///// \return			A reference to \a this stream.
		///// \remark			Identical to operator>> (bdChar8 *const s).
		///// \note			Ensure \a *s is large enough to hold the expected
		/////					string plus a null terminating '\\0'. Alternatively
		/////					use operator>> (bdString &s).
		///// \note			\a *s will remain unchanged if isOk() returns false.
		//bdIStream& operator>> (bdUChar8 *const s);

		/// Attempt to read a string from the bdStreambuf.
		/// Any leading white space is skipped and the string is deemed to
		/// terminate on the first white space character or '\\0' after any initial
		/// white space. This method can read strings of any length.
		/// \param	s[out]	The string to write the string in the buffer to.
		/// \return			A reference to \a this stream.
		/// \note			\a s will remain unchanged if isOk returns false.
		bdIStream& operator>> (bdString &s);

		/// Attempt to read a boolean from the bdStreambuf.
		/// Boolean values are deemed to be '0' or '1'.
		/// \param	b[out]	The boolean which may be found.
		/// \return			A reference to \a this stream.
		/// \note			\a b will remain unchanged if isOk() returns false
		///					or the next character in the bdStreambuf (after
		///					any initial white space) is not '1' or '0'.
		bdIStream& operator>> (bdBool &b);

		/// Attempt to read a delimited line from the bdStreambuf.
		/// Extracts characters from the bdStreambuf and copies them into
		/// \a buf. Characters are copied until one of the delimiter characters
		/// is extracted from the bdStreambuf or \a bufSize-1 characters have
		/// been copied. A null terminator is always appended to the data
		/// extracted from the bdStreambuf, delimiters are not copied.
		/// If no delimiters are specified '\n' is used.
		/// \param buf[in,out] A buffer into which the extracted characters
		///					   should be copied.
		///	\param bufSize[in] The size of \a buf in bytes.
		/// \param delimiters[in] A pointer a string of delimiters.
		/// \return			A reference to \a this stream.
		bdIStream& getLine(bdNChar8 *const buf,
						   const bdUInt bufSize,
						   const bdNChar8 *delimiters = BD_NULL);

		/// Attempt to read unformatted input from the bdStreambuf.
		/// Tries to read \a nbytes characters from the underlying bdStreambuf
		/// and places them in successive locations in the array pointed
		/// to by \a buf.
		/// \param	buf[out]	The location to write the data that is read
		///						from the buffer to.
		/// \param	nbytes[in]	The number of bytes to read from the buffer.
		/// \return				The number of bytes actually read from the
		///						bdStreambuf.
		/// \note				If this differs from \a nbytes an error has
		///						occured or the \a END_OF_FILE has been reached.
		/// \note				*buf will remain unchanged if isOk() returns
		///						false.
		bdUInt read(bdNChar8 *const buf,
					  const bdUInt nbytes);

		/// Attempt to set the next location to be read from the bdStreambuf.
		/// This operation is only supported if the underlying bdStreambuf
		/// supports the bufSeek() seek operation.
		/// \param	offset[in]	The number of bytes to move starting at \a from.
		/// \param	from[in]	The initial position from which to move the read
		///						position by \a offset bytes.
		/// \return
		/// -	True, if the operation was successful.
		/// -	False, if the operation was unsuccessful.
		/// \note				There are 2 main reasons why this operation may
		///						fail. Firstly if isOk() returned false, and
		///						secondly the operation is not supported by the
		///						underlying bdStreamBuf.
		/// \todo				Improve the description of from.
		bdBool seek(const bdUInt offset,
					const bdStreambuf::SeekFrom from);

		/// Attempt to set \a offset to the offset of the next location to be
		/// read from the bdStreamBuf.
		/// The offset returned if relative to the beginning of the bdStreamBuf.
		/// \param	offset[out]	The value of the next read location in the buffer.
		/// \return
		/// -	True, if the operation was successful.
		/// -	False, if the operation was unsuccessful.
		// \note				There are 2 main reasons why this operation may
		///						fail. Firstly if isOk() returned false, and
		///						secondly the operation is not supported by the
		///						underlying bdStreamBuf.
		bdBool tell(bdUInt &offset) const;

		/// Get the underlying buffer.
		/// \return	A pointer to the underlying bdStreambuf.
		bdStreambuf* getStreambuf();

		/// Defines the error states of a bdIStream.
		/// \c ERROR_BIT indicates an internal error has occured. Typically
		/// such errors occur in the \c bdStrto*() functions.
		/// \c INPUT_VALUE_OUT_OF_RANGE indicates that an interpreted value,
		/// read using one of the \c operator>>(T &v) functions, is not in
		/// the valid range for the data type \c T.
		enum StatusFlags
		{
			ERROR_BIT					= (1 << 0),
			INPUT_VALUE_OUT_OF_RANGE	= (1 << 1)
		};

	protected:

		/// Extract characters from the underlying bdStreambuf until
		/// a non-whitespace character is read. Upon return \a c will
		/// be equal to the first non white space character read.
		/// \param	c[out]	A pointer to the point in the buffer where the first
		///					non-whitespace character was found.
		/// \note			\a c will remain unchanged if the \a END_OF_FILE is
		///					reached before a non white space character is read.
		void skipWhiteSpace(bdNChar8 &c);

		/// Parse a sequence of characters that represent an integer value
		/// (signed or unsigned) into the character array pointed to by \a s.
		/// \param	s[out]		A pointer to the location where the integer will be
		///						written.
		/// \param	maxlen[in]	The maximum number of characters to write to \a s.
		/// \return				The number of bytes written to the string \a s.
		bdUInt getIntegerString(bdNChar8* s,
								  const bdUInt maxlen);

		/// Parse a sequence of characters that represent the exponential part
		/// of a floating point value into the character array pointed
		/// to by \a s.
		/// \param	s[out]		A pointer to the location where the value will
		///						be written.
		/// \param	maxlen[in]	The maximum number of characters to write to
		///						write to \a s.
		/// \param	expChar[in]	The character to indicate that the string
		///						is an exponential. The default value is 'e'.
		/// \return				The number of bytes written to the string \a s.
		bdUInt getExpString(bdNChar8* s,
							  const bdUInt maxlen,
							  bdNChar8 expChar);

		/// Parse a sequence of characters that represent a floating point
		/// value into the character array pointed to by \a s.
		/// \param	s[out]		A pointer to the location where the float will be
		///						written.
		/// \param	maxlen[in]	The maximum number of characters to write to \a s.
		/// \return				The number of bytes written to the string \a s.
		bdUInt getFloatString(bdNChar8* s,
								const bdUInt maxlen);

	protected:

		/// The underlying bdStreambuf.
		bdStreambuf *m_streambuf;

		/// Status flags for this bdIStream see the StatusFlags enumeration.
		bdUInt m_flags;
};

#endif // BD_I_STREAM_H
