/*
 * (C) Copyright 2002-2003, 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 "XSLTrans.hpp"

// STDC++
#include <fstream>

// STDC
#include <cassert>
#include <cstring>

// C libraries
#include <libxslt/xsltInternals.h>
#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>

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

// Local
#include "../../src/ui-gxmlcpp/Util.hpp"
#include "XMLTree.hpp"
#include "XMLDump.hpp"

namespace UI {
namespace GXML {

char const * XSLTrans::_defaultEncoding = "UTF-8";

// Private helper function for constructors
void XSLTrans::genTrans(char const * xmlBuffer, int size, std::string const & baseURI)
{
	// if size < 0, we must have a valid, NULL-Terminated C-String
	if (size < 0) size = (int)std::strlen(xmlBuffer);
	// Step 1: Create an "xmlDocPtr"
	_doc = xmlParseMemory(xmlBuffer, size);
	if (!_doc)
	{
		UI_THROW_CODE(Parse_, "getTrans: Stylesheet not well-formed");
	}

	// Support for local includes/imports
	// See: http://mail.gnome.org/archives/xslt/2001-August/msg00037.html
	if (_doc->URL == 0 && baseURI != "")
	{
		_doc->URL = (xmlChar *)xmlMalloc(baseURI.size()+1);
		if (!_doc->URL)
		{
			xmlFreeDoc(_doc);
			UI_THROW_CODE(Parse_, "getTrans: Stylesheet includes not well-formed");
		}
		std::memcpy((void *)_doc->URL, baseURI.c_str(), baseURI.size()+1);
	}

	// Step 2: Create an "xsltStylesheetPtr"
	_style = xsltParseStylesheetDoc(_doc);
	if (!_style)
	{
		xmlFreeDoc(_doc);
		UI_THROW_CODE(Style_, "getTrans: Stylesheet not valid");
	}
}

XSLTrans::XSLTrans(char const * xmlBuffer, int size, std::string const & baseURI)
{
	genTrans(xmlBuffer, size, baseURI);
}

XSLTrans::XSLTrans(std::string const & xmlString, std::string const & baseURI)
{
	genTrans(xmlString.c_str(), -1, baseURI);
}

XSLTrans::XSLTrans(std::ifstream & f, std::string const & baseURI)
{
	genTrans(UI::Util::istream2String(f).c_str(), -1, baseURI);
}

XSLTrans::~XSLTrans()
{
	// This frees _doc as well
	xsltFreeStylesheet(_style);
}

// Trans into XMLTree
UI::Util::auto_ptr<XMLTree> XSLTrans::transToXMLTree(xmlDocPtr const doc) const
{
	assert(doc);
	xmlDocPtr resultTree(xsltApplyStylesheet(_style, doc, 0));
	if (!resultTree)
	{
		UI_THROW_CODE(Trans_, "transToXMLTree: Error in XSL transformation");
	}
	return UI::Util::auto_ptr<XMLTree>(new XMLTree(resultTree));
}

// This is the master trans method
UI::Util::auto_ptr<XMLDump> XSLTrans::trans(xmlDocPtr const doc) const
{
	assert(doc);
	xmlDocPtr resultTree = xsltApplyStylesheet(_style, doc, 0);
	if (!resultTree)
	{
		UI_THROW_CODE(Trans_, "trans: Error in XSL transformation");
	}
	XSLTOutputBuf * obuf(new XSLTOutputBuf(resultTree, _style));
	xmlFreeDoc(resultTree);

	XMLDump * result(new XMLDump(obuf));
	return UI::Util::auto_ptr<XMLDump>(result);
}

UI::Util::auto_ptr<XMLDump> XSLTrans::trans(XMLTree const * xmlTree) const
{
	assert(xmlTree);
	return trans(xmlTree->getDocPtr());
}

UI::Util::auto_ptr<XMLDump> XSLTrans::trans(std::string const & xmlString) const
{
	XMLTree xmlTree(xmlString);
	return trans(&xmlTree);
}

xsltStylesheetPtr XSLTrans::getStylesheetPtr()
{
	return _style;
}

}}
