// 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 <istream>
#include <ostream>
#include <sstream>

#include <xsd/cxx/xml.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::element<C> const&)
            : container_ (0)
        {
        }

        template <typename C>
        type (xml::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::element<c> const& e)
        {
          return T (e);
        }

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


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

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


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

      public:
        typedef typename sequence::iterator iterator;
        typedef typename sequence::const_iterator const_iterator;

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

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

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

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

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

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

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

          return *this;
        }

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

            for (typename sequence::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 (e);
          traits<X>::set_container (v_.back (), container ());
        }

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

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

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

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

      public:
        using type::container;

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

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

      private:
        sequence v_;
      };


      // 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>
        fundamental_base (xml::element<C> const& e)
        {
          std::basic_stringstream<C> s;
          s << e.value ();
          s >> x_;
        }

        template<typename C>
        fundamental_base (xml::attribute<C> const& a)
        {
          std::basic_stringstream<C> s;
          s << a.value ();
          s >> 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>
      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>
      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::element<C> const& e)
      {
        std::basic_stringstream<C> s;
        s << e.value ();

        short t;
        s >> t;

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

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

        short t;
        s >> t;

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


      // Specialization for 'unsigned char'
      //

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

        unsigned short t;
        s >> t;

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

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

        unsigned short t;
        s >> t;

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

      // Specialization for 'bool'.
      //
      //
      template<>
      template<>
      inline
      fundamental_base<bool>::
      fundamental_base (xml::element<char> const& e)
      {
        x_ = (e.value () == "true") || (e.value () == "1");
      }

      template<>
      template<>
      inline
      fundamental_base<bool>::
      fundamental_base (xml::element<wchar_t> const& e)
      {
        x_ = (e.value () == L"true") || (e.value () == L"1");
      }

      template<>
      template<>
      inline
      fundamental_base<bool>::
      fundamental_base (xml::attribute<char> const& a)
      {
        x_ = (a.value () == "true") || (a.value () == "1");
      }

      template<>
      template<>
      inline
      fundamental_base<bool>::
      fundamental_base (xml::attribute<wchar_t> const& a)
      {
        x_ = (a.value () == L"true") || (a.value () == L"1");
      }
    }
  }
}


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

#endif  // XSD_CXX_TREE_ELEMENTS_HXX
