/*
 * (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
*/
/**
 * @file XMLTree.hpp
 * @version @$Id$
 * @author Schlund + Partner AG
 * @brief *ps*
 *
 * (C) Copyright by Schlund+Partner AG
 *
 * Synopsis: @#include <ui-gxmlcpp/XMLTree.hpp>
 *
 */

#ifndef UI_GXML_XMLTREE_HPP
#define UI_GXML_XMLTREE_HPP

// THIS IS A COMPAT API ONLY
#include <ui-gxmlcpp/compat_warning.h>

// STDC++
#include <string>
#include <memory>
#include <fstream>

// C++ libraries
#include <ui-utilcpp/auto_ptr_compat.hpp>
#include <ui-gxmlcpp/XMLNodeSet.hpp>
#include <ui-gxmlcpp/Exception.hpp>
#include <ui-gxmlcpp/XPathContext.hpp>
#include <ui-gxmlcpp/XPathObject.hpp>
#include <ui-gxmlcpp/Tree.hpp>

/* NOTES
   o xmlChar is one byte.
   o "xmlChar" is compatible to "char"; xmlChar * Strings are always 0-terminated; (xmlChar *) is compatible to (char *)/string.c_str()
   o Simply using char *, string makes us still compatible with: UTF-8, ASCII, ISO-8859-*, ... (basically, all "up-to-8-Bits-Encodings").
   o UCS-2/UCS-4 encodings might be a problem.

   UNICODE  ISO 10646    All characters of the planet listed and labeled....

   UCS-2: 16-Bit Encoding of UNICODE.
   UCS-4: 32-Bit Encoding of UNICODE.
   UTF-8: 8-Bit Encoding of UNICODE. All todays ISO-8859-1 texts qualify as UTF-8. This is what UNIXses will do.
*/

namespace UI {
namespace GXML {

/** @brief High level XML tree abstraction. */
class XMLTree
{
public:
	/** @brief Error codes for exceptions. */
	enum ErrorCode
	{
		Parse_=1,             // XML could not be parsed
		Context_,             // Could not create (XPath) context for Tree
		NoNodeFromPath_,      // Could not find node from specified xpath
		NoStringFromPath_,    // Could not find node from specified xpath
		NoBoolFromPath_,      // Could not find node from specified xpath
		NoFloatFromPath_,     // Could not find node from specified xpath
		NodeCreation_,        // Could not create a node
		NodeAdding_,          // Could not add a created node
		TreeInvalid_          // ???
	};
	/** @brief Exceptions for this class. */
	typedef CodeException<ErrorCode> Exception;

	/** @brief Create from libxml2's xmlDocPtr. */
	XMLTree(xmlDocPtr const doc);
	/** @brief Create from a XML buffer. */
	XMLTree(char const * xmlBuffer, int size=-1);  // if size < 0, xmlBuffer must be a valid C-String!
	/** @brief Create from a XML string. */
	XMLTree(std::string const & xmlString);
	/** @brief Create from a XML file stream. */
	XMLTree(std::ifstream & xmlStream);
	/** @brief Create from a XML stream. */
	XMLTree(std::istream & xmlStream);

	~XMLTree();

	Tree * getNewInterface() { return &_doc; };
	Tree const * getNewInterfaceConst() const { return &_doc; };

	UI::Util::auto_ptr<XMLNodeSet> getNodeSet(std::string const & xpath) const;

	/** Get libxml2's internal xmlDocPtr of this tree.
	 *
	 * @note Not recommended, but we currently still need this.
	 */
	xmlDocPtr getDocPtr() const;

	/** Get libxml2's internal context of this tree.
	 *
	 * You get the one context created together with the tree.
	 *
	 * @note Contexts are not thread-safe.
	 * @note Not recommended, but we currently still need this.
	 */
	xmlXPathContextPtr getXPathContextPtr() const;

	/** Get a (new) XPath context for this tree.
	 *
	 * You get a new (thread safe) context of the tree.
	 *
	 * @note Not recommended, but we currently still need this.
	 */
	XPathContext getXPathContext() const;

	////////////////////////////////////////////////////////////////////////
	// "Get Methods" based on "XPath"; xpaths are XPath Expressions;
	// all xpaths can be given an optional position
	//  - Methods delivering (xmlChar *) point into the tree
	//  - Methods delivering (std::string, int) obviously copy from tree
	//  - getValue-Methods will deliver the contents of the first child only
	//    e.g.: getValue("/req/X", 0) on
	// "<req><X>a b c<subtag>def</subtag></X></req>" returns: "a b c" only.

	// "libxml2"-Functions. Do not use if you want to be abstract.
	xmlChar * getXmlCharValue(xmlChar const * xpath) const;
	xmlChar * getXmlCharValue(std::string const & xpath) const;

	// "C++"-functions
	char * getAddrValue(std::string const & xpath) const;
	std::string getValue(std::string const & xpath) const;

	char * getAddrName(std::string const & xpath) const;
	std::string getName(std::string const & xpath) const;

	void setValue(std::string const & xpath, char * value);
	void setValue(std::string const & xpath, std::string const & value);

	int getCount(xmlChar const * xpath) const;
	int getCount(char const * xpath) const;
	int getCount(std::string const & xpath) const;

	////////////////////////////////////////////////////////////////////////
	// "Modifying Methods" based on "XPath"; xpaths are XPath Expressions
	//  - Methods delivering (xmlChar *) point into the tree
	//  - Methods delivering (std::string, int) copy from tree
	void delTag(std::string const & xpath);
	void addTag(std::string const & xpath, std::string const & name, std::string const & content);

	/** @brief Add a new node at the same level(tree depth) the given xpath points to.
	 *
	 * @param xpath to sibling node
	 * @param name the name of the new node
	 * @param content the content of the new node
	 */
	void addSiblingTag(std::string const & xpath, std::string const & name, std::string const & content);

	/** @brief Get sibling nodes beneath the given xpath expression.
	 *
	 * @param xpath Path to parent node.
	 * @returns All nodes beneath the given XPath.
	 */
	std::string getSiblingXML(std::string const & xpath = "/*" ) const;

	/** @brief Add the whole tree as new child for node.
	 *
	 * @note This will COPY. No other method available from libxml2. We cannot mix trees.
	 */
	void addTree(std::string const & xpath, XMLTree const * xmlTree);

	/** @brief Add the whole tree given as XML as new child for node. */
	void addXML(std::string const & xpath, std::string const & xml);

	/** @brief Serialize (dump) tree.
	 *
	 * @see XMLDump.
	 */
	std::string getXML(std::string const & xpath="/") const;

	/** @brief Get a copy of this tree. */
	UI::Util::auto_ptr<XMLTree> getTree(std::string const & path="/");

	/** Get value with type-check. */
	std::string getString(std::string const & xpath) const { return XPathObject(XPathContext(getDocPtr()), xpath).getString(); };
	bool getBool(std::string const & xpath) const  { return XPathObject(XPathContext(getDocPtr()), xpath).getBoolean(); };
	double getFloat(std::string const & xpath) const  { return XPathObject(XPathContext(getDocPtr()), xpath).getNumber(); };

	/** @{ */
	/** @brief Get node from Xpath. */
	static xmlNodePtr nodeFromPath(xmlXPathContextPtr const context, xmlChar const * xpath, bool doThrow);
	static xmlNodePtr nodeFromPath(xmlXPathContextPtr const context, std::string const & xpath, bool doThrow);
	/** @} */

private:
	Tree _doc;
	XPathContext _context;
};

}}
#endif
