/*****************************************************************************
   file         : $Id: nodes.h,v 1.12 2006/09/08 13:25:49 nils Exp $
   description  :
   ------------------------------------------------------------------------

   copyright    : (C) 2006 by Nils Springob, Aachen, GERMANY
   email        : nils.springob@nicai-systems.de
   project      : nicai-systems library

 *****************************************************************************/

#ifndef _NICAI_XML_NODES_H_
#define _NICAI_XML_NODES_H_

#include "xml/xml.h"

#include <map>
#include <string>
#include <boost/lexical_cast.hpp>

namespace nicai {
namespace xml {


enum nodetypes {
    // note: values are synchronized with xmlElementType from libxml
    NULL_NODE        = 0,
    TAG_NODE         = 1,
    ATTRIBUTE_NODE   = 2,
    TEXT_NODE        = 3,
    CDATA_NODE       = 4,
    INSTRUCTION_NODE = 7,
    COMMENT_NODE     = 8,
    DOCUMENT_NODE    = 9,

    ENDTAG_NODE      = 20,
    ENDDOCUMENT_NODE = 21,
    POLY_NODE        = 22
} ;

/*****************************************************************************/

class node {
    private:
        nodetypes nodeType;
    protected:
        node(nodetypes _nodeType) : nodeType(_nodeType) {}
    public:
        bool operator== (const node & node) const;
        virtual ~node() = 0;
        virtual node * clone() const = 0;
        nodetypes getNodeType() const {if (this) return nodeType; return NULL_NODE;}
        bool isDocument() const {return nodeType==DOCUMENT_NODE;}
        bool isTag() const {return nodeType==TAG_NODE;}
        bool isComment() const {return nodeType==COMMENT_NODE;}
        bool isInstruction() const {return nodeType==INSTRUCTION_NODE;}
        bool isCdata() const {return nodeType==CDATA_NODE;}
        bool isAttribute() const {return nodeType==ATTRIBUTE_NODE;}
        bool isEndTag() const {return nodeType==ENDTAG_NODE;}
        bool isEndDocument() const {return nodeType==ENDDOCUMENT_NODE;}
        bool isText() const {return nodeType==TEXT_NODE;}
};


class document : public node {
    private:
        std::string encoding;
        std::string standalone;
    public:
        document() : node(DOCUMENT_NODE) {}
        document(const std::string & _encoding, const std::string & _standalone) : node(DOCUMENT_NODE), encoding(_encoding), standalone(_standalone) {}
        virtual ~document() {}
        virtual document * clone() const {return new document(*this);}
        const std::string & getEncoding() const {return encoding;}
        const std::string & getStandalone() const {return standalone;}
        bool operator==(const document &) const {return true;}
};


class tag : public node {
    private:
        std::string name;
    public:
        tag(const std::string & _name) : node(TAG_NODE), name(_name) {}
        virtual ~tag() {}
        virtual tag * clone() const {return new tag(*this);}
        const std::string & getName() const {return name;}
        bool operator==(const tag & node) const {return name==node.name;}
};


class comment : public node {
    private:
        std::string str;
    public:
        comment(const std::string & _str) : node(COMMENT_NODE), str(_str) {}
        virtual ~comment() {}
        virtual comment * clone() const {return new comment(*this);}
        const std::string & getText() const {return str;}
        bool operator==(const comment & node) const {return str==node.str;}
};


class instruction : public node {
    private:
        std::string target;
        std::string data;
    public:
        instruction(const std::string & _target, const std::string & _data) : node(INSTRUCTION_NODE), target(_target), data(_data) {}
        virtual ~instruction() {}
        virtual instruction * clone() const {return new instruction(*this);}
        const std::string & getTarget() const {return target;}
        const std::string & getData() const {return data;}
        bool operator==(const instruction & node) const {return (target==node.target) && (data==node.data);}
};


class cdata : public node {
    private:
        std::string data;
    public:
        cdata(const std::string & _data) : node(CDATA_NODE),  data(_data) {}
        virtual ~cdata() {}
        virtual cdata * clone() const {return new cdata(*this);}
        const std::string & getData() const {return data;}
        bool operator==(const cdata & node) const {return data==node.data;}
};


class attribute : public node {
    private:
        std::string name;
        std::string val;
    public:
        attribute(const std::string & _name, const std::string & _val) : node(ATTRIBUTE_NODE),  name(_name), val(_val) {}
        explicit attribute(const std::pair<const std::string, std::string> & p) : node(ATTRIBUTE_NODE),  name(p.first), val(p.second) {}
        virtual ~attribute() {}
        virtual attribute * clone() const {return new attribute(*this);}
        const std::string & getName() const {return name;}
        const std::string & getVal() const {return val;}
        bool operator==(const attribute & node) const {return (name==node.name) && (val==node.val);}
};


class endtag : public node {
    public:
        endtag() : node(ENDTAG_NODE) {}
        virtual ~endtag() {}
        virtual endtag * clone() const {return new endtag(*this);}
        bool operator==(const endtag &) const {return true;}
};


class enddocument : public node {
    public:
        enddocument() : node(ENDDOCUMENT_NODE) {}
        virtual ~enddocument() {}
        virtual enddocument * clone() const {return new enddocument(*this);}
        bool operator==(const enddocument &) const {return true;}
};


class text : public node {
    private:
        std::string str;
    public:
        text(const std::string & _str) : node(TEXT_NODE),  str(_str) {}
        virtual ~text() {}
        virtual text * clone() const {return new text(*this);}
        const std::string & getText() const {return str;}
        bool operator==(const text & node) const {return str==node.str;}
};


class polynode : public node {
    public:
        polynode() : node(POLY_NODE) {}
        virtual ~polynode() {}
        virtual oxmlbasestream & tostream(oxmlbasestream & xout) const = 0;
        bool operator==(const polynode & node) const {return false; /* FIXME */}
};


class attributemap {
    private:
        typedef std::map<std::string, std::string> maptype;
        std::vector<std::string> indices;
        maptype data;

    public:

        template <class T>
        const T get (const std::string & name, const T & defaultVal) const {
            T result;
            if (boost::conversion::try_lexical_convert(get(name), result)) return result; else return defaultVal;
        }
        
        const std::string get (const std::string & name) const {if (contains(name)) return const_cast<attributemap *>(this)->get(name); return std::string();}
        std::string & get (const std::string & name) {
            maptype::iterator it = data.find(name);
            if (it==data.end())
            {
                it = data.insert(std::pair<const std::string, std::string>(name, std::string())).first;
                indices.push_back(name);
            }
            return it->second;
        }
        std::pair<const std::string, std::string> operator[] (int idx) const {return *data.find(indices[idx]);}
        int size() const {return indices.size();}
        void clear() {data.clear(); indices.clear();}
        bool contains(const std::string & name) const {return data.find(name)!=data.end();}

        // syntactical sugar:
        const std::string operator[] (const std::string & name) const {return get(name);}
        std::string & operator[] (const std::string & name) {return get(name);}
};


} // namespace
} // namespace

#endif


