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


#ifndef	__NMETALANG__
#define	__NMETALANG__


		#include "data/nvariant.h"
		#include "container/nlist.h"


namespace GS {
	namespace IO { class Handle; }

	namespace NML {
		class Tag;
		class File;

static	const int	version = 1;

/// Metatag.
class	Tag
{
		friend struct Parser;
		friend class File;

protected:

		/// Locate a tag through its path.
static	Tag			*GetTagEx(const List <Tag *> &tag, const char *path, const File *root = 0, bool verbose = false);

		List <Tag *> tags;

		Variant		value;

public:

		NPLACEMENT_NEW(Metatag)

		String		name;

		/// Get this tag parent by exploring a given tag hierarchy.
		Tag			*GetParent(Tag *) const;

		/// Get value.
		Variant		&GetValue() { return value; }
		const Variant &GetValue() const { return value; }

		/// Remove a child tag from this tag.
		bool		RemoveTag(Tag *t) { return tags.Remove(t); }
		/// Delete all child tags.
		uint		DeleteChildren(const char *filter = 0);
		/// Get child count.
		uint		GetChildCount()	const { return tags.GetCount(); }
		/// Return the tag children list.
		const List <Tag *> &GetTags() const { return tags; }

		/// Add child to tag.
		Tag			*AddChild(Tag *);
		Tag			*AddChild(const char *);
		Tag			*AddChild(const char *, bool);
		Tag			*AddChild(const char *, int);
		Tag			*AddChild(const char *, uint);
		Tag			*AddChild(const char *, float);
		Tag			*AddChild(const char *, const char *);
		Tag			*AddChild(const char *, void *, size_t);

		/// Return tag child.
		Tag			*GetTag(const char *path, const File * = 0, bool verbose = false) const;
		/// Return the tag at a given path only if it matches the required type.
		Tag			*GetTypedTag(const char *path, Variant::Type, const File * = 0, bool verbose = false) const;

		bool		GetBool() const { bool v; if (value.Get(v)) return v; return false; }
		int			GetInteger() const { return value.i_value; }
		uint		GetUnsigned() const { return value.u_value; }
		const char	*GetString() const { return value.s_value; }
		float		GetReal() const { return value.f_value; }

		void		SetBool(bool v) { value = v; }
		void		SetInteger(int v) { value = v; }
		void		SetUnsigned(uint v) { value = v; }
		void		SetString(const char *v) { value = v; }
		void		SetReal(float v) { value = v; }

		Variant::Type GetType() const { return value.GetType(); }

		/// Clone a tag and optionally recurse and clone its children.
		bool		Clone(const Tag &src, bool recursive = true);
		/// Clone this tag and optionally recurse and clone its children.
		Tag			*Clone(bool recursive = true) const;

		/// Free the tag data, set its type to nVariant::VariantNone.
		void		Free();

		Tag() {}
		Tag(const char *n) : name(n) {}
		Tag(const char *n, bool v) : name(n), value(v) {}
		Tag(const char *n, int v) : name(n), value(v) {}
		Tag(const char *n, uint v) : name(n), value(v) {}
		Tag(const char *n, float v) : name(n), value(v) {}
		Tag(const char *n, const char *v) : name(n), value(v) {}
		Tag(const char *n, void *data, size_t size) : name(n), value(data, size) {}

		~Tag();
};

/*!
	@short	Meta file.

	A metafile contain the whole database in memory.
	This class is not meant to be used with heavy databases.

	Tags can be accessed using tag paths. A tag path is formatted as follow:
	"root:node:...:tag;".

	@note	The closing hyphen is not required but warnings will be sent to the
			log if you don't use it. The node delimiter ':' can be repeated	any
			number of time between nodes with no effect (eg: ":::node1::node2:;").
			By definition a file is a node tag and cannot have a value.

	@author Emmanuel Julien (ejulien@gsworks.fr)
*/
class	File
{
		friend struct Parser;

public:

enum	Binary
{
		Binary_UU = 0,					///< UUEncoded.
		Binary_yEnc						///< yEncoded.
};

protected:

		List <Tag *>	tags;
		Binary			binary_method;	///< Binary encoding method.

public:

		String			name;

		/// Get the file ASCII binary encoding method.
		Binary			GetBinaryMethod() const { return binary_method; }

		const List <Tag *> &GetTags() const { return tags; }

		uint			GetRootCount() const { return tags.GetCount(); }

		void			Clear() { ListDeleteAllPtr(Tag *, tags); }
		bool			isEmpty() const { return asbool(GetRootCount() == 0); }

		/// Add a root tag to the file.
		Tag				*AddRoot(Tag *);
		Tag				*AddRoot(const char *);
		Tag				*AddRoot(const char *, bool);
		Tag				*AddRoot(const char *, int);
		Tag				*AddRoot(const char *, float);
		Tag				*AddRoot(const char *, const char *);
		Tag				*AddRoot(const char *, void *, size_t);

		/// Unlink a root tag from file.
		bool			UnlinkRoot(Tag *);

		/// Return the tag at a given path.
		Tag				*GetTag(const char *path, bool verbose = false) const
		{	return Tag::GetTagEx(tags, path, this, verbose);	}

		/// Return the tag at a given path only if it match the required type.
		Tag				*GetTypedTag(const char *path, Variant::Type, bool verbose = false) const;

		bool			GetBool(const char *path, bool _default = false, bool verbose = false) const;
		int				GetInteger(const char *path, int _default = -1, bool verbose = false) const;
		float			GetReal(const char *path, float _default = -1.f, bool verbose = false) const;
		const char		*GetString(const char *path, const char *_default = 0, bool verbose = false) const;

		/// Duplicate the structure of a source file into this file.
		void			Import(const File &, bool clear_before_import = true);
		/// Clone a file and all its tags.
		File			*Clone() const;

		void			Free();

		File() : binary_method(Binary_yEnc) {}
		~File();
};

/*!
	@short	Meta file parser.
	@author Emmanuel Julien (ejulien@gsworks.fr)
*/
struct	Parser
{
static	const char		*ParseTagPreprocessorDirective(Tag &tag, const char *s, const char *e);

static	bool			IsMetafile(const char *);

static	bool			SaveBinaryTag(IO::Handle &, const Tag &, File::Binary = File::Binary_yEnc);
static	bool			SaveTag(IO::Handle &, const Tag &, File::Binary = File::Binary_yEnc, uint idt = 0);

static	bool			ParseTag(Tag &, const char *s, const char *e, const char **r = 0);

static	bool			LoadFromMemory(const char *, size_t, File &);
static	File			*Load(const char *, bool verbose = true);
static	bool			Load(const char *, File &, bool verbose = true);

static	bool			Save(IO::Handle &, const File &);
static	bool			Save(const char *, const File &);
static	bool			SaveBinary(IO::Handle &, const File &);
static	bool			SaveBinary(const char *, const File &);
};

#define	NMLTagForeach(__I__, __T__) ListForeachPtr(GS::NML::Tag *, __I__, (__T__).GetTags())
#define	NMLFileForeach(__I__, __F__) ListForeachPtr(GS::NML::Tag *, __I__, (__F__).GetTags())

	} // NML
} // GS


#endif	// __NMETALANG__
