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

// PURPOSE: A streamed data buffer.

#ifndef BD_STREAMBUF_H
#define BD_STREAMBUF_H

#include <bdCore/bdMemory/bdMemory.h>

/// A simple data buffer representation.
/// This class can be used by bdStreambuf to buffer reads and writes.
/// This class makes no attempt to allocate or deallocate the memory for
/// the buffer.
class bdBuffer
{
	public:

		BD_DECLARE_NEW_AND_DELETE_OPERATORS

		/// Default constructor.
		inline bdBuffer()
		: m_begin(BD_NULL),
		  m_end(BD_NULL),
		  m_next(BD_NULL)
		{
		}

		/// An accessor function for the beginning of the buffer.
		/// \return A pointer to the head of the buffer.
		inline bdNChar8* getBegin() const
		{
			return m_begin;
		}

		/// An accessor function for one past the end of the buffer.
		/// \return A pointer to the one past the end of the buffer.
		inline bdNChar8* getEnd() const
		{
			return m_end;
		}

		/// Returns the \a m_next pointer.
		/// \return A pointer to the place in the buffer that will be
		///  used next.
		/// \note This is typically a pointer to the next character to be read
		///  from the buffer, or the next slot to be written to in the buffer.
		inline bdNChar8* getNext() const
		{
			return m_next;
		}

		/// Returns the number of characters that can be stored in this buffer.
		/// \return The capacity of the buffer.
		inline bdUInt getCapacity() const
		{
			if (m_begin < m_end)
			{
				// 64bit compatability cast:
				// On a machine with 64bit address pointers the following sum
				// will produce a 64bit value. It's ok to cast down to 32bit
				// as the value cannot be that big.
				return static_cast<bdUInt>(m_end - m_begin);
			}

			return 0;
		}

		/// Returns the number of bytes currently unutilized in the buffer.
		/// \return The number of bytes not being used in the buffer.
		inline bdUInt getBytesLeft() const
		{
			if (m_next < m_end)
			{
				// 64bit compatability cast:
				// On a machine with 64bit address pointers the following sum
				// will produce a 64bit value. It's ok to cast down to 32bit
				// as the value cannot be that big.
				return static_cast<bdUInt>(m_end - m_next);
			}

			return 0;
		}

		/// Returns the number of bytes currently utlized in the buffer.
		/// \return The number of bytes being used in the buffer.
		inline bdUInt getBytesUsed() const
		{
			if (m_begin < m_next)
			{
				// 64bit compatability cast:
				// On a machine with 64bit address pointers the following sum
				// will produce a 64bit value. It's ok to cast down to 32bit
				// as the value cannot be that big.
				return static_cast<bdUInt>(m_next - m_begin);
			}

			return 0;
		}

	public:

		/// Pointer to the start of the buffer.
		bdNChar8 *m_begin;

		/// Pointer to one past the end of the buffer.
		bdNChar8 *m_end;

		/// Pointer to the next character in the buffer.
		bdNChar8 *m_next;
};

/// An abstract base class for deriving stream buffer objects.
/// This base class provides two character buffers, m_getBuffer for buffered
/// reads, and m_putBuffer for buffered writes. To use these buffers they should
/// be setup using setGetBuffer() and/or setPutBuffer(). If not the stream
/// buffer will use unbuffered read and/or writes. A bdStreambuf derived object
/// may use both, either, or none of these buffers.
/// The class has two distinct interfaces. The public members are used,
/// typically by i/o streams (e.g. bdOStream and bdIStream), to perform high
/// level operations on the streambuf (e.g. reading, writing or seeking). The
/// protected virtual members are designed to be overridden in derived classes,
/// where they should preform the physical input and output operations.
class bdStreambuf
{
	public:

		BD_DECLARE_NEW_AND_DELETE_OPERATORS

		/// Defines the starting point for seek operations.
		enum SeekFrom
		{
			BEG,
			CUR,
			END
		};

		/// Defines the possible error states of the bdStreambuf.
		enum StatusFlags
		{
			END_OF_FILE	= (1 << 0),
			WRITE_ERROR = (1 << 1)
		};

		/// Default constructor.
		/// Creates an unbuffered bdStreambuf.
		inline bdStreambuf();

		/// Virtual destructor.
		virtual ~bdStreambuf();

		/// Checks the status of the bdStreambuf.
		/// \return
		///  - False if an error (including \a END_OF_FILE) has occured.
		///  - True otherwise.
		inline bdBool isOk() const;

		/// Check if it is ok to read from the stream.
		/// \return
		///  - True if is is ok to read from the buffer.
		///  - False otherwise.
		inline bdBool isOkToRead() const;

		/// Sets the buffer used for buffered reads.
		/// The bdStreambuf will make no attempt to release the memory
		/// associated with the buffer.
		/// \param buffer[in] The buffer to use when reading from this stream.
		/// \param capacity[in] The size of the buffer being passed in.
		inline void setGetBuffer(bdNChar8 *const buffer,
							     const bdUInt capacity);

		/// Sets the buffer used for buffered writes.
		/// The bdStreambuf will make no attempt to release the memory
		/// associated with the buffer.
		/// \param buffer[in] The buffer to use when writing from this stream.
		/// \param capacity[in] The size of the buffer being passed in.
		inline void setPutBuffer(bdNChar8 *const buffer,
							     const bdUInt capacity);

		/// Attempts to copy nbytes from the bdStreambuf into the array
		/// pointed to by dest.
		/// \param dest[out] The buffer to write the bytes into.
		/// \param nbytes[in] The number of bytes to try and read from this
		///  buffer into the dest buffer.
		/// \return The number of bytes read.
		/// \note This will set the \a END_OF_FILE flag in \a m_flags if the number
		/// of bytes read is not equal to \a nbytes.
		inline bdUInt getFrom(bdNChar8 *const dest,
								const bdUInt nbytes);

		/// Attempts to copy nbytes from the array pointed to by src
		/// into the bdStreambuf.
		/// \param src[in] The buffer to read the bytes from.
		/// \param nbytes[in] The number of bytes to try and read from the src
		///  buffer into this buffer.
		/// \return The number of bytes read.
		/// \note This will set the \a WRITE_ERROR flag in \a m_flags if the number
		///  of bytes written is not equal to \a nbytes.
		inline bdUInt putTo(const bdNChar8 *const src,
							  const bdUInt nbytes);

		/// Attempts to seek to the given offset.
		/// \param offset[in]	The new position in the buffer, relative to
		///						\a from.
		/// \param from[in] The point to start seeking from.
		/// \return
		///  - True if the seek was successful.
		///  - False if unsuccessful (or not supported).
		/// \note This operation is not supported by default.
		/// \note In order to support seeking the bdStreambuf
		///  derived class must overload bufSeek().
		inline bdBool seek(const bdInt offset,
						   const SeekFrom from);

		/// Attempts to get the current read or write position.
		/// \param offset[out] The current position in the stream.
		/// \return
		///  - True if the tell was successful.
		///  - False otherwise.
		/// \note This operation is not supported by default.
		/// \note In order to support telling the bdStreambuf
		///  derived class must overload bufTell().
		inline bdBool tell(bdUInt &offset) const;

		/// Flushes any buffered reads or writes.
		/// bufFlush is also called so the derived classes can force reads
		/// and writes to the i/o device.
		inline void flush();

		/// Checks to see if the end of file has been reached.
		/// \return
		///  - True if the \a END_OF_FILE flag is set.
		///  - False otherwise.
		inline bdBool endOfFile() const;

	protected:

		/// Performs actual reads from the i/o device.
		/// This is called primarily by getFrom. It must be overridden in
		/// any bdStreambuf described classes.
		/// \param dest[out] Put the requested data into this location.
		/// \param nbytes[in] The number of bytes to get from the buffer.
		/// \return The number of bytes the function actually read.
		virtual bdUInt bufGetFrom(bdNChar8 *const dest,
									const bdUInt nbytes) = 0;

		/// Performs actual writes to the i/o device.
		/// This is called primarily by putTo and flush. It must be overridden
		/// in any bdStreambuf described classes.
		/// \param src[in] Get the required data from this location.
		/// \param nbytes[in] The number of bytes to try and write to the buffer.
		/// \return The number of bytes the function actually wrote.
		virtual bdUInt bufPutTo(bdNChar8 *const src,
								  const bdUInt nbytes) = 0;

		/// This method should attempt to seek to the specified \a offset
		/// within the i/o devices data stream.
		/// \param offset[in] The offset to use when seeking.
		/// \param from[in] The place to start seeking.
		/// \return
		///  - True if the seek was successful.
		///  - False otherwise.
		/// \note The default implementation does nothing and returns false.
		virtual bdBool bufSeek(const bdInt offset,
							   const SeekFrom from);

		/// This method should attempt to set \a offset to the current
		/// read/write position within the i/o devices data stream.
		/// \param offset The offset to set.
		/// \return
		///  - True if the tell was successful.
		///  - False otherwise.
		/// \note The default implementation does nothing, returns false, and
		///  offset is left unchanged.
		virtual bdBool bufTell(bdUInt &offset) const;

		/// This method should flush any reads or writes internally buffered
		/// by the i/o device.
		/// \note The default implementation does nothing.
		virtual void bufFlush();

	protected:

		/// The bdStreambuf error/status flags.
		bdUInt m_flags;

		/// The buffer used to buffer reads.
		bdBuffer m_getBuffer;

		/// The buffer used to buffer writes.
		bdBuffer m_putBuffer;
};

#include <bdCore/bdStreams/bdStreambuf.inl>

#endif // BD_STREAMBUF_H
