//===========================================================================//
// File:	memstrm.cpp                                                         //
// Project: MUNGA           Brick: Resource Manager		                    //
// Contents: Implementation Details of resource management                   //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
//---------------------------------------------------------------------------//
// Copyright (C) 1994-1995, Virtual World Entertainment, Inc.                //
//                      PROPRIETARY AND CONFIDENTIAL                         //
//===========================================================================//

#include <munga.hpp>
#pragma hdrstop

#if !defined(MEMSTRM_HPP)
	#include <memstrm.hpp>
#endif

//#############################################################################
//##########################    MemoryStream    ###############################
//#############################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MemoryStream::MemoryStream(
	void *stream_start,
	size_t stream_size,
	size_t initial_offset
)
{
	Check_Pointer(this);

	streamStart = (char*)stream_start;
	streamSize = stream_size;
	Verify(initial_offset <= stream_size);
	currentPosition = streamStart + initial_offset;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MemoryStream&
	MemoryStream::ReadBytes(
		void *ptr,
		size_t number_of_bytes
	)
{
	Check(this);
	Check_Pointer(ptr);
	Verify(number_of_bytes > 0);
	Verify(GetBytesRemaining() >= number_of_bytes);

	Mem_Copy(ptr, GetPointer(), number_of_bytes, number_of_bytes);
	AdvancePointer(number_of_bytes);
	return *this;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MemoryStream&
	MemoryStream::WriteBytes(
		const void *ptr,
		size_t number_of_bytes
	)
{
	Check(this);
	Check_Pointer(ptr);
	Verify(number_of_bytes > 0)

	Mem_Copy(GetPointer(), ptr, number_of_bytes, GetBytesRemaining());
	AdvancePointer(number_of_bytes);
	return *this;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MemoryStream&
	MemoryStream_Write(
		MemoryStream *stream,
		istream *input
	)
{
	Check(stream);
	Check_Pointer(input);

	Verify(!input->bad());
	input->clear();

	//
	//--------------------------------------------------------
	// If we are reading a binary stream, read it in one chunk
	//--------------------------------------------------------
	//
	#if defined(LBE4)
		if (input->flags() & ios::binary)
		{
	#endif
			input->seekg(0, ios::end);
			int len=input->tellg();
			input->clear();
			input->seekg(0);

			if (!stream->AllocateBytes(len))
			{
				Fail("Not enough memory remaining in stream!");
			}
			input->read((char*)stream->GetPointer(), len);
			stream->AdvancePointer(len);
	#if defined(LBE4)
		}

		//
		//----------------------------------------------------------------
		// Read a text file character by character if we are a DOS machine
		//----------------------------------------------------------------
		//
		else
		{
			input->clear();
			input->seekg(0);
			unsigned char byte;

			while (input->get(byte))
			{
				*stream << byte;
			}
		}
	#endif
	return *stream;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MemoryStream&
	MemoryStream_Write(
		MemoryStream *stream,
		const MemoryStream *input_stream
	)
{
	Check(stream);
	Check(input_stream);

	return
		stream->WriteBytes(
			input_stream->GetPointer(),
			input_stream->GetBytesRemaining()
		);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	MemoryStream::TestInstance() const
{
	return size_t(currentPosition - streamStart) <= streamSize;
}

//#############################################################################
//######################    DynamicMemoryStream    ############################
//#############################################################################

//
// calculate the allocation size for stream
//
inline size_t
	Calculate_Buffer_Size(size_t needed)
{
	return (needed+0x80)&~0x7F;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DynamicMemoryStream::DynamicMemoryStream(size_t stream_size):
	MemoryStream(NULL, stream_size)
{
	Check_Pointer(this);

	streamSize = Calculate_Buffer_Size(stream_size);
	streamStart = new char[streamSize];
	Register_Pointer(streamStart);

	currentPosition = streamStart;
	streamLength = 0L;
	ownsStream = True;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DynamicMemoryStream::DynamicMemoryStream(
	void *stream_start,
	size_t stream_size,
	size_t initial_offset
):
	MemoryStream(stream_start, stream_size, initial_offset)
{
	Check_Pointer(this);
	ownsStream = False;
	streamLength = stream_size;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DynamicMemoryStream::~DynamicMemoryStream()
{
	Check(this);
	if (ownsStream)
	{
		Unregister_Pointer(streamStart);
		delete[] streamStart;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	DynamicMemoryStream::AllocateBytes(size_t count)
{
	Check(this);

	if (ownsStream && MemoryStream::GetBytesRemaining() < count)
	{
		size_t current_offset = currentPosition - streamStart;
		size_t new_stream_size =
			streamSize + count - MemoryStream::GetBytesRemaining() + 1;
		new_stream_size = Calculate_Buffer_Size(new_stream_size);
		char *new_stream_start = new char[new_stream_size];
		Register_Pointer(new_stream_start);

		Mem_Copy(new_stream_start, streamStart, streamSize, new_stream_size);
		Unregister_Pointer(streamStart);
		delete[] streamStart;
		streamStart = new_stream_start;
		streamSize = new_stream_size;
		currentPosition = streamStart + current_offset;
	}
	return True;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MemoryStream&
	DynamicMemoryStream::WriteBytes(
		const void *ptr,
		size_t number_of_bytes
	)
{
	Check(this);
	Check_Pointer(ptr);
	Verify(number_of_bytes > 0)

	//
	//--------------------------------------------------------------------
	// If we own the stream, and we don't have enough memory to accept the
	// write, copy the buffer and make a new stream that is bigger
	//--------------------------------------------------------------------
	//
	AllocateBytes(number_of_bytes);
	Mem_Copy(
		GetPointer(),
		ptr,
		number_of_bytes,
		MemoryStream::GetBytesRemaining()
	);
	AdvancePointer(number_of_bytes);

	streamLength = Max(streamLength, GetBytesUsed());
	return *this;
}

