#ifndef BABBLEMUD_XMLTREE_H
#define BABBLEMUD_XMLTREE_H

#include <string>
#include <list>
#include <map>

#include "strings.h"
#include "entities.h"

using namespace std;

#define MAX_XML_DOCUMENT_SIZE (1000 * 1024)   // max XML file length - 1,000 Kb
#define MAX_XML_NAME_LENGTH 255        // max name of element (eg. <trigger> )


// each node has attributes (eg. name="nick")
// contents (eg. <foo>bar</foo> ) - in this case "bar" is the contents
// and possible children (eg. <child> blah </child> )
  
class XmlNode
  {
protected:
  
  const string name_;                 // node name (eg. <foo x="22"> has name "foo")
  const string::size_type position_;  // character position it appeared on in XML document
  bool used_;                         // did we ever use this node?
  bool empty_;                        // is node empty? (eg. <foo name="nick" /> )
 
  public:

  StringMap attributes_;  // its attributes (eg. age="22" )
  string              contents_;    // contents of node (eg. <foo>bar</foo> - contents is "bar")
  list<XmlNode>       children_;    // its children   (nodes within nodes)

  // ctor
  XmlNode (const string &           name = "", 
           const string::size_type  position = 0,
           const string &           contents = "")
    : name_ (name), position_ (position)
      { Initialise (contents); }
  
  void MarkUsed   (const bool & used = true)  { used_ = used; }
  void MarkEmpty  (const bool & empty = true) { empty_ = empty; }

  const string::size_type GetPosition () const { return position_; }
  const bool              IsUsed      () const { return used_; }
  const bool              IsEmpty     () const { return empty_; }
  const string            GetName     () const { return name_; }
  // return an attribute as const
  const string operator[] (const string & key) const 
    {
    StringMap::const_iterator i = attributes_.find (key);
    if (i == attributes_.end ())
       return "";
    else
      return i->second;
    }
  
  void Initialise (const string & contents = "")
    {
    contents_ = contents;;  // set contents
    MarkUsed (false);       // not used
    MarkEmpty (false);      // not empty
    children_.clear ();     // no children
    attributes_.clear ();   // no attributes
    };
  
  };  // end of XmlNode
  
class XmlParser 
  {

protected:

  string &            xmlBuffer_;       //  buffer for XML parsing
  XmlNode &           root_;            // root element
  string::size_type   xmlPosition_;     // where we are in the buffer  
  StringMap           customEntities_;  // custom XML entities
  string              documentName_;    // root document name  (from <!DOCTYPE name .. ?> )

  void SkipComments (const bool bAndSpaces);
  void SkipSpaces ();
  void ProcessNode (XmlNode & parent);
  void ProcessAttributes (const string sNodeName, XmlNode & node);
  void ProcessDeclaration (const string strDeclaration);
  void ProcessDoctype ();
  void ProcessDTD ();
  void ProcessEntity ();

  string GetName (const string & sType);
  string GetValue (const string & sType, 
                   const string sName, 
                   string::size_type & iStart);

  string ReplaceEntities (const string & s) const
    {
    return ::ReplaceEntities (s, &customEntities_);
    }

  public:

   // constructor - pass down a text buffer, and a root node
  XmlParser (string & sText, XmlNode & root) 
    : xmlBuffer_ (sText), root_ (root), xmlPosition_ (0) { };

  // builds an XML structure from the supplied string
  void Parse ();

  string::size_type GetPosition () const { return xmlPosition_; }
        
  };    // end of class XmlParser

list<XmlNode>::const_iterator FindNode (const list<XmlNode> & nodelist, 
                                        const string & name);
list<XmlNode>::const_iterator NeedNode (const list<XmlNode> & nodelist, 
                                        const string & name);
void ShowAttribute (const pair<const string, const string> & attribute); 
void ShowElement (const XmlNode & element);
  
#endif // BABBLEMUD_XMLTREE_H

