// file      : xsd/cxx/tree/elements.hxx
// author    : Boris Kolpackov <boris@codesynthesis.com>
// copyright : Copyright (c) 2005-2006 Code Synthesis Tools CC
// license   : GNU GPL v2 + exceptions; see accompanying LICENSE file

#ifndef XSD_CXX_TREE_ELEMENTS_HXX
#define XSD_CXX_TREE_ELEMENTS_HXX

#include <map>
#include <string>
#include <memory>  // std::auto_ptr
#include <istream>
#include <ostream>
#include <sstream>
#include <cassert>

#include <xsd/cxx/xml/dom/elements.hxx>

#include <xsd/cxx/tree/exceptions.hxx>

namespace xsd
{
  namespace cxx
  {
    namespace tree
    {
      //
      //
      struct flags
      {
        // Use the following flags to modify the default behavior
        // of the parsing and serialization functions.
        //

        // Keep DOM association in the resulting tree. Useful for
        // dealing with "type-less" content such as mixed content
        // models, any/anyAttribute, and anyType/anySimpleType.
        //
        static unsigned long const keep_dom = 0x00000001;

        // Do not try to validate instance documents. Note that
        // the xsd runtime assumes instance documents are valid so
        // you had better make sure they are if you specify this flag.
        //
        //
        static unsigned long const dont_validate = 0x00000002;

        // Do not initialize the Xerces-C++ runtime.
        //
        static unsigned long const dont_initialize = 0x00000004;


        // The following flags are for internal use.
        //
        static unsigned long const not_root = 0x00000008;

      public:
        flags (unsigned long x = 0)
            : x_ (x)
        {
        }

        operator unsigned long () const
        {
          return x_;
        }

      private:
        unsigned long x_;
      };

      // Parsing properties. Refer to xsd/cxx/xml/elements.hxx for XML-
      // related properties.
      //
      template <typename C>
      class properties: public xml::properties<C>
      {
      };

      //
      //
      struct identity
      {
        virtual
        ~identity ()
        {
        }

        identity ()
        {
        }

        virtual bool
        before (identity const&) const = 0;

        virtual void
        throw_duplicate_id () const = 0;

      private:
        identity (identity const&);

        identity&
        operator= (identity const&);
      };


      // anyType
      //
      class type
      {
      public:
        virtual
        ~type ()
        {
          // Everyhting should have been unregistered by now.
          //
          assert (map_.get () == 0 || map_->size () == 0);
        }

      public:
        type ()
            : container_ (0)
        {
        }

        template <typename C>
        type (xml::dom::element<C> const& e, flags f, type* container)
            : dom_info_ (dom_info_factory::create (e, f)),
              container_ (container)
        {
        }

        template <typename C>
        type (xml::dom::attribute<C> const& a, flags f, type* container)
            : dom_info_ (dom_info_factory::create (a, f)),
              container_ (container)
        {
        }

        template <typename C>
        type (std::basic_string<C> const&,
              xml::dom::element<C> const*,
              flags,
              type* container)
            : dom_info_ (0), // List elements don't have associated DOM nodes.
              container_ (container)
        {
        }

        type (type const& t, flags f = 0, type* container = 0)
            : container_ (container)
        {
          if (t.dom_info_.get ())
          {
            std::auto_ptr<dom_info> r (t.dom_info_->clone (f, container));
            dom_info_ = r;
          }
        }

      public:
        type&
        operator= (type const&)
        {
          return *this;
        }

      public:
        virtual type*
        _clone (flags f = 0, type* container = 0) const
        {
          return new type (*this, f, container);
        }

        // Container API.
        //
      public:
        type const*
        _container () const
        {
          return container_ ? container_ : this;
        }

        type*
        _container ()
        {
          return container_ ? container_ : this;
        }


        type const*
        _root () const
        {
          type const* r (_container ());

          for (type const* c = r->_container ();
               c != r;
               c = c->_container ())
            r = c;

          return r;
        }

        type*
        _root ()
        {
          type* r (_container ());

          for (type* c = r->_container (); c != r; c = c->_container ())
            r = c;

          return r;
        }

      public:
        /*
        virtual void
        container (type* c)
        {
          if (container_ == c)
            return;

          // Revoke registrations from our ex-container.
          //
          if (container_ != 0 && map_.get ())
          {
            for (map::iterator i (map_->begin ()); i != map_->end (); ++i)
            {
              //std::wcerr << "revoking " << i->second
              //           << " to " << container_ << std::endl;

              container_->unregister_id (*(i->first));
            }
          }

          // Copy registrations to our new container.
          //
          if (c != 0 && map_.get ())
          {
            for (map::iterator i (map_->begin ()); i != map_->end (); ++i)
            {
              //std::wcerr << "copying " << i->second
              //           << " to " << c << std::endl;

              c->register_id (*(i->first), i->second);
            }
          }

          container_  = c;
        }
        */

      public:
        void
        _register_id (identity const& id, type* t)
        {
          if (map_.get () == 0)
            map_.reset (new map);

          // First register on our container. If there are any duplications,
          // they will be detected by this call and we don't need to clean
          // the map.
          //
          if (_container () != this)
	    _container ()->_register_id (id, t);

          if (!map_->insert (
                std::pair<identity const*, type*> (&id, t)).second)
          {
            id.throw_duplicate_id ();
          }
        }

        //@@ Does not inherit from exception.
        //
        struct not_registered: virtual std::exception
        {
          virtual char const*
          what () const throw ()
          {
            return "attempt to unregister non-existent id";
          }
        };

        void
        _unregister_id (identity const& id)
        {
          if (map_.get ())
          {
            map::iterator it (map_->find (&id));

            if (it != map_->end ())
            {
              map_->erase (it);

              if (_container () != this)
	        _container ()->_unregister_id (id);

              return;
            }
          }

          throw not_registered ();
        }

        type*
        _lookup_id (identity const& id) const
        {
          if (map_.get ())
          {
            map::const_iterator it (map_->find (&id));

            if (it != map_->end ())
              return it->second;
          }

          return 0;
        }

        // DOM association.
        //
      public:
        xercesc::DOMNode const*
        _node () const
        {
          return dom_info_.get () ? dom_info_->node() : 0;
        }

        xercesc::DOMNode*
        _node ()
        {
          return dom_info_.get () ? dom_info_->node () : 0;
        }

      private:
        struct dom_info
        {
          virtual
          ~dom_info ()
          {
          }

          dom_info ()
          {
          }

          virtual std::auto_ptr<dom_info>
          clone (flags f, type* c) const = 0;

          virtual xercesc::DOMNode*
          node () = 0;

        private:
          dom_info (dom_info const&);

          dom_info&
          operator= (dom_info const&);
        };


        struct dom_element_info: public dom_info
        {
          dom_element_info (xercesc::DOMElement& e, flags f)
              : doc_ ((f & flags::not_root) ? 0 : e.getOwnerDocument ()),
                e_ (e)
          {
            assert (f & flags::keep_dom);
          }

          virtual std::auto_ptr<dom_info>
          clone (flags f, type* c) const
          {
            using std::auto_ptr;

            if ((f & flags::not_root) == 0)
            {
              // We preserver DOM associations only in complete
              // copies from root.
              //
              if (doc_.get () == 0)
                return auto_ptr<dom_info> (0);

              return auto_ptr<dom_info> (new dom_element_info (*doc_));
            }

            // We are not a root node so container shouldn't be 0.
            //
            assert (c != 0);


            // Now we are going to find the corresponding element in
            // the new tree.
            //
            {
              using xercesc::DOMNode;
              using xercesc::DOMElement;
              using xercesc::DOMNodeList;

              DOMNode& pn (*e_.getParentNode ());
              assert (pn.getNodeType () == DOMNode::ELEMENT_NODE);
              DOMElement& p (static_cast<DOMElement&> (pn));

              DOMNodeList& nl (*p.getChildNodes ());

              XMLSize_t size (nl.getLength ()), i (0);

              // We should have at least one child.
              //
              assert (size != 0);

              for ( ;i < size && !e_.isSameNode (nl.item (i)); ++i);

              // e_ should be in the list.
              //
              assert (i < size);

              DOMNode& n (*c->_node ()->getChildNodes ()->item (i));
              assert (n.getNodeType () == DOMNode::ELEMENT_NODE);

              return auto_ptr<dom_info> (
                new dom_element_info (
                  static_cast<DOMElement&> (n), f | flags::keep_dom));
            }
          }

          virtual xercesc::DOMNode*
          node ()
          {
            return &e_;
          }

        private:
          dom_element_info (xercesc::DOMDocument const& d)
              : doc_ (static_cast<xercesc::DOMDocument*> (
                        d.cloneNode (true))),
                e_ (*doc_->getDocumentElement ())
          {
          }

        private:
          xml::dom::auto_ptr<xercesc::DOMDocument> doc_;
          xercesc::DOMElement& e_;
        };


        struct dom_attribute_info: public dom_info
        {
          dom_attribute_info (xercesc::DOMAttr& a, flags f)
              : a_ (a)
          {
            assert (f & flags::not_root);
            assert (f & flags::keep_dom);
          }

          virtual std::auto_ptr<dom_info>
          clone (flags f, type* c) const
          {
            using std::auto_ptr;

            if ((f & flags::not_root) == 0)
            {
              // We preserver DOM associations only in complete
              // copies from root.
              //
              return auto_ptr<dom_info> (0);
            }

            // We are not a root node so container shouldn't be 0.
            //
            assert (c != 0);

            // We are going to find the corresponding attribute in
            // the new tree.
            //
            using xercesc::DOMNode;
            using xercesc::DOMAttr;
            using xercesc::DOMElement;
            using xercesc::DOMNamedNodeMap;

            DOMElement& p (*a_.getOwnerElement ());
            DOMNamedNodeMap& nl (*p.getAttributes ());

            XMLSize_t size (nl.getLength ()), i (0);

            // We should have at least one child.
            //
            assert (size != 0);

            for ( ;i < size && !a_.isSameNode (nl.item (i)); ++i);

            // a_ should be in the list.
            //
            assert (i < size);

            DOMNode& n (*c->_node ()->getAttributes ()->item (i));
            assert (n.getNodeType () == DOMNode::ATTRIBUTE_NODE);

            return auto_ptr<dom_info> (
              new dom_attribute_info (
                static_cast<DOMAttr&> (n), f | flags::keep_dom));
          }

          virtual xercesc::DOMNode*
          node ()
          {
            return &a_;
          }

        private:
          xercesc::DOMAttr& a_;
        };

        // For Sun C++ 5.6.
        //
        struct dom_info_factory;
        friend struct type::dom_info_factory;

        struct dom_info_factory
        {
          template <typename C>
          static std::auto_ptr<dom_info>
          create (xml::dom::element<C> const& e, flags f)
          {
            using std::auto_ptr;

            if ((f & flags::keep_dom) == 0)
              return auto_ptr<dom_info> (0);

            return auto_ptr<dom_info> (
              new dom_element_info (
                const_cast<xercesc::DOMElement&> (*e.dom_element ()), f));
          }

          template <typename C>
          static std::auto_ptr<dom_info>
          create (xml::dom::attribute<C> const& a, flags f)
          {
            using std::auto_ptr;

            if ((f & flags::keep_dom) == 0)
              return auto_ptr<dom_info> (0);

            return auto_ptr<dom_info> (
              new dom_attribute_info (
                const_cast<xercesc::DOMAttr&> (*a.dom_attribute ()), f));
          }
        };


        std::auto_ptr<dom_info> dom_info_;


        // ID/IDREF map.
        //
      private:
        struct identity_comparator
        {
          bool operator () (identity const* x, identity const* y) const
          {
            return x->before (*y);
          }
        };

        typedef
        std::map<identity const*, type*, identity_comparator>
        map;

        std::auto_ptr<map> map_;

      private:
        type* container_;
      };

      template <typename C>
      inline std::basic_ostream<C>&
      operator<< (std::basic_ostream<C>& os, type const&)
      {
        return os;
      }


      // anySimpleType
      //
      class simple_type: public type
      {
      public:
        simple_type ()
        {
        }

        template <typename C>
        simple_type (xml::dom::element<C> const& e, flags f, type* container)
            : type (e, f, container)
        {
        }

        template <typename C>
        simple_type (xml::dom::attribute<C> const& a,
                     flags f,
                     type* container)
            : type (a, f, container)
        {
        }

        template <typename C>
        simple_type (std::basic_string<C> const& s,
                     xml::dom::element<C> const* e,
                     flags f,
                     type* container)
            : type (s, e, f, container)
        {
        }

        simple_type (simple_type const& other,
                     flags f = 0,
                     type* container = 0)
            : type (other, f, container)
        {
        }

      public:
        virtual simple_type*
        _clone (flags f = 0, type* container = 0) const
        {
          return new simple_type (*this, f, container);
        }
      };

      template <typename C>
      inline std::basic_ostream<C>&
      operator<< (std::basic_ostream<C>& os, simple_type const&)
      {
        return os;
      }

      //
      //
      template <typename T>
      struct traits
      {
        static bool
        same_container (T& i, type* c)
        {
          return i._container () == c;
        }

        template <typename C>
        static std::auto_ptr<T>
        create (xml::dom::element<C> const& e,
                flags f,
                type* container)
        {
          return std::auto_ptr<T> (new T (e, f, container));
        }

        template <typename C>
        static std::auto_ptr<T>
        create (xml::dom::attribute<C> const& a,
                flags f,
                type* container)
        {
          return std::auto_ptr<T> (new T (a, f, container));
        }

        template <typename C>
        static std::auto_ptr<T>
        create (std::basic_string<C> const& s,
                xml::dom::element<C> const& parent,
                flags f,
                type* container)
        {
          return std::auto_ptr<T> (new T (s, &parent, f, container));
        }

        template <typename C>
        static std::auto_ptr<T>
        create (std::basic_string<C> const& s,
                flags f,
                type* container)
        {
          return std::auto_ptr<T> (new T (s, 0, f, container));
        }

        static std::auto_ptr<T>
        clone (T const& other, flags f, type* container)
        {
          return std::auto_ptr<T> (other._clone (f, container));
        }
      };


      // Proxy pointer that help to fake inheritance from fundamental types.
      //
      /*
      template <typename X>
      class proxy_ptr
      {
      public:
        proxy_ptr (X& x)
            : x_ (x)
        {
        }

        operator X* ()
        {
          return x_.real_ptr ();
        }

        operator typename X::base_type* ()
        {
          return &x_.b_;
        }

      private:
        X& x_;
      };
      */

      namespace bits
      {
        template<typename X, typename Y>
        struct return_type
        {
          typedef Y r;
        };

        template<typename X>
        struct return_type<X, X>
        {
          typedef X& r;
        };
      }

      // Fundamental base template.
      //
      template <typename X>
      class fundamental_base: public simple_type
      {
        template <typename>
        friend class proxy_ptr;

        typedef X base_type;

      public:
        fundamental_base ()
            : x_ ()
        {
        }

        fundamental_base (X x)
            : x_ (x)
        {
        }

        fundamental_base (fundamental_base const& other,
                          flags f = 0,
                          type* container = 0)
            : simple_type (other, f, container),
              x_ (other.x_)
        {
        }

        fundamental_base&
        operator= (X const& x)
        {
          if (&x_ != &x)
            x_ = x;

          return *this;
        }

      public:
        template <typename C>
        fundamental_base (xml::dom::element<C> const& e,
                          flags f,
                          type* container)
            : simple_type (e, f, container),
              x_ (traits<X>::create (e, f, container))
        {
        }

        template <typename C>
        fundamental_base (xml::dom::attribute<C> const& a,
                          flags f,
                          type* container)
            : simple_type (a, f, container),
              x_ (traits<X>::create (a, f, container))
        {
        }

        template <typename C>
        fundamental_base (std::basic_string<C> const& s,
                          xml::dom::element<C> const* e,
                          flags f,
                          type* container)
            : simple_type (s, e, f, container),
              x_ (traits<X>::create (s, f, container))
        {
        }

      public:
        operator X const& () const
        {
          return x_;
        }

        operator X& ()
        {
          return x_;
        }

        template <typename Y>
        operator Y () const
        {
          return Y (x_);
        }

        /*
          This is a nice idea but it doesn't work well
          with function templates, e.g.:

          template<X>
          void destroy (X*);

        proxy_ptr<fundamental_base const>
        operator& () const
        {
          return proxy_ptr<fundamental_base const> (*this);
        }

        proxy_ptr<fundamental_base>
        operator& ()
        {
          return proxy_ptr<fundamental_base> (*this);
        }


      private:
        fundamental_base const*
        real_ptr () const
        {
          return this;
        }

        fundamental_base*
        real_ptr ()
        {
          return this;
        }
        */

      public:
        virtual fundamental_base*
        _clone (flags f = 0, type* container = 0) const
        {
          return new fundamental_base (*this, f, container);
        }

      private:
        X x_;
      };

      template <typename C, typename X>
      inline
      std::basic_ostream<C>&
      operator<< (std::basic_ostream<C>& os, fundamental_base<X> x)
      {
        X const& r (x);
        return os << r;
      }

      template <typename C, typename X>
      inline
      std::basic_istream<C>&
      operator>> (std::basic_istream<C>& is, fundamental_base<X>& x)
      {
        X& r (x);
        return is >> r;
      }
    }
  }
}

#endif  // XSD_CXX_TREE_ELEMENTS_HXX

// #include <xsd/cxx/tree/elements.ixx>

// Traits for C++ fundamental types.
//
#include <xsd/cxx/tree/traits.hxx>
