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

#ifndef XSD_CXX_XML_HXX
#define XSD_CXX_XML_HXX

#include <string>
#include <cassert>

#include <xercesc/dom/DOM.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/XMLUniDefs.hpp> // chLatin_L, etc
#include <xercesc/util/PlatformUtils.hpp>

namespace xsd
{
  namespace cxx
  {
    namespace xml
    {
      template <typename C>
      std::basic_string<C>
      transcode (XMLCh const* s, std::size_t length = 0);

      template <typename C>
      XMLCh*
      transcode (std::basic_string<C> const& s);

      //
      //
      class string
      {
      public :
        template <typename C>
        string (std::basic_string<C> const& s)
            : s_ (transcode<C> (s))
        {
        }

        template <typename C>
        string (C const* s)
            : s_ (transcode<C> (s))
        {
        }

        ~string ()
        {
          xercesc::XMLString::release (&s_);
        }

        XMLCh const*
        c_str () const
        {
          return s_;
        }

      private:
        string (string const&);

        string&
        operator= (string const&);

      private:
        XMLCh* s_;
      };


      template <>
      inline
      std::basic_string<char>
      transcode<char> (XMLCh const* s, std::size_t length)
      {
        assert (length == 0); //@@ TODO

        if (s == 0)
          return std::basic_string<char> ();

        char* buf (xercesc::XMLString::transcode (s));

        std::basic_string<char> r (buf);

        xercesc::XMLString::release (&buf);

        return r;
      }

      template <>
      inline
      std::basic_string<wchar_t>
      transcode<wchar_t> (XMLCh const* s, std::size_t length)
      {
        if (s == 0)
          return std::basic_string<wchar_t> ();

        // std::wcerr << s << std::endl;

        std::size_t l (0);

        if (length)
        {
          for (; l < length; ++l)
          {
            if (s[l] == 0)
              break;
          }
        }
        else
          l = xercesc::XMLString::stringLen (s);

        std::basic_string<wchar_t> r (l, L'0');

        for (std::size_t i (0); i <= l; ++s, ++i)
        {
          r[i] = *s;
        }

        return r;
      }

      template <>
      inline
      XMLCh*
      transcode (std::basic_string<char> const& s)
      {
        return xercesc::XMLString::transcode (s.c_str ());
      }

      template <>
      inline
      XMLCh*
      transcode (std::basic_string<wchar_t> const& s)
      {
        std::size_t l (s.length ());

        XMLCh* r (new XMLCh[l + 1]);
        XMLCh* ir (r);

        for (std::size_t i (0); i < l; ++ir, ++i)
        {
          *ir = static_cast<XMLCh>(s[i]);
          //std::wcerr << s[i] << "->" << *ir << std::endl;
        }

        *ir = XMLCh (0);

        // std::wcerr << r << std::endl;

        return r;
      }


      template <typename C>
      class element;

      template <typename C>
      std::basic_string<C>
      ns_prefix (std::basic_string<C> const& ns, element<C> const& e);

      template <typename C>
      class element
      {
        typedef std::basic_string<C> string_type;

      public:
        element (xercesc::DOMElement const* e)
            : e_ (0),
              ce_ (e),
              name_ (transcode<C> (e->getLocalName ())),
              namespace__ (transcode<C> (e->getNamespaceURI ()))
        {
        }

        element (xercesc::DOMElement* e)
            : e_ (e),
              ce_ (e),
              name_ (transcode<C> (e->getLocalName ())),
              namespace__ (transcode<C> (e->getNamespaceURI ()))
        {
        }

        element (string_type const& name, element& parent)
            : e_ (0),
              ce_ (0),
              name_ (name)
        {
          xercesc::DOMDocument* doc (
            parent.dom_element ()->getOwnerDocument ());

          e_ = doc->createElement (string (name).c_str ());

          parent.dom_element ()->appendChild (e_);

          ce_ = e_;
        }

        element (string_type const& name,
                 string_type const& ns,
                 element& parent)
            : e_ (0),
              ce_ (0),
              name_ (name),
              namespace__ (ns)
        {
          string_type prefix (ns_prefix (ns, parent));

          xercesc::DOMDocument* doc (
            parent.dom_element ()->getOwnerDocument ());

          e_ = doc->createElementNS (
            string (ns).c_str (),
            string (prefix.empty ()
                    ? name
                    : prefix + string_type (1, ':') + name).c_str ());

          parent.dom_element ()->appendChild (e_);

          ce_ = e_;
        }

      public:
        string_type
        name () const
        {
          return name_;
        }

        string_type
        namespace_ () const
        {
          return namespace__;
        }

      public:
        element<C>
        parent () const
        {
          return
            dynamic_cast<xercesc::DOMElement const*> (ce_->getParentNode ());
        }

      public:
        string_type
        value () const
        {
          return transcode<C> (dom_element ()->getTextContent ());
        }

        void
        value (string_type const& v)
        {
          xercesc::DOMText* text (
            dom_element ()->getOwnerDocument ()->createTextNode(
              string (v).c_str ()));

          dom_element ()->appendChild (text);
        }

      public:
        string_type
        operator[] (string_type const& s) const
        {
          XMLCh const* value (ce_->getAttribute (string (s).c_str ()));

          return transcode<C> (value);
        }

      public:
        xercesc::DOMElement const*
        dom_element () const
        {
          return ce_;
        }

        xercesc::DOMElement*
        dom_element ()
        {
          return e_;
        }

      private:
        xercesc::DOMElement* e_;
        xercesc::DOMElement const* ce_;

        string_type name_;
        string_type namespace__;
      };


      template <typename C>
      class attribute
      {
        typedef std::basic_string<C> string_type;

      public:
        attribute (xercesc::DOMAttr const* a)
            : a_ (0),
              ca_ (a),
              name_ (transcode<C> (a->getLocalName ())),
              value_ (transcode<C> (a->getValue ()))
        {
        }

        attribute (xercesc::DOMAttr* a)
            : a_ (a),
              ca_ (a),
              name_ (transcode<C> (a->getLocalName ())),
              value_ (transcode<C> (a->getValue ()))
        {
        }

        attribute (string_type const& name,
                   string_type const& v,
                   element<C>& parent)
            : a_ (0),
              ca_ (0),
              name_ (name),
              value_ ()
        {
          xercesc::DOMDocument* doc (
            parent.dom_element ()->getOwnerDocument ());

          a_ = doc->createAttribute (string (name).c_str ());

          value (v);

          parent.dom_element ()->setAttributeNode (a_);

          ca_ = a_;
        }

        attribute (string_type const& name,
                   string_type const& ns,
                   string_type const& v,
                   element<C>& parent)
            : a_ (0),
              ca_ (0),
              name_ (name),
              value_ ()
        {
          string_type prefix (ns_prefix (ns, parent));

          xercesc::DOMDocument* doc (
            parent.dom_element ()->getOwnerDocument ());

          a_ = doc->createAttributeNS (
            string (ns).c_str (),
            string (prefix.empty ()
                    ? name
                    : prefix + string_type (1, ':') + name).c_str ());

          value (v);

          parent.dom_element ()->setAttributeNodeNS (a_);

          ca_ = a_;
        }

        string_type
        name () const
        {
          return name_;
        }

        string_type
        value () const
        {
          return value_;
        }

        void
        value (string_type const& v)
        {
          value_ = v;
          a_->setValue (string (v).c_str ());
        }

        xml::element<C>
        element () const
        {
          return xml::element<C> (
            static_cast<xercesc::DOMElement const*> (
              ca_->getOwnerElement ()));
        }

        xml::element<C>
        element ()
        {
          return xml::element<C> (a_->getOwnerElement ());
        }


      public:
        xercesc::DOMAttr const*
        dom_attribute () const
        {
          return ca_;
        }

        xercesc::DOMAttr*
        dom_attribute ()
        {
          return a_;
        }

      private:

      private:
        xercesc::DOMAttr* a_;
        xercesc::DOMAttr const* ca_;

        string_type name_;
        string_type value_;
      };

      template <typename C>
      std::basic_string<C>
      prefix (std::basic_string<C> const& n)
      {
        std::size_t i (0);

        while (i < n.length () && n[i] != ':')
          ++i;

        //std::wcerr << "prefix " << n << " "
        //           << std::wstring (n, i == n.length () ? i : 0, i)
        //           << std::endl;

        return std::basic_string<C> (n, i == n.length () ? i : 0, i);
      }

      template <typename C>
      std::basic_string<C>
      uq_name (std::basic_string<C> const& n)
      {
        std::size_t i (0);

        while (i < n.length () && n[i] != ':')
          ++i;

        return
          std::basic_string<C> (n.c_str () + (i == n.length () ? 0 : i + 1));
      }

      template <typename C>
      std::basic_string<C>
      ns_name (element<C> const& e, std::basic_string<C> const& n)
      {
        std::basic_string<C> wp (prefix (n));

        XMLCh const* xns (e.dom_element ()->lookupNamespaceURI (
                            wp.empty () ? 0 : string (wp).c_str ()));


        std::basic_string<C> ns (
          xns ? transcode<C> (xns) : std::basic_string<C> ());

        return ns;

      }

      template <typename C>
      std::basic_string<C>
      fq_name (element<C> const& e, std::basic_string<C> const& n)
      {
        std::basic_string<C> ns (ns_name (e, n));
        std::basic_string<C> un (uq_name (n));

        return ns.empty () ? un : (ns + C ('#') + un);
      }

      class no_prefix {};

      template <typename C>
      std::basic_string<C>
      ns_prefix (std::basic_string<C> const& ns, element<C> const& e)
      {
        string xns (ns);

        XMLCh const* p (
          e.dom_element ()->lookupNamespacePrefix (xns.c_str (), false));

        if (p == 0)
        {
          bool r (e.dom_element ()->isDefaultNamespace (xns.c_str ()));

          if (r)
            return std::basic_string<C> ();
          else
            throw no_prefix ();
        }

        return transcode<C> (p);
      }

      //
      //
      template <typename C>
      class error_handler : public  xercesc::DOMErrorHandler
      {
      public:
        error_handler (xercesc::DOMErrorHandler* eh = 0)
            : failed_ (false), eh_ (eh)
        {
        }

        virtual bool
        handleError (xercesc::DOMError const& e);

        bool
        failed () const
        {
          return failed_;
        }

      private:
        bool failed_;
        xercesc::DOMErrorHandler* eh_;
      };


      // Simple auto_ptr version that calls release() instead of delete.
      //
      template <typename X>
      struct auto_ptr
      {
        auto_ptr (X* x)
            : x_ (x)
        {
        }

        ~auto_ptr ()
        {
          if (x_)
            x_->release ();
        }

        X&
        operator* () const
        {
          return *x_;
        }

        X*
        operator-> () const
        {
          return x_;
        }

        // Conversion to bool.
        //
        typedef X* (auto_ptr::*boolean_convertible)() const;

        operator boolean_convertible () const throw ()
        {
          return x_ ? &auto_ptr<X>::operator-> : 0;
        }

      private:
        X* x_;
      };

      template <typename C>
      xercesc::DOMDocument*
      dom (xercesc::DOMInputSource& is, xercesc::DOMErrorHandler* eh);

      template <typename C>
      xercesc::DOMDocument*
      dom (std::basic_string<C> const& uri, xercesc::DOMErrorHandler* eh);

      inline void
      initialize ()
      {
        // Initialize Xerces runtime.
        //
        xercesc::XMLPlatformUtils::Initialize();
      }

      inline void
      terminate ()
      {
        // Initialize Xerces runtime.
        //
        xercesc::XMLPlatformUtils::Terminate();
      }
    }
  }
}

#include <xsd/cxx/xml.ixx>
#include <xsd/cxx/xml.txx>

#endif  // XSD_CXX_XML_HXX
