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

#ifndef XSD_FRONTEND_SEMANTIC_GRAPH_ELEMENTS_HXX
#define XSD_FRONTEND_SEMANTIC_GRAPH_ELEMENTS_HXX

#include <iosfwd>

#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/exception.hpp>

#include <cult/types.hxx>

#include <cult/rtti/type-info.hxx>

#include <cult/containers/set.hxx>
#include <cult/containers/map.hxx>
#include <cult/containers/list.hxx>
#include <cult/containers/pair.hxx>
#include <cult/containers/graph.hxx>
#include <cult/containers/vector.hxx>

#include <frontend-elements/context.hxx>

namespace XSDFrontend
{
  namespace SemanticGraph
  {
    using namespace Cult::Types;

    namespace Bits
    {
      using Cult::Containers::Graph;

      //@@ Should end up in Cult::Meta
      //
      template <typename X>
      struct strip_pointer
      {
        typedef X Type;
      };

      template <typename X>
      struct strip_pointer<X*>
      {
        typedef X Type;
      };

      template <typename I>
      struct PointerIterator
      {
        typedef
        typename strip_pointer<typename I::Value>::Type
        Value;

        typedef I BaseIterator;
        typedef Value& Reference;
        typedef Value* Pointer;

        PointerIterator ()
            : i_ () // i_ can be of a pointer type.
        {
        }

        PointerIterator (I const& i)
            : i_ (i)
        {
        }

      public:
        Reference
        operator* () const
        {
          return **i_;
        }

        Pointer
        operator-> () const
        {
          return *i_;
        }

        I const&
        base () const
        {
          return i_;
        }

      public:
        PointerIterator&
        operator++ ()
        {
          ++i_;
          return *this;
        }

        PointerIterator
        operator++ (Int)
        {
          PointerIterator r (*this);
          ++i_;
          return r;
        }

        PointerIterator&
        operator-- ()
        {
          --i_;
          return *this;
        }

        PointerIterator
        operator-- (Int)
        {
          PointerIterator r (*this);
          --i_;
          return r;
        }

      private:
        I i_;
      };

      template <typename I>
      inline
      Boolean
      operator== (PointerIterator<I> const& a, PointerIterator<I> const& b)
      {
        return a.base () == b.base ();
      }

      template <typename I>
      inline
      Boolean
      operator!= (PointerIterator<I> const& a, PointerIterator<I> const& b)
      {
        return a.base () != b.base ();
      }

      template <typename I>
      inline
      typename PointerIterator<I>::BaseIterator::difference_type
      operator- (PointerIterator<I> const& a, PointerIterator<I> const& b)
      {
        return a.base () - b.base ();
      }
    }

    //
    //
    typedef
    boost::filesystem::filesystem_error
    InvalidPath;

    typedef
    boost::filesystem::path
    Path;

    typedef
    Cult::Containers::Vector<Path>
    Paths;

    typedef
    FrontendElements::Context
    Context;

    //
    //
    class Node;
    class Edge;

    //
    //
    class Annotates;
    class Annotation;

    //
    //
    class Edge
    {
    public:
      Context&
      context () const
      {
        return context_;
      }

      virtual
      ~Edge ()
      {
      }

    public:
      Boolean
      annotated () const
      {
        return annotates_ != 0;
      }

      Annotation&
      annotation ();

    public:
      template <typename X>
      Boolean
      is_a () const
      {
        return dynamic_cast<X const*> (this) != 0;
      }

    protected:
      friend class Bits::Graph<Node, Edge>;

      Edge ()
          : annotates_ (0)
      {
      }

      Void
      add_edge_right (Annotates& a)
      {
        assert (annotates_ == 0);
        annotates_ = &a;
      }

    private:
      mutable Context context_;
      Annotates* annotates_;
    };

    inline Boolean
    operator== (Edge const& x, Edge const& y)
    {
      return &x == &y;
    }


    //
    //
    class Node
    {
    public:
      Context&
      context () const
      {
        return context_;
      }

    public:
      Path const&
      file () const
      {
        return file_;
      }

      UnsignedLong
      line () const
      {
        return line_;
      }

      UnsignedLong
      column () const
      {
        return column_;
      }

    public:
      Boolean
      annotated () const
      {
        return annotates_ != 0;
      }

      Annotation&
      annotation ();

    public:
      template <typename X>
      Boolean
      is_a () const
      {
        return dynamic_cast<X const*> (this) != 0;
      }

    public:

      virtual
      ~Node ()
      {
      }

    protected:
      friend class Bits::Graph<Node, Edge>;

      Node (Path const& file, UnsignedLong line, UnsignedLong column)
          : annotates_ (0), file_ (file), line_ (line), column_ (column)
      {
      }

      Node () // For virtual inheritance.
      {
        abort (); // Told you so!
      }

      // This is a bunch of experimental sink functions that allow
      // extensions in the form of one-way edges.
      //
      //void
      //add_edge_left (Edge&)
      //{
      //}

      Void
      add_edge_right (Edge&)
      {
      }

      Void
      add_edge_right (Annotates& a)
      {
        assert (annotates_ == 0);
        annotates_ = &a;
      }

    private:
      mutable Context context_;
      Annotates* annotates_;
      Path file_;
      UnsignedLong line_;
      UnsignedLong column_;
    };

    inline Boolean
    operator== (Node const& x, Node const& y)
    {
      return &x == &y;
    }


    //
    //
    typedef WideString Name;


    //
    //
    class Scope;
    class Nameable;


    //
    //
    class Names: public virtual Edge
    {
    public:
      Name
      name () const
      {
        return name_;
      }

      Scope&
      scope () const
      {
        return *scope_;
      }

      Nameable&
      named () const
      {
        return *named_;
      }

    protected:
      friend class Bits::Graph<Node, Edge>;

      Names (Name const& name)
          : name_ (name)
      {
      }

      Void
      set_left_node (Scope& n)
      {
        scope_ = &n;
      }

      Void
      set_right_node (Nameable& n)
      {
        named_ = &n;
      }

      Void
      clear_left_node (Scope& n)
      {
        assert (scope_ == &n);
        scope_ = 0;
      }

      Void
      clear_right_node (Nameable& n)
      {
        assert (named_ == &n);
        named_ = 0;
      }

    private:
      Scope* scope_;
      Nameable* named_;
      Name name_;
    };


    class Nameable: public virtual Node
    {
    public:
      Boolean
      named () const
      {
        return named__ != 0;
      }

      Name
      name () const
      {
        assert (named ());
        return named__->name ();
      }

      Scope&
      scope ()
      {
        assert (named ());
        return named__->scope ();
      }

      Names&
      named_ ()
      {
        assert (named ());
        return *named__;
      }

    protected:
      friend class Bits::Graph<Node, Edge>;

      Nameable ()
          : named__ (0)
      {
      }

      Void
      add_edge_right (Names& e)
      {
        assert (named__ == 0);
        named__ = &e;
      }

      Void
      remove_edge_right (Names& e)
      {
        assert (named__ == &e);
        named__ = 0;
      }

      using Node::add_edge_right;

    private:
      Names* named__;
    };


    //
    //
    typedef
    Cult::Containers::Set<Nameable*>
    Nameables;


    //
    //
    class Scope: public virtual Nameable
    {
    protected:
      typedef
      Cult::Containers::List<Names*>
      NamesList;

      typedef
      Cult::Containers::Map<Names*, NamesList::Iterator>
      ListIteratorMap;

      typedef
      Cult::Containers::Map<Name, NamesList>
      NamesMap;

    public:
      typedef
      Bits::PointerIterator<NamesList::Iterator>
      NamesIterator;

      typedef
      Bits::PointerIterator<NamesList::ConstIterator>
      NamesConstIterator;

      typedef
      Cult::Containers::Pair <NamesConstIterator, NamesConstIterator>
      NamesIteratorPair;

      NamesIterator
      names_begin ()
      {
        return names_.begin ();
      }

      NamesIterator
      names_end ()
      {
        return names_.end ();
      }

      NamesConstIterator
      names_begin () const
      {
        return names_.begin ();
      }

      NamesConstIterator
      names_end () const
      {
        return names_.end ();
      }

      virtual NamesIteratorPair
      find (Name const& name) const
      {
        NamesMap::ConstIterator i (names_map_.find (name));

        if (i == names_map_.end ())
          return NamesIteratorPair (names_.end (), names_.end ());
        else
          return NamesIteratorPair (i->second.begin (), i->second.end ());
      }

      NamesIterator
      find (Names& e)
      {
        ListIteratorMap::Iterator i (iterator_map_.find (&e));
        return i != iterator_map_.end () ? i->second : names_.end ();
      }

    protected:
      friend class Bits::Graph<Node, Edge>;

      Scope (Path const& file, UnsignedLong line, UnsignedLong column)
          : Node (file, line, column)
      {
      }

      Scope ()
      {
      }

      Void
      add_edge_left (Names& e)
      {
        NamesList::Iterator i (names_.insert (names_.end (), &e));
        iterator_map_[&e] = i;
        names_map_[e.name ()].push_back (&e);
      }

      Void
      remove_edge_left (Names& e)
      {
        ListIteratorMap::Iterator i (iterator_map_.find (&e));
        assert (i != iterator_map_.end ());

        names_.erase (i->second);
        iterator_map_.erase (i);

        NamesMap::Iterator j (names_map_.find (e.name ()));

        for (NamesList::Iterator i (j->second.begin ());
             i != j->second.end (); ++i)
        {
          if (*i == &e)
            i = j->second.erase (i);
        }
      }

      //@@ Ideally should be protected but then NodeArg has no way to
      //   access it. Maybe when (if) I move NodeArg into Grpah I can
      //   resolve this.
      //
    public:
      Void
      add_edge_left (Names& e, NamesIterator const& after)
      {
        NamesList::Iterator i;

        if (after.base () == names_.end ())
          i = names_.insert (names_.begin (), &e);
        else
        {
          NamesList::Iterator j (after.base ());
          i = names_.insert (++j, &e);
        }

        iterator_map_[&e] = i;
        names_map_[e.name ()].push_back (&e);
      }

    protected:
      using Nameable::add_edge_right;

    private:
      NamesList names_;
      ListIteratorMap iterator_map_;
      NamesMap names_map_;
    };


    //
    //
    class Belongs;
    class Inherits;
    class Arguments;

    class Type: public virtual Nameable
    {
    protected:
      typedef
      Cult::Containers::Vector<Belongs*>
      Classifies;

      typedef
      Cult::Containers::Vector<Inherits*>
      Begets;

    public:
      typedef
      Bits::PointerIterator<Classifies::ConstIterator>
      ClassifiesIterator;

      ClassifiesIterator
      classifies_begin () const
      {
        return classifies_.begin ();
      }

      ClassifiesIterator
      classifies_end () const
      {
        return classifies_.end ();
      }

      //
      //
      Boolean
      inherits_p () const
      {
        return inherits_ != 0;
      }

      Inherits&
      inherits () const
      {
        assert (inherits_ != 0);
        return *inherits_;
      }

      //
      //
      typedef
      Bits::PointerIterator<Begets::ConstIterator>
      BegetsIterator;

      BegetsIterator
      begets_begin () const
      {
        return begets_.begin ();
      }

      BegetsIterator
      begets_end () const
      {
        return begets_.end ();
      }

    protected:
      friend class Bits::Graph<Node, Edge>;

      Type ()
          : inherits_ (0)
      {
      }

      Void
      add_edge_right (Belongs& e)
      {
        classifies_.push_back (&e);
      }

      Void
      add_edge_right (Inherits& e)
      {
        begets_.push_back (&e);
      }

      using Nameable::add_edge_right;

      Void
      add_edge_left (Arguments&)
      {
      }

      Void
      add_edge_left (Inherits& e)
      {
        assert (inherits_ == 0);
        inherits_ = &e;
      }

    private:
      Inherits* inherits_;
      Begets begets_;
      Classifies classifies_;
    };


    class Instance: public virtual Nameable
    {
    public:
      Belongs&
      belongs () const
      {
        return *belongs_;
      }

      Type&
      type () const;

      Boolean
      typed () const
      {
        return belongs_ != 0;
      }

    protected:
      friend class Bits::Graph<Node, Edge>;

      Instance ()
          : belongs_ (0)
      {
      }

      Void
      add_edge_left (Belongs& e)
      {
        belongs_ = &e;
      }

    private:
      Belongs* belongs_;
    };


    class Belongs: public virtual Edge
    {
    public:
      Instance&
      instance () const
      {
        return *instance_;
      }

      Type&
      type () const
      {
        return *type_;
      }

    protected:
      friend class Bits::Graph<Node, Edge>;

      Belongs ()
      {
      }

      Void
      set_left_node (Instance& n)
      {
        instance_ = &n;
      }

      Void
      set_right_node (Type& n)
      {
        type_ = &n;
      }

    private:
      Instance* instance_;
      Type* type_;
    };


    //
    //
    class Inherits: public virtual Edge
    {
    public:
      virtual Type&
      base () const
      {
        return *base_;
      }

      virtual Type&
      derived () const
      {
        return *derived_;
      }

    protected:
      Inherits ()
      {
      }

      Void
      set_left_node (Type& n)
      {
        derived_ = &n;
      }

      Void
      set_right_node (Type& n)
      {
        base_ = &n;
      }

    private:
      Type* base_;
      Type* derived_;
    };


    class Extends: public virtual Inherits
    {
    protected:
      friend class Bits::Graph<Node, Edge>;

      Extends ()
      {
      }
    };

    class Restricts: public virtual Inherits
    {
    protected:
      typedef
      Cult::Containers::Map<WideString, WideString>
      Facets;

    public:
      typedef
      Facets::Iterator
      FacetIterator;

      Boolean
      facet_empty ()
      {
        return facets_.empty ();
      }

      FacetIterator
      facet_begin ()
      {
        return facets_.begin ();
      }

      FacetIterator
      facet_end ()
      {
        return facets_.end ();
      }

      FacetIterator
      facet_find (WideString const& name)
      {
        return facets_.find (name);
      }

      Void
      facet_insert (String const& name, String const& value)
      {
        facets_[name] = value;
      }

    protected:
      friend class Bits::Graph<Node, Edge>;

      Restricts ()
      {
      }

    protected:
      Facets facets_;
    };


    //
    //
    class Member;
    class Namespace;

    class BelongsToNamespace: public virtual Edge
    {
    public:
      Member&
      member () const
      {
        assert (member_ != 0);
        return *member_;
      }

      Namespace&
      namespace_ () const
      {
        assert (namespace__ != 0);
        return *namespace__;
      }

    protected:
      friend class Bits::Graph<Node, Edge>;

      BelongsToNamespace ()
          : member_ (0), namespace__ (0)
      {
      }

      Void
      set_left_node (Member& n)
      {
        member_ = &n;
      }

      Void
      set_right_node (Namespace& n)
      {
        namespace__ = &n;
      }

    private:
      Member* member_;
      Namespace* namespace__;
    };


    //
    //
    class Member: public virtual Instance
    {
    public:
      // Member is global either if it is defined outside any type
      // or it is a ref="" of a global member.
      //
      Boolean
      global () const
      {
        return global_;
      }

      Boolean
      qualified () const
      {
        return qualified_;
      }

      // Note that only qualified members belong to a namespace.
      //
      Namespace&
      namespace_ () const
      {
        assert (belongs_to_namespace_ != 0);
        return belongs_to_namespace_->namespace_ ();
      }


      // Default and fixed value API. Note that the fixed value semantics
      // is a superset of the default value semantics. As such setting the
      // fixed value appears as if the default value was also set.
      //
      Boolean
      default_ () const
      {
        return value_type_ != ValueType::none;
      }

      Boolean
      fixed () const
      {
        return value_type_ == ValueType::fixed;
      }

      struct NoValue {};

      WideString
      value () const
      {
        if (value_type_ != ValueType::none)
          return value_;
        else
          throw NoValue ();
      }

      //
      //
      Void
      default_ (WideString const& v)
      {
        value_ = v;
        value_type_ = ValueType::default_;
      }

      Void
      fixed (WideString const& v)
      {
        value_ = v;
        value_type_ = ValueType::fixed;
      }

    protected:
      friend class Bits::Graph<Node, Edge>;

      Member (Boolean global, Boolean qualified)
          : global_ (global),
            qualified_ (qualified),
            belongs_to_namespace_ (0),
            value_type_ (ValueType::none)
      {
      }

      Void
      add_edge_left (BelongsToNamespace& e)
      {
        // In the parser we sometimes re-add the same adge.
        //
        belongs_to_namespace_ = &e;
      }

      using Instance::add_edge_left;

    private:
      Boolean global_;
      Boolean qualified_;
      BelongsToNamespace* belongs_to_namespace_;

      struct ValueType
      {
        enum Value
        {
          none,
          default_,
          fixed
        };
      };

      WideString value_;
      ValueType::Value value_type_;
    };


    // Parametric types.
    //

    class Specialization: public virtual Type
    {
    public:
      Arguments&
      argumented () const
      {
        return *argumented_;
      }

    protected:
      friend class Bits::Graph<Node, Edge>;

      void
      add_edge_right (Arguments& a)
      {
        argumented_ = &a;
      }

      using Type::add_edge_right;

    private:
      Arguments* argumented_;
    };


    class Arguments: public virtual Edge
    {
    public:
      Type&
      type () const
      {
        return *type_;
      }

      Specialization&
      specialization () const
      {
        return *specialization_;
      }

    protected:
      friend class Bits::Graph<Node, Edge>;

      void
      set_left_node (Type& n)
      {
        type_ = &n;
      }

      void
      set_right_node (Specialization& s)
      {
        specialization_ = &s;
      }

    private:
      Type* type_;
      Specialization* specialization_;
    };


    //
    //
    class AnyType: public virtual Type
    {
    protected:
      friend class Bits::Graph<Node, Edge>;

      AnyType (Path const& file, UnsignedLong line, UnsignedLong column)
          : Node (file, line, column)
      {
      }

      AnyType () // For virtual inheritance.
      {
      }
    };


    //
    //
    class AnySimpleType: public virtual Type
    {
    protected:
      friend class Bits::Graph<Node, Edge>;

      AnySimpleType (Path const& file, UnsignedLong line, UnsignedLong column)
          : Node (file, line, column)
      {
      }

      AnySimpleType () // For virtual inheritance.
      {
      }
    };
  }
}

// ADL won't find it because Path is a typedef. Note that this
// function prints in native format.
//
std::wostream&
operator<< (std::wostream& os, XSDFrontend::SemanticGraph::Path const& path);

#endif  // XSD_FRONTEND_SEMANTIC_GRAPH_ELEMENTS_HXX
