/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include "filesystem/io_buffer.h"
		#include "log/log.h"
		#include "ntypes.h"

		using namespace GS::IO;


//------------------------------------------------------------------------------
uint		Buffer::GetCaps() const { return io->GetCaps(); }

Handle		*Buffer::Open(const char *path, Mode mode)
{
	AutoPtr <Handle> io_h(io->Open(path, mode));
	if	(io_h.IsNull())
		return NULL;

	AutoPtr <BufferHandle> b_h(new BufferHandle(this, io_h));
	if	(b_h.IsNull())
		return NULL;

	if	(!b_h->read_buffer.buffer.Allocate(read_buffer_size))
		return NULL;

	b_h->size = io_h->GetSize();

	io_h.Detach();
	return b_h.Detach();
}
void		Buffer::Close(Handle *h)
{
	if	(BufferHandle *b_h = (BufferHandle *)h)
		b_h->handle = NULL;
}

bool		Buffer::Delete(const char *path) { return io->Delete(path); }

size_t		Buffer::Tell(Handle *h) { return ((BufferHandle *)h)->pos; }
size_t		Buffer::Seek(Handle *h, ptrdiff_t offset, SeekRef seek)
{
	if	(BufferHandle *c_h = (BufferHandle *)h)
	{
		switch (seek)
		{
			case SeekStart:
				c_h->pos = Types::Clamp <ptrdiff_t> (offset, 0, c_h->size);
				break;
			case SeekCurrent:
				c_h->pos = Types::Clamp <ptrdiff_t> (c_h->pos + offset, 0, c_h->size);
				break;
			case SeekEnd:
				c_h->pos = Types::Clamp <ptrdiff_t> (c_h->size + offset, 0, c_h->size);
				break;
		}
		return 0;
	}
	return size_t(-1);
}
size_t		Buffer::Read(Handle *h, void *data, size_t size)
{
// __LOG_V__ << "Buffer::Read: size = " << size << ".\n";

	size_t read_size = 0;
	if	(BufferHandle *b_h = (BufferHandle *)h)
	{
		while (read_size < size)
		{
			ptrdiff_t buffer_pos = (b_h->pos + read_size) - b_h->read_buffer.start_pos;

			if	((buffer_pos >= 0) && (buffer_pos < ptrdiff_t(b_h->read_buffer.usage)))
			{
				ptrdiff_t copy_size = Types::Min <ptrdiff_t> (b_h->read_buffer.usage - buffer_pos, size - read_size);
				Memory::Copy((char *)data + read_size, b_h->read_buffer.buffer.c_ptr() + buffer_pos, copy_size);
// __LOG_V__ << "Buffer read: pos = " << buffer_pos << ", size = " << copy_size << ".\n";
				read_size += copy_size;
			}
			else	// refill cache
			{
				b_h->read_buffer.start_pos = b_h->pos + read_size;
				if	(b_h->read_buffer.start_pos >= b_h->size)
				{
					b_h->read_buffer.usage = 0;
					break;	// EJ 01/05: Improves EOF performance for the OGG streaming interface which makes many out of bound calls.
				}

				io->Seek(b_h->handle, b_h->read_buffer.start_pos, SeekStart);
				b_h->read_buffer.usage = io->Read(b_h->handle, b_h->read_buffer.buffer.c_ptr(), b_h->read_buffer.buffer.GetSize());
// __LOG_V__ << "Buffer fill: pos = " << b_h->read_buffer.start_pos << ", size = " << b_h->read_buffer.usage << ".\n";
				if	(b_h->read_buffer.usage == 0)
					break;	// EOF
			}
		}
		b_h->pos += read_size;
	}
	return read_size;
}
size_t		Buffer::Write(Handle *h, const void *data, size_t size)
{
	if	(BufferHandle *b_h = (BufferHandle *)h)
	{
		size_t write_size = io->Write(b_h->handle, data, size);
		b_h->pos += write_size;
		return write_size;
	}
	return 0;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
GS::String	Buffer::Hash(const char *path)
{	return io->Hash(path);	}	// [EJ] Do not perform a FileLoad here, IO::Buffer is typically sitting in front of a slow IO backend which potentially optimizes hash transfer.
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool		Buffer::MkDir(const char *path)
{	return io->MkDir(path);	}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
Buffer::Buffer(Base *base, size_t read_size, size_t write_size) : io(base)
{
	read_buffer_size = Types::Clamp <size_t> (read_size, 0, Units::MB(16));
	write_buffer_size = Types::Clamp <size_t> (write_size, 0, Units::MB(16));
}
//------------------------------------------------------------------------------
