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


#ifndef	__NARCHIVE__
#define	__NARCHIVE__


		#include "nstring/nstring.h"
		#include "container/nlist.h"
		#include "memory/nauto_ptr.h"
		#include "filesystem/io_handle.h"


namespace GS {
	namespace Threading { class Mutex; }

/// Archive index entry.
struct	ArchiveEntry
{
		// Do not change this enumeration order!
enum	Method
{
	MethodRaw			=	0,
	MethodZLibCompress
};

		String			path;				///< Entry path.

		char			method;				///< Compression method.
		size_t			offset;				///< Data offset in archive.
		size_t			length;				///< Original length.
		size_t			compressed_length;	///< Compressed length.
};

/*!
	@short	Archive index.
	@author Emmanuel Julien (ejulien@gsworks.fr)
*/
class	ArchiveIndex
{
		friend class Archive;

protected:

		AutoList <ArchiveEntry *> index;

public:
		/// Find an entry in the index file.
		ArchiveEntry	*FindEntry(const char *alias) const;

		/// Load archive index.
		bool			Load(const char *uri);
		/// Save archive index.
		bool			Save(const char *uri);
};

/*!
	@short	Archive storage.

	A pretty straightforward solid archive file format.
	Supports per-file compression algorithm selection.

	@author Emmanuel Julien
*/
class	Archive
{
public:

enum	Revision
{
	Legacy				=	0,
	EnhancedLegacy
};

protected:

		AutoPtr <Threading::Mutex> access_mutex;

		bool			verbose;
		bool			append_mode;		///< Archive open in append mode.

		ArchiveIndex	index;				///< Archive index table.

		AutoPtr <IO::Handle> handle;

		Revision		revision;
		size_t			offset_padding,		///< File offset padding.
						size_padding;		///< File size padding.

public:

		/*!
			@short	Set file offset padding.

			Padding archive content is necessary on some platforms that do not
			support seeking or reading an arbitrary amount of bytes inside a
			file. This is often the case with the DVD unit of console systems.
		*/
		void			SetOffsetPadding(size_t padding = 0)
		{	offset_padding = padding;	}
		/*!
			@short	Set file size padding.
			@see	SetOffsetPadding().
		*/
		void			SetSizePadding(size_t padding = 0)
		{	size_padding = padding;	}

		/// Return internal index object.
		const List <ArchiveEntry *> &GetIndex() const
		{	return index.index;	}

		/// Set the verbose mode.
		void			SetVerbose(bool v = true)
		{	verbose = v;	}

		/// Load current archive index file.
		bool			LoadIndex(const char *path);
		/// Save current archive index file.
		bool			SaveIndex(const char *path);

		/*!
			@short	Open archive.

			@note	If no index is provided one will automatically be created
					upon archive opening. Although this a fast process it
					requires that the archive be opened on a stream-able
					file system and that the whole archive be seeked through.
		*/
		bool			OpenRead(const char *uri, const char *index_uri = 0);
		/// Create a new archive.
		bool			CreateNew(const char *);
		/// Close archive.
		void			Close();

		/// Check if a file exists in archive.
		ArchiveEntry	*Exists(const char *path) const
		{	return index.FindEntry(path);	}

		/*!
			@short	Load a file from the archive.

			@note	The output buffer is expected to old enough room to store
					the decompressed data.
			@see	Exists().
		*/
		bool			FileRead(const char *, void *);

		/// Write a memory block to the archive.
		ArchiveEntry	*MemoryBlockWrite(const char *, const void *, size_t, int compression_level = 6);
		/// Write a file to the archive.
		ArchiveEntry	*FileWrite(const char *path, const char *alias = 0, int compression_level = 6);

		Archive();
		~Archive();
};

}	// GS


#endif	// __NARCHIVE__
