/*
 * (C) Copyright 2002, Schlund+Partner AG
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

// Local configuration
#include "config.h"

// Implementation
#include "XMLDump.hpp"

// STDC++
#include <cassert>

#ifndef LIBXML2_NEW_BUFFER
#define xmlBufContent(buf) ((buf)->content)
#define xmlBufUse(buf) ((buf)->use)
#endif

// C++ libaries
#include <ui-utilcpp/Text.hpp>

namespace UI {
namespace GXML {

//
// XSLTDump
//
XSLTOutputBuf::XSLTOutputBuf(xmlDocPtr result, xsltStylesheetPtr style)
	:_encoder(0)
{
	const xmlChar *encoding;

	if ((result == 0) || (result->children == 0))
	{
		UI_THROW_CODE(ResultInvalid_, "Result Doc invalid (0 or no children)");
	}
	if (style == NULL)
	{
		UI_THROW_CODE(StyleInvalid_, "Style invalid (0)");
	}

	XSLT_GET_IMPORT_PTR(encoding, style, encoding);
	if (encoding != NULL)
	{
		_encoder = xmlFindCharEncodingHandler((char *)encoding);
		if ((_encoder != NULL) &&	(xmlStrEqual((const xmlChar *)_encoder->name, (const xmlChar *) "UTF-8")))
		{
			_encoder = 0;
		}
		_buf = xmlAllocOutputBuffer(_encoder);
	}
	else
	{
		_buf = xmlAllocOutputBuffer(0);
	}
	if (_buf == 0)
	{
		UI_THROW_CODE(Mem_, "Allocation error");
	}
	xsltSaveResultTo(_buf, result, style);
}

XSLTOutputBuf::~XSLTOutputBuf()
{
	xmlOutputBufferClose(_buf);
}

char const *
XSLTOutputBuf::getAddr() const
{
	if (_encoder)
	{
		return (char *) xmlBufContent(_buf->conv);
	}
	else
	{
		return (char *) xmlBufContent(_buf->buffer);
	}
}

int
XSLTOutputBuf::getSize() const
{
	if (_encoder)
	{
		return xmlBufUse(_buf->conv);
	}
	else
	{
		return xmlBufUse(_buf->buffer);
	}
}

//
// XMLDump
//
char const * const XMLDump::_nullString = "";

// genDump: Master Create Dump Function. Must be used in Constructors only
// Note: xmlNodeDump segfaults with path="/"; this is the only reason why differentiate docDump, nodeDump... :(
void
XMLDump::genDump(xmlDocPtr const doc, xmlXPathContextPtr const ctxt, std::string const & path)
{
	_xsltDump = 0;
	assert(doc);

	if (path == "/")
	{
		_nodeDump = 0;
		xmlDocDumpMemoryEnc(doc, &_docDump.first, &_docDump.second, "UTF-8");
		if (!_docDump.first)
		{
			UI_THROW_CODE(Mem_, "xmlDocDumpMemoryEnc: Allocation error");
		}
	}
	else
	{
		assert(ctxt);
		_nodeDump = xmlBufferCreate();
		if (!_nodeDump)
		{
			UI_THROW_CODE(Mem_, "xmlBufferCreate: Allocation error");
		}
		xmlNodePtr node(XMLTree::nodeFromPath(ctxt, path, false));
		if (!node)
		{
			xmlBufferFree(_nodeDump);
			UI_THROW_CODE(NodeNotFound_, "Node not found from path: " + path);
		}

		xmlNodeDump(_nodeDump, doc, node, 0, 0);
		if (!_nodeDump->content)
		{
			xmlBufferFree(_nodeDump);
			UI_THROW_CODE(Mem_, "Allocation error");
		}
	}
}

XMLDump::XMLDump(xmlDocPtr const doc, xmlXPathContextPtr const ctxt, std::string const & path)
{
	genDump(doc, ctxt, path);
}

XMLDump::XMLDump(XMLTree const * const tree, std::string const & path)
{
	XPathContext context(tree->getDocPtr());

	genDump(tree->getDocPtr(), context.get(), path);
}

XMLDump::XMLDump(xmlDocPtr const doc)
{
	genDump(doc, 0, "/");
}


XMLDump::XMLDump(XSLTOutputBuf const * buf)
	:_nodeDump(0)
	,_docDump(std::make_pair((xmlChar *)0,0))
	,_xsltDump((XSLTOutputBuf *) buf)
{}

XMLDump::~XMLDump()
{
	if (_nodeDump)
	{
		xmlBufferFree(_nodeDump);
	}
	if (_docDump.first)
	{
		xmlFree(_docDump.first);
	}
	if (_xsltDump)
	{
		delete _xsltDump;
	}
}

char const *
XMLDump::getAddr() const
{
	if (_xsltDump)
	{
		return _xsltDump->getAddr();
	}
	else if (_nodeDump)
	{
		return (char *) _nodeDump->content;
	}
	else if (_docDump.first)
	{
		return (char *) _docDump.first;
	}
	return _nullString;
}

std::string
XMLDump::get() const
{
	return getAddr();
}

int
XMLDump::getSize() const
{
	if (_xsltDump)
	{
		return _xsltDump->getSize();
	}
	else if (_nodeDump)
	{
		return xmlBufferLength(_nodeDump);
	}
	else if (_docDump.first)
	{
		return _docDump.second;
	}
	return 0;
}

}}
