/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          CXmlDoc.h  -  description
                             -------------------
    begin                : Sun May 22 2003
    based on the code    : Mikko Lahteenmaki <Laza@Flashmail.com>
    copyright            : (C) 2003 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 __CXMLDOC_H__
#define __CXMLDOC_H__

#include "CXmlNode.h"
#include "CXmlTokenizer.h"

#include <sptk3/CBuffer.h>

#include <string>
#include <map>

/// @addtogroup XML
/// @{

/// @brief XML entities
///
/// Maps an XML entity string to a presentation string.
class CXmlEntities : public std::map<std::string,std::string> {
public:

   /// Constructor
   CXmlEntities() { }

    /**
     * Remove named entity.
     * @param name const char *, entity name to remove
     * Returns true, if operation success
     */
   void removeEntity(const char *name) { remove(name); }

    /**
     * Add entity to map, If entity named 'name' exists already in map,
     * it's value is replaced with 'replacement'
     * @param name const char *, entity to add/change
     * @param replacement const char *, value that represents entity
     */
   void setEntity(const char *name, const char *replacement) { (*this)[name] = replacement; }
};

/// @brief XML document type.
/// 
/// Represents tag <DOCTYPE ...> in XML document.
/// It can return a map of all entities(). 
/// Provides the name(), public_id() and system_id() functions. 
class CXmlDocType {
   friend class CXmlParser;
public:

   /// Constructor
   CXmlDocType() { m_html = false; }
   /// Constructor
   CXmlDocType(const char *name, const char *public_id=0, const char *system_id=0);

   /// Returns the name of the document type as specified in the <!DOCTYPE name> tag.
   const std::string &name() const { return m_name; }

   /// Returns the public identifier of the external DTD subset.
   /// Returns empty string if there is no public identifier.
   const std::string &publicID() const { return m_public_id; }

    /**
     * Returns the system identifier of the external DTD subset.
     * Returns empty string if there is no system identifier.
     */
   const std::string &systemID() const { return m_system_id; }

   /**
    * Returns a map of all entities described in the DTD.
    * NOTE: Map doesn't hold default entities.
    */
   CXmlEntities &entities() { return m_entities;  }

   /**
    * Returns a map of all entities described in the DTD.
    * NOTE: Map doesn't hold default entities.
    */
   const CXmlEntities &entities() const { return m_entities;  }

    /**
     * Encodes string to XML representation.
     * I.e. Converts "<test>" to "&lt;test&gt;"
     * Returns true, any entities replaced.
     * @param str const char *, string to convert
     * @param ret std::string &, converted string is stored to this.
     */
   bool encodeEntities(const char *str, std::string &ret);

    /**
     * Decodes entities in string to their actual values.
     * I.e. Converts "&lt;test&gt;" to "<test>"
     * Returns true, any entities expanded.
     * @param str string to convert
     * @param ret converted string is stored to this.
     */
   bool decodeEntities(const char *str, std::string &ret);

    /**
     * Search for entity with given name
     * Returns true, if attribute is found.
     * @param name const char *, entity to search
     */
   bool hasEntity(const char *name) const;

    /**
     * Remove named entity from entity map.
     * Returns true, if entity removed.
     * @param name entity to remove
     */
   void removeEntity(const char *name) { m_entities.removeEntity(name); }

    /**
     * Return replacement value for named entity.
     * If entity is not found, empty string is returned.
     * @param name const char *, entity name
     */
   std::string getReplacement(const char *name) const;

    /**
     * Add entity to map, If entity named 'name' exists already in map,
     * it's value is replaced with 'replacement'
     * @param name entity to add/change
     * @param replacement value that represents entity
     */
   void setEntity(const char *name, const char *replacement) { m_entities.setEntity(name, replacement); }

private:
   CXmlEntities m_entities;    ///< List of entities

   std::string m_name;         ///< Document type name
   std::string m_public_id;    ///< Public ID
   std::string m_system_id;    ///< System ID

   bool        m_html;         ///< HTML mode flag
};

/// @brief XML document.
///
/// Represents the entire XML document.
/// It provides access to document root node, which includes all nodes in XML document tree.
class CXmlDoc: public CXmlNode {
   friend class CXmlParser;
public:

   /// Constructs an empty document, without doctype.
   CXmlDoc();

   /// Constructs an empty document, with doctype.
   /// @param name const char *, name of the document.
   /// @param public_id const char *, public id of the document, placed on DOCTYPE declaration
   /// @param system_id const char *, system id of the document, placed on DOCTYPE declaration
   CXmlDoc(const char *name, const char *public_id=0, const char *system_id=0);

   /// Destructor
   virtual ~CXmlDoc() { clear(); }

   /// Destroys all nodes in document
   virtual void clear();

    /**
     * Returns doctype of document.
     * You can use it to add e.g. custom entities.
     <pre>
     mydoc->doctype().set_entity("myentity", "myreplacement");
     </pre>
     */
   CXmlDocType &docType() { return m_doctype; }

    /**
     * Returns doctype of document.
     * You can use it to add e.g. custom entities.
     <pre>
     mydoc->doctype().set_entity("myentity", "myreplacement");
     </pre>
     */
   const CXmlDocType &docType() const { return m_doctype; }

    /**
     * Returns pointer to root element of document.
     */
   CXmlNode *rootNode();

    /**
     * Returns indentation in save.
     */
   static int indentSpaces() { return m_indent_spaces; }

    /**
     * Set indentation in save, defaults to 2
     * @param i as new indent spaces
     */
   static void indentSpaces(int i) { m_indent_spaces = i; }

   /// Loads document from buffer.
   /// @param buffer const CBuffer&, source buffer
   /// @param htmlMode bool, true for HTML mode
   void load(const CBuffer &buffer,bool htmlMode=false);

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

protected:	

   /// Creates new named node of type CXmlNode::DOM_ELEMENT.
   /// It can be added to document DOM tree.
   /// @param tagname const char *, name of the element
   /// @see CXmlNode
   CXmlNode *createElement(const char *tagname);


private:
   CXmlDocType m_doctype;        ///< Document type
   static int  m_indent_spaces;  ///< Indent spaces
};

/// @}

#endif
