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

#include <bdPlatform/bdPlatformMemory/bdPlatformMemory.h>

inline 
bdStreambuf::bdStreambuf()
: m_flags(0)
{
}

inline
bdBool bdStreambuf::isOk() const
{
	return m_flags == 0;
}

inline 
bdBool bdStreambuf::isOkToRead() const
{
	// cannot read is we are the the end of the file and the get buffer
	// is empty
	return !((m_flags & END_OF_FILE) && (m_getBuffer.getBytesLeft() == 0));
}

inline 
void bdStreambuf::setGetBuffer(bdNChar8 *const buffer,
							   const bdUInt capacity)
{
	// For the get buffer initially m_next == m_end
	// so the buffer is full. This forces the initial call
	// to bufGetFrom.
	m_getBuffer.m_begin = buffer;
	m_getBuffer.m_end = m_getBuffer.m_begin + capacity;
	m_getBuffer.m_next = m_getBuffer.m_end;	
}

inline 
void bdStreambuf::setPutBuffer(bdNChar8 *const buffer,
							   const bdUInt capacity)
{
	// For the put buffer initially m_next == m_begin
	// so the buffer is empty.
	m_putBuffer.m_begin = buffer;
	m_putBuffer.m_end = m_putBuffer.m_begin + capacity;
	m_putBuffer.m_next = m_putBuffer.m_begin;	
}

inline 
bdUInt bdStreambuf::getFrom(bdNChar8 *const dest, 
							  const bdUInt nbytes)
{
	bdUInt bytesRead = 0;

	if (m_getBuffer.getCapacity() == 0)
	{
		// unbuffered
		bytesRead = bufGetFrom(dest, nbytes);
		if (bytesRead != nbytes)
		{
			// EOF
			m_flags |= END_OF_FILE;
		}
	}
	else
	{
		// buffered
		bdUInt bytesLeft = nbytes;

		while (bytesLeft)
		{
			if ((m_getBuffer.getBytesLeft() == 0) && !endOfFile()) 
			{
				// have read all the bytes in the buffer, so refill it.
				const bdUInt bufferedBytes = bufGetFrom(m_getBuffer.m_begin, m_getBuffer.getCapacity());
				m_getBuffer.m_next = m_getBuffer.m_begin;
								
				if (bufferedBytes < m_getBuffer.getCapacity())
				{
					// EOF
					m_getBuffer.m_end = m_getBuffer.m_begin + bufferedBytes;
					m_flags |= END_OF_FILE;
				}
			}

			const bdUInt bytesInBuffer = m_getBuffer.getBytesLeft();

			if (bytesInBuffer == 0)
			{
				// we must have reached the eof, otherwise the buffer would have
				// been filled or partially filled above.
				break;
			}

			const bdUInt bytesToRead  = BD_MIN(bytesInBuffer, bytesLeft);	
			bdMemcpy(dest + bytesRead, m_getBuffer.m_next, bytesToRead);

			m_getBuffer.m_next += bytesToRead;			
			bytesRead += bytesToRead;
			bytesLeft -= bytesToRead;
		}
	}

	return bytesRead;
}


inline 
bdUInt bdStreambuf::putTo(const bdNChar8 *const src, 
							const bdUInt nbytes)
{
	bdUInt bytesWritten = 0;

	if (m_putBuffer.getCapacity() == 0)
	{
		// unbuffered
		bytesWritten = bufPutTo(const_cast<bdNChar8*>(src), nbytes);
		if (bytesWritten != nbytes)
		{
			// WRITE ERROR
			m_flags |= WRITE_ERROR;
		}
	}
	else
	{
		// buffered
		bdUInt bytesLeft = nbytes;

		while (bytesLeft)
		{
			if (m_putBuffer.getBytesLeft() == 0) 
			{
				// the buffer is full so write it out.
				const bdUInt debufferedBytes = bufPutTo(m_putBuffer.m_begin, m_putBuffer.getBytesUsed());
												
				if (debufferedBytes < m_putBuffer.getBytesUsed())
				{
					// WRITE ERROR
					m_flags |= WRITE_ERROR;
					break;
				}

				m_putBuffer.m_next = m_putBuffer.m_begin;
			}

			const bdUInt bytesInBuffer = m_putBuffer.getBytesLeft();

			const bdUInt bytesToWrite  = BD_MIN(bytesInBuffer, bytesLeft);
			bdMemcpy( m_putBuffer.m_next, src + bytesWritten, bytesToWrite);			

			m_putBuffer.m_next += bytesToWrite;			
			bytesWritten += bytesToWrite;
			bytesLeft -= bytesToWrite;
		}
	}

	return bytesWritten;
}

inline 
bdBool bdStreambuf::seek(const bdInt offset, 
				   const SeekFrom from)
{
	// flush the buffers to be safe
	flush();
	bdBool success = bufSeek(offset, from);
	if (success)
	{
		// resets end of file flag if successful
		m_flags &= (~END_OF_FILE);
	}

	return success;
}

inline 
bdBool bdStreambuf::tell(bdUInt &offset) const
{
	return bufTell(offset);
}

inline 
void bdStreambuf::flush()
{
	// flush the put buffer
	if (m_putBuffer.getBytesUsed() > 0)
	{
		bdUInt written = bufPutTo(m_putBuffer.m_begin, m_putBuffer.getBytesUsed() );
		if (written != m_putBuffer.getBytesUsed() )
		{
			m_flags |= WRITE_ERROR;
		}

		m_putBuffer.m_next = m_putBuffer.m_begin;
	}

	// flush the get buffer
	m_getBuffer.m_next = m_getBuffer.m_end;

	bufFlush();
}

inline 
bdBool bdStreambuf::endOfFile() const
{
	return m_flags & END_OF_FILE;
}
