/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          CXmlNode.h  -  description
                             -------------------
    begin                : Sun May 22 2003
    based on the code    : Mikko Lahteenmaki <Laza@Flashmail.com>
    copyright            : (C) 2003-2006 by Alexey S.Parshin
    email                : alexeyp@gmail.com
 ***************************************************************************/

/***************************************************************************
   This library is free software; you can redistribute it and/or modify it
   under the terms of the GNU Library General Public License as published by
   the Free Software Foundation; either version 2 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 Library
   General Public License for more details.

   You should have received a copy of the GNU Library 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.

   Please report all bugs and problems to "alexeyp@gmail.com"
 ***************************************************************************/

#ifndef __CXMLNODE_H__
#define __CXMLNODE_H__

#include <sptk3/CBuffer.h>
#include <sptk3/CDateTime.h>
#include <sptk3/xml/CXmlAttributes.h>
#include <sptk3/xml/CXmlNodeList.h>

#include <string>
#include <map>

namespace sptk {

   /// @addtogroup XML
   /// @{

	class CXmlDoc;
	class CXmlNode;
	class CXmlNode;

   /// @brief XML node
   ///
   /// XML node with the attributes and possible child XML nodes
	class SP_EXPORT CXmlNode {
		friend class CXmlParser;
		friend class CXmlNodeList;
		friend class CXmlDoc;
		friend class CXmlElement;
	public:
      /// @brief Node type enumeration
		enum CXmlNodeType {
			DOM_UNDEFINED = 0,      ///< Type isn't defined yet
			DOM_DOCUMENT=1,         ///< Document node
			DOM_ELEMENT=2,          ///< Normal element node, can contain subnodes
			DOM_PI=4,               ///< Processing Instruction node
			DOM_TEXT=8,             ///< Cdata where all default entities MUST be escaped.
			DOM_CDATA_SECTION=16,   ///< Cdata section, which can contain preformatted char data.
			DOM_COMMENT=32          ///< Comment node
		};

      /// @brief CXmlNode own iterator for subnodes
		typedef CXmlNodeList::iterator       iterator;

      /// @brief CXmlNode own const_iterator for subnodes
		typedef CXmlNodeList::const_iterator const_iterator;

	private:
      /// @brief Sets parent node - for CXmlParser
		void parent(CXmlNode *p);

	protected:
      /// @brief An empty nodes set to emulate a set of stub iterators
		static CXmlNodeList emptyNodes;

	protected:

      /// @brief An empty string to use as a stub for value()
		static const std::string emptyString;

		CXmlDoc*           m_document;           ///< Document pointer
		CXmlNode*          m_parent;             ///< Parent node pointer

      /// @brief Protected constructor - for derived classes
      ///
      /// @param doc CXmlDoc&, node document
		CXmlNode(CXmlDoc& doc); 

      /// @brief Protected constructor - for derived classes
      ///
      /// @param parent CXmlNode&, node document
		CXmlNode(CXmlNode& parent);

      /// @brief Destructor
		virtual ~CXmlNode();

	public:

      /// @brief Finds the first subnode with the given name
      ///
      /// Returns node pointer or NULL, if the node with such name is not found.
      /// @param name std::string, the name to find
      /// @param recursively bool, if true, also search in all subnodes
		CXmlNode *findFirst(std::string name,bool recursively=true) const;

      /// @brief Finds the first subnode with the given name, or creates a new one.
      ///
      /// Returns node pointer. If the node with such name is not found, then new
      /// node is created.
      /// @param name std::string, the name to find
      /// @param recursively bool, if true, also search in all subnodes
		CXmlNode *findOrCreate(std::string name,bool recursively=true);

      /// @brief Returns node type
		virtual CXmlNodeType type() const = 0;

      /// @brief Selects subnodes with the given name
      ///
      /// @param nodes CXmlNodeVector&, the resulting list of subnodes
      /// @param name std::string, the name for subnodes
		void select(CXmlNodeVector& nodes,std::string name) const;

      /// @brief Performs a deep copy of node and all its subnodes
      ///
      /// @param node const CXmlNode&, a node to copy from
		virtual void copy(const CXmlNode& node);

      /// @brief Deletes all children and clears all the attributes
      ///
      /// Any memory, associated with children or attributes,
      /// is released.
		virtual void clear();

      /// @brief Returns parent node of this node.
      ///
      /// For CXmlDocument this returns 'this' pointer.
		CXmlNode *parent() const { return m_parent; }

      /// @brief Returns document context associated with this node.
		CXmlDoc *document() const { return m_document; }

      /// @brief Returns the node name.
      ///
      /// The meaning of the name depends on the node type:
		virtual const std::string& name() const=0;

      /// @brief Sets the new name for the node
      /// @param name const std::string&, new node name
		virtual void name(const std::string& name)=0;

      /// @brief Sets new name for node
      /// @param name const char *, new node name
		virtual void name(const char *name)=0;

      /// @brief Returns the value of the node
      ///
      /// The meaning of the value depends on the node type.
      /// DOM_DOCUMENT and DOM_ELEMENT don't have values
		virtual const std::string& value() const { return emptyString; }

      /// @brief Sets new value to node.
      ///
      /// @param new_value const std::string &, new value
      /// @see value()
		virtual void value(const std::string &new_value) {}

      /// @brief Sets new value to node
      ///
      /// @param new_value const char *, value to set
      /// @see value()
		virtual void value(const char *new_value) {}

      /// @brief Returns cdatas combined from children.
      ///
      /// E.g. "Some <tag>text</tag> here" becomes: "Some text here"
		std::string text() const;

      /// Returns true, if node has any attributes
		bool hasAttributes() const ;

      /// Returns true, if given attribute is found
      /// @param attr const char *, attribute to search
		bool hasAttribute(const char *attr) const;

      /// Returns attribute value for given attribute.
      /// HTML tags can have empty attributes, for those you should use has_attribute() method.
      /// @param attr name of attribute
      /// @param defaultValue const char *, a default value. If attribute doesn't exist then default value is returned.
		CXmlAttribute getAttribute(std::string attr,const char *defaultValue=0L) const;

      /// Sets new value to attribute 'attr'.
      /// If attribute is not found, it's added to map.
      /// @param attr attribute name
      /// @param value attribute value
      /// @param defaultValue const char *, a default value. If attribute value is matching default value than attribute isn't stored (or removed if it existed).
		void setAttribute(const char *attr, CXmlAttribute value,const char *defaultValue=0L);

      /// Sets new value to attribute 'attr'.
      /// If attribute is not found, it's added to map.
      /// @param attr const string&, an attribute name
      /// @param value CXmlAttribute, attribute value
      /// @param defaultValue const char *, a default value. If attribute value is matching default value than attribute isn't stored (or removed if it existed).
		void setAttribute(const std::string& attr, CXmlAttribute value,const char *defaultValue=0L);

      /// Saves node to buffer.
      /// @param buffer to save
      /// @param indent how many indent spaces at start
		virtual void save(CBuffer &buffer, int indent=0) const;

      /// @brief Returns the first subnode iterator
		virtual iterator begin() { return emptyNodes.end(); }

      /// @brief Returns the first subnode const iterator
		virtual const_iterator begin() const { return emptyNodes.end(); }

      /// @brief Returns the end subnode iterator
		virtual iterator end() { return emptyNodes.end(); }

      /// @brief Returns the end subnode const iterator
		virtual const_iterator end() const { return emptyNodes.end(); }

      /// @brief Returns a number of subnodes
		virtual size_t size() const { return 0; }

      /// @brief Returns true if node has no subnodes of subnodes
		virtual bool empty() const { return true; }

      /// @brief Appends a subnode
		virtual void push_back(CXmlNode* node) {}

      /// @brief Inserts a subnode
      ///
      /// @param pos iterator, insert position with the list of subnodes
      /// @param node CXmlNode*, node to insert
		virtual void insert(iterator pos,CXmlNode* node) {}

      /// @brief Removes a subnode
      ///
      /// Release all the allocated memory and disconnects from parent (this node)
		virtual void remove(CXmlNode* node) {}

      /// @brief Removes a subnode
      ///
      /// Disconnects subnode from parent (this node)
		virtual void unlink(CXmlNode* node) {}

	public:
		CXmlAttributes attributes;           ///< Node attributes

		bool isDocument() const { return type() == DOM_DOCUMENT; }          ///< Document node
		bool isElement()  const { return type() == DOM_ELEMENT;  }          ///< Normal element node, can contain subnodes
		bool isPI()       const { return type() == DOM_PI;       }          ///< Processing Instruction node
		bool isText()     const { return type() == DOM_TEXT;     }          ///< Cdata where all default entities MUST be escaped.
		bool isCDataSection() const { return type() == DOM_CDATA_SECTION; } ///< Cdata section, which can contain preformatted char data.
		bool isComment()  const { return type() == DOM_COMMENT;  }          ///< Comment node
	};

   /// XML Element
	class CXmlElement : public CXmlNode {
		friend class CXmlDoc;
		const std::string* m_name;               ///< Node name, stored in the parent document SST
		CXmlNodeList       m_nodes;              ///< The list of subnodes
	protected:
      /// @brief Protected constructor for creating CXmlDoc only
      ///
      /// @param doc CXmlDoc&, a document.
		CXmlElement(CXmlDoc& doc) : CXmlNode(doc) {}

	public:
      /// @brief Constructor
      ///
      /// @param parent CXmlNode*, a parent node.
      /// @param tagname const char*, a name of XML tag
		CXmlElement(CXmlNode& parent,const char* tagname)
		: CXmlNode(parent) {
			name(tagname);
		}

      /// @brief Constructor
      ///
      /// @param parent CXmlNode*, a parent node.
      /// @param tagname const char*, a name of XML tag
		CXmlElement(CXmlNode* parent,const char* tagname)
		: CXmlNode(*parent) {
			name(tagname);
		}

      /// @brief Constructor
      ///
      /// @param parent CXmlNode &, a parent node.
      /// @param tagname const string&, a name of XML tag
		CXmlElement(CXmlNode& parent,const std::string& tagname)
		: CXmlNode(parent) {
			name(tagname);
		}

      /// @brief Returns the node name.
      ///
      /// The meaning of the value depends on the node type
		virtual const std::string& name() const { return *m_name; }

      /// @brief Sets the new name for the node
      /// @param name const std::string&, new node name
		virtual void name(const std::string& name);

      /// @brief Sets new name for node
      /// @param name const char *, new node name
		virtual void name(const char *name);

      /// @brief Returns node type
		virtual CXmlNodeType type() const { return DOM_ELEMENT; }

      /// @brief Returns the first subnode iterator
		virtual iterator begin() { return m_nodes.begin(); }

      /// @brief Returns the first subnode const iterator
		virtual const_iterator begin() const { return m_nodes.begin(); }

      /// @brief Returns the end subnode iterator
		virtual iterator end() { return m_nodes.end(); }

      /// @brief Returns the end subnode const iterator
		virtual const_iterator end() const { return m_nodes.end(); }

      /// @brief Returns a number of subnodes
		virtual size_t size() const { return m_nodes.size(); }

      /// @brief Returns true if node has no subnodes of subnodes
		virtual bool empty() const { return m_nodes.empty(); }

      /// @brief Appends a subnode
      ///
      /// @param node CXmlNode*, node to append
		virtual void push_back(CXmlNode* node);

      /// @brief Inserts a subnode
      ///
      /// @param pos iterator, insert position with the list of subnodes
      /// @param node CXmlNode*, node to insert
		virtual void insert(iterator pos,CXmlNode* node);

      /// @brief Removes a subnode
      ///
      /// Any memory allocated for subnode is released and subnode is 
      /// removed from its parent
      /// @param node CXmlNode*, node to remove
		virtual void remove(CXmlNode* node);

      /// @brief Removes a subnode
      ///
      /// Disconnects subnode from parent (this node)
		virtual void unlink(CXmlNode* node);

      /// @brief Deletes all children and clears all the attributes
      ///
      /// Any memory, associated with children or attributes,
      /// is released.
		virtual void clear();
	};

   /// @brief Base class for XML nodes with value
	class CXmlBaseTextNode : public CXmlNode {
		std::string        m_value;              ///< Node value
	protected:
      /// @brief returns node name
		virtual const std::string& nodeName() const { return emptyString; }
	public:
	   /// @brief Constructor
		CXmlBaseTextNode(CXmlNode *parent,const char *data)
		: CXmlNode(*parent) 
		{
			value(data);
		}

      /// @brief Returns the value of the node
      ///
      /// The meaning of the value depends on the node type.
      /// DOM_DOCUMENT and DOM_ELEMENT don't have values
		virtual const std::string& value() const { return m_value; }

      /// @brief Sets new value to node.
      ///
      /// @param new_value const std::string &, new value
      /// @see value()
		virtual void value(const std::string &new_value) { m_value = new_value; }

      /// @brief Sets new value to node
      ///
      /// @param new_value const char *, value to set
      /// @see value()
		virtual void value(const char *new_value) { m_value = new_value; }

      /// @brief Returns the node name.
      ///
      /// The meaning of the value depends on the node type
		virtual const std::string& name() const { return nodeName(); }

      /// @brief Sets the new name for the node
      /// @param name const std::string&, new node name
		virtual void name(const std::string& name) {}

      /// @brief Sets new name for node
      /// @param name const char *, new node name
		virtual void name(const char *name) {}

	};

   /// XML Text
	class CXmlText : public CXmlBaseTextNode {
		static const std::string nodeNameString;
	protected:
      /// @brief returns node name
		virtual const std::string& nodeName() const { return nodeNameString; }
	public:
      /// @brief Constructor
      ///
      /// @param parent CXmlNode&, a parent node.
      /// @param data const char*, a text
		CXmlText(CXmlNode& parent,const char *data)
		: CXmlBaseTextNode(&parent, data) {}

      /// @brief Constructor
      ///
      /// @param parent CXmlNode*, a parent node.
      /// @param data const char*, a text
		CXmlText(CXmlNode* parent,const char *data)
		: CXmlBaseTextNode(parent, data) {}

      /// @brief Constructor
      ///
      /// @param parent CXmlNode &, a parent node.
      /// @param data const std::string&, a text
		CXmlText(CXmlNode& parent,const std::string& data)
		: CXmlBaseTextNode(&parent, data.c_str()) {}

      /// @brief Returns node type
		virtual CXmlNodeType type() const { return DOM_TEXT; }
	};

   /// XML comment
	class CXmlComment : public CXmlBaseTextNode {
		static const std::string nodeNameString;
	protected:
      /// @brief returns node name
		virtual const std::string& nodeName() const { return nodeNameString; }
	public:
      /// @brief Constructor
      ///
      /// @param parent CXmlNode &, a parent node.
      /// @param data const char *, a comment
		CXmlComment(CXmlNode& parent,const char *data)
		: CXmlBaseTextNode(&parent, data) {}

      /// @brief Constructor
      ///
      /// @param parent CXmlNode*, a parent node.
      /// @param data const char*, a comment
		CXmlComment(CXmlNode* parent,const char* data)
		: CXmlBaseTextNode(parent, data) {}

      /// @brief Constructor
      ///
      /// @param parent CXmlNode &, a parent node.
      /// @param data const std::string&, a comment
		CXmlComment(CXmlNode& parent,const std::string& data)
		: CXmlBaseTextNode(&parent, data.c_str()) {}

      /// @brief Returns node type
		virtual CXmlNodeType type() const { return DOM_COMMENT; }
	};

   /// XML CData section
	class CXmlCDataSection : public CXmlBaseTextNode {
		static const std::string nodeNameString;
	protected:
      /// @brief returns node name
		virtual const std::string& nodeName() const { return nodeNameString; }
	public:
      /// @brief Constructor
      ///
      /// @param parent CXmlNode&, a parent node.
      /// @param data const char*, a data
		CXmlCDataSection(CXmlNode& parent,const char* data)
		: CXmlBaseTextNode(&parent, data) {}

      /// @brief Constructor
      ///
      /// @param parent CXmlNode*, a parent node.
      /// @param data const char*, a data
		CXmlCDataSection(CXmlNode* parent,const char* data)
		: CXmlBaseTextNode(parent, data) {}
      /// @brief Constructor
      ///
      /// @param parent CXmlNode &, a parent node.
      /// @param data const std::string&, a data
		CXmlCDataSection(CXmlNode& parent,const std::string& data)
		: CXmlBaseTextNode(&parent, data.c_str()) {}

      /// @brief Returns node type
		virtual CXmlNodeType type() const { return DOM_CDATA_SECTION; }
	};

   /// XML processing instructions (PI)
	class CXmlPI : public CXmlBaseTextNode {
		const std::string* m_name;               ///< Node name, stored in the parent document SST
	public:
      /// @brief Constructor
      ///
      /// @param parent CXmlNode &, a parent node. Make sure it's a pointer to the existing node.
      /// @param target std::string, a target tag name
      /// @param data const char *, a data
		CXmlPI(CXmlNode& parent,std::string target,const char *data)
		: CXmlBaseTextNode(&parent, data) {
			name(target);
		}

      /// @brief Constructor
      ///
      /// @param parent CXmlNode&, a parent node. Make sure it's a pointer to the existing node.
      /// @param target std::string, a target tag name
      /// @param data const char*, a data
		CXmlPI(CXmlNode* parent,std::string target,const char* data)
		: CXmlBaseTextNode(parent, data) {
			name(target);
		}

      /// @brief Constructor
      ///
      /// @param parent CXmlNode &, a parent node
      /// @param target std::string, a target tag name
      /// @param data const std::string&, a data
		CXmlPI(CXmlNode& parent,std::string target,const std::string& data)
		: CXmlBaseTextNode(&parent, data.c_str()) {
			name(target);
		}

      /// @brief Returns the node name.
      ///
      /// The meaning of the value depends on the node type
		virtual const std::string& name() const { return *m_name; }

      /// @brief Sets the new name for the node
      /// @param name const std::string&, new node name
		virtual void name(const std::string& name);

      /// @brief Sets new name for node
      /// @param name const char *, new node name
		virtual void name(const char *name);

      /// @brief Returns node type
		virtual CXmlNodeType type() const { return DOM_PI; }
	};

#define XML_TYPE_DOC             CXmlNode::DOM_DOCUMENT        ///< Node type is a document
#define XML_TYPE_ELEMENT         CXmlNode::DOM_ELEMENT         ///< Node type is an element
#define XML_TYPE_PI              CXmlNode::DOM_PI		         ///< Node type is a processing instruction
#define XML_TYPE_TEXT            CXmlNode::DOM_TEXT	         ///< Node type is a text
#define XML_TYPE_CDATA_SECTION   CXmlNode::DOM_CDATA_SECTION	///< Node type is CDataSection
#define XML_TYPE_COMMENT         CXmlNode::DOM_COMMENT         ///< Node type is a comment

   /// @}
}
#endif
