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


		#include <cstdio>
		#include <cstring>
		#include "metafile/nml.h"
		#include "ascii/ascii_encoder.h"
		#include "filesystem/filesystem.h"
		#include "filesystem/io_handle.h"
		#include "memory/nauto_ptr.h"
		#include "platform_config.h"
		#include "platform.h"
		#include "log/log.h"

		using namespace GS;
		using namespace GS::NML;


//------------------------------------------------------------------------------
bool				Parser::SaveTag(IO::Handle &out, const Tag &tag, File::Binary method, uint idt)
{
	//---------------------------------------------------------------------------
	#define		OUTPUT_INDENT	{	for	(uint n = 0; n < idt; n++) out << "\t";	}
	//---------------------------------------------------------------------------

	if	(tag.name.IsEmpty() && !tag.GetChildCount())
		return true;	// silently skip this tag

	OUTPUT_INDENT;
	out << "<" << tag.name.c_str();

	switch (tag.GetValue().GetType())
	{
		case Variant::VariantNone:
			if	(tag.GetChildCount())
			{
				out << "=\n";

				NMLTagForeach(child, tag)
					if	(!SaveTag(out, *child, method, idt + 1))
						return false;

				OUTPUT_INDENT;
			}
			break;

		case Variant::VariantBinary:
		{
			uint olen = (uint)~0;

			switch (method)
			{
				case File::Binary_UU:
					olen = nAsciiEncoder::UUEncode((uchar *)tag.GetValue().GetBinaryBuffer(), tag.GetValue().GetBinarySize());
					break;

				case File::Binary_yEnc:
					olen = nAsciiEncoder::yEncode((uchar *)tag.GetValue().GetBinaryBuffer(), tag.GetValue().GetBinarySize());
					break;
			}

			if	(olen)
			{
				Array <uchar> aenc(olen, Alloc::Metatag);

				if	(aenc.IsValid())
				{
					uint asize = 0;
					switch (method)
					{
						case File::Binary_UU:
							asize = nAsciiEncoder::UUEncode((uchar *)tag.GetValue().GetBinaryBuffer(), tag.GetValue().GetBinarySize(), &aenc[0], olen);
							break;

						case File::Binary_yEnc:
							asize = nAsciiEncoder::yEncode((uchar *)tag.GetValue().GetBinaryBuffer(), tag.GetValue().GetBinarySize(), &aenc[0], olen);
							break;
					}
					if	(asize != olen)
						__LOG_W__ << "Internal ASCII encoding inconsistency detected while processing tag '" << tag.name << "'.\n";

					char str[256];
					_snprintf(str, 255, "==%d:%d", asize, tag.GetValue().GetBinarySize());		// ==[encoded size:decoded size] is encoded binary.
					out << str;

					if	(method == File::Binary_yEnc)						// @ marker select yEncoding.
						out << "@";
					out << "\n";

					out.Write(aenc, asize);
				}
				else
					__LOG_E__ << "Tag '" << tag.name << "' failed to allocate internal binary buffer.\n";
			}
//			else	__LOG_W__ << "NULL size ASCII encoded binary tag '" << tag.id << "'.\n";
		}
		break;

		case Variant::VariantInteger:
		{
			char str[256];
			_snprintf(str, 255, "=%d", tag.GetInteger());
			out << str;
		}
		break;

		case Variant::VariantFloat:
		{
			char str[256];
			_snprintf(str, 255, "=%f", tag.GetReal());
			out << str;
		}
		break;

		case Variant::VariantString:
			out << "=\"" << tag.GetString() << "\"";
			break;

		case Variant::VariantBool:
			out << "=" << (tag.GetBool() ? "True" : "False");
			break;

		default:
			__ERR__(__LOG_E__ << "No method to output tag '" << tag.name << "' type.\n", false)
	}

	out << ">\n";
	return true;
}
bool				Parser::Save(IO::Handle &h, const File &file)
{
	h << "<Version=1.0>\n";
	NMLFileForeach(tag, file)
		if	(!SaveTag(h, *tag, file.GetBinaryMethod(), 0))
			return false;
	return true;
}
bool				Parser::Save(const char *uri, const File &file)
{
	if	(!uri)
		return false;

	AutoPtr <IO::Handle> h(Platform::Get().io->Open(uri, IO::ModeWrite));
	if	(h.IsNull())
		__ERR__(__LOG_E__ << "Failed to open nml output '" << uri << "'.\n", false)

	return Save(*h, file);
}
//------------------------------------------------------------------------------
