// file      : xsd/cxx/tree/elements.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_TREE_ELEMENTS_HXX
#define XSD_CXX_TREE_ELEMENTS_HXX

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

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

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

namespace xsd
{
  namespace cxx
  {
    namespace tree
    {

      //
      //
      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&);
      };


      //
      //
      class type
      {
      public:
        virtual
        ~type ()
        {
        }

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

        template <typename C>
        type (xml::dom::element<C> const&)
            : container_ (0)
        {
        }

        template <typename C>
        type (xml::dom::attribute<C> const&)
            : container_ (0)
        {
        }

        type (type const&)
            : container_ (0)
        {
        }

      public:
        //@@ Can't get it be protected?
        //
        type&
        operator= (type const&)
        {
          return *this;
        }

      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;
        }

        //@@
        //protected:

      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;
        }

        //@@
        //protected:

      public:
        void
        register_id (identity const& id, type* t)
        {
          if (map_.get () == 0)
          {
            map_ = std::auto_ptr<map> (new map);
          }

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

          if (container () != this)
	    container ()->register_id (id, t);
        }

        //
        //
        struct not_registered: virtual 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;
        }


      private:
        type* container_;

        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_;
      };


      //
      //
      template <typename T>
      struct traits
      {
        static void
        set_container (T& i, type* c)
        {
          i.container (c);
        }

        template <typename C>
        static T
        create (xml::dom::element<C> const& e)
        {
          return T (e);
        }

        template <typename C>
        static T
        create (xml::dom::attribute<C> const& a)
        {
          return T (a);
        }

        template <typename C>
        static T
        create (std::basic_string<C> const& s,
                xml::dom::element<C> const& parent)
        {
          return T (s, parent);
        }
      };


      //
      //
      template <>
      struct traits<type>
      {
        template <typename C>
        static xml::dom::element<C> const&
        create (xml::dom::element<C> const& e)
        {
          return e;
        }

        template <typename C>
        static xml::dom::attribute<C> const&
        create (xml::dom::attribute<C> const& a)
        {
          return a;
        }

        template <typename C>
        static xml::dom::element<C> const&
        create (std::basic_string<C> const&, xml::dom::element<C> const& e)
        {
          return e;
        }
      };

      // optional
      //

      template <typename X>
      class optional
      {
      public:
        explicit
        optional (type* container = 0)
            : container_ (container)
        {
        }

        optional (optional const& y, type* container = 0)
            : container_ (container)
        {
          if (y)
            set (*y);
        }

        /*
        template <typename Y>
        optional_iterator (optional_iterator<Y> const& y)
        {
        }
        */

        optional&
        operator= (optional const& y)
        {
          if (x_.get () == y.x_.get ())
            return *this;

          if (y)
            set (*y);
          else
            reset ();

          return *this;
        }

        /*
        template <typename Y>
        optional_iterator&
        operator= (optional_iterator<Y> const& y)
        {
          return *this;
        }
        */

        // Pointer-like interface.
        //
      public:
        X const*
        operator-> () const
        {
          return x_.get ();
        }

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

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

        X&
        operator* ()
        {
          return get ();
        }

        typedef void (optional::*bool_convertible) ();

        operator bool_convertible () const
        {
          return present () ? &optional::reset : 0;
        }

        // Get/set interface.
        //
      public:
        bool
        present () const
        {
          return x_.get () != 0;
        }

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

        X&
        get ()
        {
          return *x_;
        }

        void
        set (X const& y)
        {
          if (x_.get () != 0)
            *x_ = y;
          else
          {
            x_ = std::auto_ptr<X> (new X (y));
            traits<X>::set_container (*x_, container_);
          }
        }

        void
        reset ()
        {
          x_.reset ();
        }

      private:
        template <typename>
        friend class optional;

      private:
        type* container_;
        std::auto_ptr<X> x_;
      };


      // sequence
      //

      //@@ Complete emulation of std::sequence.
      //
      template <typename X>
      class sequence: public type
      {
        typedef std::vector<X> base_sequence;

      public:
        typedef typename base_sequence::value_type      value_type;
        typedef typename base_sequence::pointer         pointer;
        typedef typename base_sequence::const_pointer   const_pointer;
        typedef typename base_sequence::reference       reference;
        typedef typename base_sequence::const_reference const_reference;

        typedef typename base_sequence::iterator iterator;
        typedef typename base_sequence::const_iterator const_iterator;

        typedef typename base_sequence::reverse_iterator reverse_iterator;
        typedef typename base_sequence::const_reverse_iterator const_reverse_iterator;

        typedef typename base_sequence::size_type       size_type;
        typedef typename base_sequence::difference_type difference_type;
        typedef typename base_sequence::allocator_type  allocator_type;

      public:
        sequence (type* c = 0)
        {
          type::container (c);
        }

        sequence (sequence const& v, type* c = 0)
            : type () // gcc warning
        {
          type::container (c);

          v_.reserve (v.v_.size ());

          for (typename base_sequence::const_iterator
                 i (v.v_.begin ()), e (v.v_.end ()); i != e; ++i)
          {
            push_back (*i);
          }
        }

        sequence&
        operator= (sequence const& v)
        {
          if (this == &v)
            return *this;

          v_.clear ();
          v_.reserve (v.v_.size ());

          for (typename base_sequence::const_iterator
                 i (v.v_.begin ()), e (v.v_.end ()); i != e; ++i)
          {
            push_back (*i);
          }

          return *this;
        }

      public:
        size_type
        size () const
        {
          return v_.size ();
        }

        size_type
        max_size () const
        {
          return v_.max_size ();
        }

        size_type
        capacity () const
        {
          return v_.capacity ();
        }

        bool
        empty () const
        {
          return v_.empty ();
        }

        void
        reserve (size_type n)
        {
          return v_.reserve (n);
        }

      public:
        const_iterator
        begin () const
        {
          return v_.begin ();
        }

        const_iterator
        end () const
        {
          return v_.end ();
        }

        iterator
        begin ()
        {
          return v_.begin ();
        }

        iterator
        end ()
        {
          return v_.end ();
        }

        // reverse
        //

        const_reverse_iterator
        rbegin () const
        {
          return v_.rbegin ();
        }

        const_reverse_iterator
        rend () const
        {
          return v_.rend ();
        }

        reverse_iterator
        rbegin ()
        {
          return v_.rbegin ();
        }

        reverse_iterator
        rend ()
        {
          return v_.rend ();
        }

      public:
        X&
        operator[] (size_type n)
        {
          return v_[n];
        }

        X const&
        operator[] (size_type n) const
        {
          return v_[n];
        }

        X&
        at (size_type n)
        {
          return v_.at (n);
        }

        X const&
        at (size_type n) const
        {
          return v_.at (n);
        }

        X&
        front ()
        {
          return v_.front ();
        }

        X const&
        front () const
        {
          return v_.front ();
        }

        X&
        back ()
        {
          return v_.back ();
        }

        X const&
        back () const
        {
          return v_.back ();
        }

      public:
        void
        push_back (X const& x)
        {
          if (v_.capacity () < v_.size () + 1)
          {
            base_sequence v;
            v.reserve (v_.size () + 1);

            for (iterator i (v_.begin ()), e (v_.end ()); i != e; ++i)
            {
              X& t (*i);
              traits<X>::set_container (t, 0);
              v.push_back (t);
              traits<X>::set_container (v.back (), container ());
            }

            v_.swap (v);
          }

          v_.push_back (x);
          traits<X>::set_container (v_.back (), container ());
        }

        void
        pop_back ()
        {
          traits<X>::set_container (back (), 0);
          v_.pop_back ();
        }

        iterator
        insert(iterator position, X const& x)
        {
          base_sequence v;
          v.reserve (v_.size () + 1);

          difference_type d (position - v_.begin ());

          for (iterator i (v_.begin ()); i != position; ++i)
          {
            X& t (*i);
            traits<X>::set_container (t, 0);
            v.push_back (t);
            traits<X>::set_container (v.back (), container ());
          }

          v.push_back (x);
          traits<X>::set_container (v.back (), container ());

          for (iterator i (position), e (v_.end ()); i != e; ++i)
          {
            X& t (*i);
            traits<X>::set_container (t, 0);
            v.push_back (t);
            traits<X>::set_container (v.back (), container ());
          }

          v_.swap (v);
          return v_.begin () + d;
        }

        iterator
        erase(iterator position)
        {
          if (position == end ())
            return end ();

          base_sequence v;
          v.reserve (v_.size () - 1);

          difference_type d (position - v_.begin ());

          for (iterator i (v_.begin ()); i != position; ++i)
          {
            X& t (*i);
            traits<X>::set_container (t, 0);
            v.push_back (t);
            traits<X>::set_container (v.back (), container ());
          }

          traits<X>::set_container (*position++, 0);

          for (iterator i (position), e (v_.end ()); i != e; ++i)
          {
            X& t (*i);
            traits<X>::set_container (t, 0);
            v.push_back (t);
            traits<X>::set_container (v.back (), container ());
          }

          v_.swap (v);

          return static_cast<size_type> (d) == v_.size ()
            ? v_.end ()
            : v_.begin () + d;
        }

        void
        clear ()
        {
          v_.clear ();
        }

        void
        swap (sequence& x)
        {
          v_.swap (x.v_);
        }

      public:
        template <typename C>
        explicit
        sequence (xml::dom::element<C> const& e)
        {
          init (e.value (), e);
        }

        template <typename C>
        explicit
        sequence (xml::dom::attribute<C> const& a)
        {
          init (a.value (), a.element ());
        }

        template <typename C>
        sequence (std::basic_string<C> const& s,
                xml::dom::element<C> const& parent)
        {
          init (s, parent);
        }

      private:
        template <typename C>
        void
        init (std::basic_string<C> const& s,
              xml::dom::element<C> const& parent)
        {
          // Note that according to the spec the separator is exactly
          // one space (0x20). This makes our life much easier.
          //

          for (std::size_t i (0), j (s.find (C (' ')));;)
          {
            if (j != std::basic_string<C>::npos)
            {
              push_back (
                traits<X>::create (
                  std::basic_string<C> (s, i, j - i), parent));

              i = j + 1;
              j = s.find (C (' '), i);
            }
            else
            {
              // Last element.
              push_back (
                traits<X>::create (
                  std::basic_string<C> (s, i), parent));

              break;
            }
          }
        }

      public:
        using type::container;

        // Note that sequence doesn't act like a real container.
        //
        virtual void
        container (type* c)
        {
          type::container (c);

          for (typename base_sequence::iterator i (v_.begin ()), e (v_.end ());
               i != e; ++i)
          {
            traits<X>::set_container (*i, container ());
          }
        }

      private:
        base_sequence v_;
      };

      template <typename C, typename X>
      std::basic_ostream<C>&
      operator<< (std::basic_ostream<C>& os, sequence<X> const& v)
      {
        for (typename sequence<X>::const_iterator
               b (v.begin ()), e (v.end ()), i (b); i != e; ++i)
        {
          if (i != b)
            os << C (' ');

          os << *i;
        }

        return os;
      }


      // 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 type
      {
        template <typename>
        friend class proxy_ptr;

        typedef X base_type;

      public:
        fundamental_base ()
            : x_ ()
        {
        }

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

        template <typename C>
        explicit
        fundamental_base (xml::dom::element<C> const& e)
        {
          std::basic_istringstream<C> is (e.value ());
          is >> x_;
        }

        template <typename C>
        explicit
        fundamental_base (xml::dom::attribute<C> const& a)
        {
          std::basic_istringstream<C> is (a.value ());
          is >> x_;
        }

        template <typename C>
        fundamental_base (std::basic_string<C> const& s,
                          xml::dom::element<C> const&)
        {
          std::basic_istringstream<C> is (s);
          is >> x_;
        }

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

          return *this;
        }

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

        operator X& ()
        {
          return x_;
        }

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

        template <typename Y>
        operator Y ()
        {
          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;
        }

      private:
        X x_;
      };

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

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


      // Specialization for 'signed char'
      //

      template<>
      template<typename C>
      inline
      fundamental_base<signed char>::
      fundamental_base (xml::dom::element<C> const& e)
      {
        std::basic_istringstream<C> is (e.value ());

        short t;
        is >> t;

        x_ = static_cast<signed char> (t);
      }

      template<>
      template<typename C>
      inline
      fundamental_base<signed char>::
      fundamental_base (xml::dom::attribute<C> const& a)
      {
        std::basic_istringstream<C> is (a.value ());

        short t;
        is >> t;

        x_ = static_cast<signed char> (t);
      }

      template<>
      template<typename C>
      inline
      fundamental_base<signed char>::
      fundamental_base (std::basic_string<C> const& s,
                        xml::dom::element<C> const&)
      {
        std::basic_istringstream<C> is (s);

        short t;
        is >> t;

        x_ = static_cast<signed char> (t);
      }

      // Specialization for 'unsigned char'
      //

      template<>
      template<typename C>
      inline
      fundamental_base<unsigned char>::
      fundamental_base (xml::dom::element<C> const& e)
      {
        std::basic_istringstream<C> is (e.value ());

        unsigned short t;
        is >> t;

        x_ = static_cast<unsigned char> (t);
      }

      template<>
      template<typename C>
      inline
      fundamental_base<unsigned char>::
      fundamental_base (xml::dom::attribute<C> const& a)
      {
        std::basic_istringstream<C> is (a.value ());

        unsigned short t;
        is >> t;

        x_ = static_cast<unsigned char> (t);
      }

      template<>
      template<typename C>
      inline
      fundamental_base<unsigned char>::
      fundamental_base (std::basic_string<C> const& s,
                        xml::dom::element<C> const&)
      {
        std::basic_istringstream<C> is (s);

        unsigned short t;
        is >> t;

        x_ = static_cast<unsigned char> (t);
      }
    }
  }
}

#endif  // XSD_CXX_TREE_ELEMENTS_HXX

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