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


		#include "filesystem/data_store.h"
		#include "rand/rand.h"
		#include "memory/nauto_ptr.h"

		using namespace GS;
		using namespace GS::IO;


//------------------------------------------------------------------------------
DataStore::Entry *DataStore::GetEntry(const String &id) const
{
	ListForeachPtr(Entry *, e, entries)
		if	(e->id == id)
			return e;
	return NULL;
}
String		DataStore::GetNewId()
{
	for ( ; ; ++id_seed)
	{
		String id = String::Format("store_%012d", id_seed);
		if	(GetEntry(id) == NULL)
			return id;
	}
	return String();
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
size_t		DataStore::GetEntrySize(const String &id) const
{
	if	(Entry *e = GetEntry(id))
		return e->size;
	return 0;
}
size_t		DataStore::GetStoreSize() const
{
	size_t size = 0;
	ListForeachPtr(Entry *, e, entries)
		size += e->size;
	return size;
}
size_t		DataStore::GetFreeStore() const
{	return limit - GetStoreSize();	}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
String		DataStore::Reserve(size_t size)
{
	Threading::MutexLock lock(&mutex);

	if	(limit > 0)	// enforce size limit
	{
		size_t store_size = GetStoreSize();
		if	(size > (limit - store_size))
			return String(); // store is full
	}

	// Reserve a new entry.
	Entry *entry = new Entry;

	entry->id = GetNewId();
	entry->size = size;
	entries.Add(entry);

	return entry->id;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool		DataStore::Store(const String &id, const void *data, size_t size, const char *user)
{
	Threading::MutexLock lock(&mutex);

	Entry *entry = GetEntry(id);
	if	((entry == NULL) || (entry->size != size))
		return false;	// invalid id/store size

	lock.Unlock();

	entry->user = user;

	AutoPtr <Handle> h(io->Open(id, ModeWrite));
	return h.IsValid() && (h->Write(data, size) == size);
}
bool		DataStore::Free(const String &id)
{
	Threading::MutexLock lock(&mutex);

	Entry *entry = GetEntry(id);
	if	(entry == NULL)
		return false;	// invalid id

	if	(!io->Delete(id))
		return false;

	return entries.Remove(entry);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// Store format
//
// 18 bytes	-	id
//  4 bytes -	size in bytes
// -----------------------------
bool		DataStore::Load(const char *path)
{
	AutoPtr <Handle> h(io->Open(path));
	if	(h.IsNull())
		return true; // nothing to restore

	entries.Clear();

	char id[18];
	while (!h->IsEOF())
	{
		if	(h->Read((void *)id, 18) != 18)
			return false;

		Entry *e = new Entry;

		e->id.Set(id, id + 18);
		e->size = h->Read <size_t> ();

		uint user_data_size = h->Read <uint> ();
		if	(!e->user.Allocate(user_data_size))
			return false;

		h->Read((void *)e->user.c_str(), user_data_size);

		entries.Add(e);
	}
	return true;
}
bool		DataStore::Save(const char *path)
{
	AutoPtr <Handle> h(io->Open(path, ModeWrite));
	if	(h.IsNull())
		return false;

	ListForeachPtr(Entry *, e, entries)
	{
		h->Write(e->id.c_str(), 18);
		h->Write(&e->size, 4);

		uint user_data_size = e->user.Len();
		h->Write(user_data_size);
		if	(user_data_size > 0)
			h->Write(e->user.c_str(), user_data_size);
	}
	return true;
}
//------------------------------------------------------------------------------
