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


		#include "filesystem/io_memory.h"

		using namespace GS::IO;


//------------------------------------------------------------------------------
uint				Memory::GetCaps() const
{	return CanRead | CanWrite | CanSeek| IsCaseSensitive;	}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
Handle				*Memory::Open(const char *uri, Mode mode)
{
	switch (mode)
	{
		case ModeRead:
			ListForeachPtr(MemoryFile *, f, fat)
				if	(f->uri == uri)
					return new MemoryHandle(this, f, mode);
			break;

		case ModeWrite:
		{
			MemoryFile *f_entry = NULL;
			ListForeachPtr(MemoryFile *, f, fat)
				if	(f->uri == uri)
				{
					f_entry = f;
					break;
				}

			if	(!f_entry)
				fat.Add(f_entry = new MemoryFile(uri));

			return f_entry ? new MemoryHandle(this, f_entry, mode) : NULL;
		}
		break;

		default: break;
	}
	return NULL;
}
void				Memory::Close(Handle *h)
{	/* Nothing to be done. */	}

bool				Memory::Delete(const char *uri)
{
	ListForeachPtr(MemoryFile *, f, fat)
		if	(f->uri == uri)
			return fat.Remove(f);
	return false;
}

size_t				Memory::Tell(Handle *h)
{
	if	(MemoryHandle *_h = (MemoryHandle *)h)
		if	(_h->file)
			return _h->cursor;
	return (size_t)-1;	
}
size_t				Memory::Seek(Handle *h, ptrdiff_t offset, SeekRef seek_ref)
{
	if	(MemoryHandle *_h = (MemoryHandle *)h)
		if	(_h->file)
		{
			switch (seek_ref)
			{
				case SeekStart:
					_h->cursor = Types::Clamp <ptrdiff_t> (offset, 0, _h->file->size);
					break;
				case SeekCurrent:
					_h->cursor = Types::Clamp <ptrdiff_t> (_h->cursor + offset, 0, _h->file->size);
					break;
				case SeekEnd:
					_h->cursor = Types::Clamp <ptrdiff_t> (_h->file->size - offset, 0, _h->file->size);
					break;
			}
			return 0;
		}

	return (size_t)-1;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
size_t				Memory::Read(Handle *h, void *ptr, size_t size)
{
	size_t read_size = 0;
	if	(MemoryHandle *_h = (MemoryHandle *)h)
		if	(_h->file && (_h->mode == ModeRead))
		{
			read_size = Types::Min <ptrdiff_t> (size, _h->file->size - _h->cursor);
			GS::Memory::Copy(ptr, &_h->file->data[(int)_h->cursor], read_size);
			_h->cursor += read_size;
		}

	return read_size;
}
size_t				Memory::Write(Handle *h, const void *ptr, size_t size)
{
	size_t write_size = 0;
	if	(MemoryHandle *_h = (MemoryHandle *)h)
		if	(_h->file && (_h->mode == ModeWrite))
		{
			size_t req_size = _h->cursor + size;
			if	(req_size > _h->file->data.GetSize())
				if	(!_h->file->data.Reallocate(req_size + 16384))	// required size + 16 kilobytes
					return 0;

			GS::Memory::Copy(&_h->file->data[(int)_h->cursor], ptr, size);
			write_size = size;

			_h->cursor += size;
			_h->file->size = Types::Max(_h->file->size, req_size);
		}

	return write_size;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
MemoryHandle::MemoryHandle(Base *io, MemoryFile *_file, Mode _mode) : Handle(io), file(_file), cursor(0), mode(_mode)
{}
MemoryHandle::~MemoryHandle()
{	GetIOSystem()->Close(this);	}
//------------------------------------------------------------------------------
