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


#ifndef	__NIOCACHE__
#define	__NIOCACHE__


		#include "filesystem/data_store.h"
		#include "filesystem/io_handle.h"
		#include "memory/nauto_ptr.h"
		#include "container/nlist.h"
		#include "nstring/nstring.h"
		#include "time/ntime.h"
		#include "unit/nunit.h"


namespace GS {
	namespace IO {

//
class	CacheHandle : public Handle
{
		friend class Cache;

protected:

		String		path;
		AutoPtr <Handle> handle;

public:

		CacheHandle(Base *io, const char *p, Handle *h) : Handle(io), path(p), handle(h) {}
		~CacheHandle();
};

/*!
	@short	Cached I/O filesystem wrapper.
	Implements a transparent file cache on top of another filesystem.
	@author	Emmanuel Julien (ejulien@nworks.fr)
*/
class	Cache : public Base
{
		SharedPtr <Base> io;
		DataStore	store;

public:

struct	Entry
{
	String		path, id;
	String		hash;

	uint		refc;
	size_t		size;

	Time		last_use;

	Entry() : refc(0), size(0) {}
};

private:

		Threading::Mutex mutex;

		size_t		cache_size;
		AutoList <Entry *> entries;

		int			ComputeEntryRecyclingScore(const Entry *) const;
		String		ReserveOnStore(size_t size);

		Entry		*GetCacheEntry(const char *path) const;
		Entry		*CreateCacheEntry(const char *path);
		bool		UpdateCacheEntry(Entry *entry, Array <char> *preloaded_data = 0);
		bool		DeleteCacheEntry(Entry *entry);

public:

virtual	uint		GetCaps() const;

virtual	Handle		*Open(const char *, Mode = ModeRead);
virtual	void		Close(Handle *);

virtual	bool		Delete(const char *);

virtual	size_t		Tell(Handle *);
virtual	size_t		Seek(Handle *, ptrdiff_t offset, SeekRef = SeekCurrent);

virtual	size_t		Read(Handle *, void *, size_t);
virtual	size_t		Write(Handle *, const void *, size_t);

virtual	bool		MkDir(const char *);

		/// Test if a given path is in cache.
		bool		IsInCache(const char *path) const { return asbool(GetCacheEntry(path)); }

		/// Reload cache state from its store.
		bool		SynchronizeWithStore(const char *store_path = "store.db");

		Cache(Base *base, Base *store, size_t cache_size = Units::MB(32));
};

	}	// IO
}	// GS


#endif	// __NIOCACHE__
