// -*- c++ -*-
#ifndef LEMON_ITERABLE_GRAPH_EXTENDER_H
#define LEMON_ITERABLE_GRAPH_EXTENDER_H

#include <lemon/invalid.h>

namespace lemon {
  
  template <typename _Base>
  class IterableGraphExtender : public _Base {
  public:

    typedef _Base Parent;
    typedef IterableGraphExtender<_Base> Graph;

    typedef typename Parent::Node Node;
    typedef typename Parent::Edge Edge;


    class NodeIt : public Node { 
      const Graph* graph;
    public:

      NodeIt() {}

      NodeIt(Invalid i) : Node(i) { }

      explicit NodeIt(const Graph& _graph) : graph(&_graph) {
	_graph.first(*static_cast<Node*>(this));
      }

      NodeIt(const Graph& _graph, const Node& node) 
	: Node(node), graph(&_graph) {}

      NodeIt& operator++() { 
	graph->next(*this);
	return *this; 
      }

    };


    class EdgeIt : public Edge { 
      const Graph* graph;
    public:

      EdgeIt() { }

      EdgeIt(Invalid i) : Edge(i) { }

      explicit EdgeIt(const Graph& _graph) : graph(&_graph) {
	_graph.first(*static_cast<Edge*>(this));
      }

      EdgeIt(const Graph& _graph, const Edge& e) : 
	Edge(e), graph(&_graph) { }

      EdgeIt& operator++() { 
	graph->next(*this);
	return *this; 
      }

    };


    class OutEdgeIt : public Edge { 
      const Graph* graph;
    public:

      OutEdgeIt() { }

      OutEdgeIt(Invalid i) : Edge(i) { }

      OutEdgeIt(const Graph& _graph, const Node& node) 
	: graph(&_graph) {
	_graph.firstOut(*this, node);
      }

      OutEdgeIt(const Graph& _graph, const Edge& edge) 
	: Edge(edge), graph(&_graph) {}

      OutEdgeIt& operator++() { 
	graph->nextOut(*this);
	return *this; 
      }

    };


    class InEdgeIt : public Edge { 
      const Graph* graph;
    public:

      InEdgeIt() { }

      InEdgeIt(Invalid i) : Edge(i) { }

      InEdgeIt(const Graph& _graph, const Node& node) 
	: graph(&_graph) {
	_graph.firstIn(*this, node);
      }

      InEdgeIt(const Graph& _graph, const Edge& edge) : 
	Edge(edge), graph(&_graph) {}

      InEdgeIt& operator++() { 
	graph->nextIn(*this);
	return *this; 
      }

    };

    /// Base node of the iterator
    ///
    /// Returns the base node (ie. the source in this case) of the iterator
    ///
    /// \todo Document in the concept!
    Node baseNode(const OutEdgeIt &e) const {
      return source(e);
    }
    /// Running node of the iterator
    ///
    /// Returns the running node (ie. the target in this case) of the
    /// iterator
    ///
    /// \todo Document in the concept!
    Node runningNode(const OutEdgeIt &e) const {
      return target(e);
    }

    /// Base node of the iterator
    ///
    /// Returns the base node (ie. the target in this case) of the iterator
    ///
    /// \todo Document in the concept!
    Node baseNode(const InEdgeIt &e) const {
      return target(e);
    }
    /// Running node of the iterator
    ///
    /// Returns the running node (ie. the source in this case) of the
    /// iterator
    ///
    /// \todo Document in the concept!
    Node runningNode(const InEdgeIt &e) const {
      return source(e);
    }

    using Parent::first;

  private:

    /// \todo When (and if) we change the iterators concept to use operator*,
    /// then the following shadowed methods will become superfluous.
    /// But for now these are important safety measures.

    void first(NodeIt &) const;
    void first(EdgeIt &) const;
    void first(OutEdgeIt &) const;
    void first(InEdgeIt &) const;

  };





  
  template <typename _Base>
  class IterableUndirGraphExtender : public IterableGraphExtender<_Base> {
  public:

    typedef IterableGraphExtender<_Base> Parent;
    typedef IterableUndirGraphExtender<_Base> Graph;
    typedef typename Parent::Node Node;

    typedef typename Parent::UndirEdge UndirEdge;

    class UndirEdgeIt : public UndirEdge { 
      const Graph* graph;
    public:

      UndirEdgeIt() { }

      UndirEdgeIt(Invalid i) : UndirEdge(i) { }

      explicit UndirEdgeIt(const Graph& _graph) : graph(&_graph) {
	_graph.first(*static_cast<UndirEdge*>(this));
      }

      UndirEdgeIt(const Graph& _graph, const UndirEdge& e) : 
	UndirEdge(e), graph(&_graph) { }

      UndirEdgeIt& operator++() { 
	graph->next(*this);
	return *this; 
      }

    };

    class IncEdgeIt : public UndirEdge { 
      const Graph* graph;
      bool forward;
      friend class IterableUndirGraphExtender;
      template <typename G>
      friend class UndirGraphExtender;
    public:

      IncEdgeIt() { }

      IncEdgeIt(Invalid i) : UndirEdge(i), forward(false) { }

      IncEdgeIt(const Graph& _graph, const Node &n)
	: graph(&_graph)
      {
	_graph._dirFirstOut(*this, n);
      }

      IncEdgeIt(const Graph& _graph, const UndirEdge &ue, const Node &n)
	: graph(&_graph), UndirEdge(ue)
      {
	forward = (_graph.source(ue) == n);
      }

      IncEdgeIt& operator++() {
	graph->_dirNextOut(*this);
	return *this; 
      }
    };

    using Parent::baseNode;
    using Parent::runningNode;

    /// Base node of the iterator
    ///
    /// Returns the base node of the iterator
    Node baseNode(const IncEdgeIt &e) const {
      return _dirSource(e);
    }
    /// Running node of the iterator
    ///
    /// Returns the running node of the iterator
    Node runningNode(const IncEdgeIt &e) const {
      return _dirTarget(e);
    }

  };
}

#endif // LEMON_GRAPH_EXTENDER_H
