/* -*- C++ -*-
 * src/lemon/merge_node_graph_wrapper.h - Part of LEMON, a generic C++ optimization library
 *
 * Copyright (C) 2004 Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
 * (Egervary Combinatorial Optimization Research Group, EGRES).
 *
 * Permission to use, modify and distribute this software is granted
 * provided that this copyright notice appears in all copies. For
 * precise terms see the accompanying LICENSE file.
 *
 * This software is provided "AS IS" with no warranty of any kind,
 * express or implied, and with no claim as to its suitability for any
 * purpose.
 *
 */

#ifndef LEMON_MERGE_NODE_GRAPH_WRAPPER_H
#define LEMON_MERGE_NODE_GRAPH_WRAPPER_H

#include <lemon/invalid.h>
#include <lemon/maps.h>
#include <lemon/map_defines.h>
#include <lemon/graph_wrapper.h>
#include <iostream>

namespace lemon {

  template <typename Graph1, typename Graph2, typename Enable=void> 
  class MergeNodeGraphWrapper : 
    public GraphWrapper<Graph1>, public GraphWrapper<Graph2> {
    typedef GraphWrapper<Graph1> Parent1;
    typedef GraphWrapper<Graph2> Parent2;
    typedef typename GraphWrapper<Graph1>::Node Graph1Node;
    typedef typename GraphWrapper<Graph2>::Node Graph2Node;
  public:
    class Node;
    class NodeIt;
    friend class Node;
    friend class NodeIt;
    template<typename Value> class NodeMap;

    MergeNodeGraphWrapper(Graph1& _graph1, Graph2& _graph2) : 
      Parent1(_graph1), Parent2(_graph2) { }

    class Node : public Graph1Node, public Graph2Node {
      friend class MergeNodeGraphWrapper<Graph1, Graph2>;
      template<typename Value> friend class NodeMap;
    protected:
      bool backward; //true, iff backward
    public:
      Node() { }
      /// \todo =false is needed, or causes problems?
      /// If \c _backward is false, then we get an edge corresponding to the 
      /// original one, otherwise its oppositely directed pair is obtained.
      Node(const Graph1Node& n1, 
	   const Graph2Node& n2, bool _backward) : 
	Graph1Node(n1), Graph2Node(n2), backward(_backward) { }
      Node(Invalid i) : Graph1Node(i), Graph2Node(i), backward(true) { }
      bool operator==(const Node& v) const { 
	return (this->backward==v.backward && 
		static_cast<Graph1Node>(*this)==
		static_cast<Graph1Node>(v) && 
		static_cast<Graph2Node>(*this)==
		static_cast<Graph2Node>(v));
      } 
      bool operator!=(const Node& v) const { 
	return (this->backward!=v.backward || 
		static_cast<Graph1Node>(*this)!=
		static_cast<Graph1Node>(v) || 
		static_cast<Graph2Node>(*this)!=
		static_cast<Graph2Node>(v));
      }
    };

    class NodeIt : public Node {
      friend class MergeNodeGraphWrapper<Graph1, Graph2>;
    protected:
      const MergeNodeGraphWrapper<Graph1, Graph2>* gw;
    public:
      NodeIt() { }
      NodeIt(Invalid i) : Node(i) { }
      NodeIt(const MergeNodeGraphWrapper<Graph1, Graph2>& _gw) : 
	Node(typename Graph1::NodeIt(*_gw.Parent1::graph), 
	     typename Graph2::Node(),
	     false), gw(&_gw) { 
	if (*static_cast<Graph1Node*>(this)==INVALID) {
// 	  *static_cast<Node*>(this)=
// 	    Node(Graph1Node(INVALID), 
// 		 typename Graph2::NodeIt(*_gw.Parent2::graph), 
// 		 true);
	  *static_cast<Graph2Node*>(this)=
	    typename Graph2::NodeIt(*_gw.Parent2::graph);
	  backward=true;
	}
      }
      NodeIt(const MergeNodeGraphWrapper<Graph1, Graph2>& _gw, 
	     const Node& n) : 
	Node(n), gw(&_gw) { }
      NodeIt& operator++() { 
	if (!this->backward) {
	  *(static_cast<Graph1Node*>(this))=
	    ++(typename Graph1::NodeIt(*gw->Parent1::graph, *this));
	  if (*static_cast<Graph1Node*>(this)==INVALID) {
// 	    *static_cast<Node*>(this)=
// 	      Node(typename Graph1::Node(INVALID), 
// 		   typename Graph2::NodeIt(*gw->Parent2::graph), true);
	    *static_cast<Graph2Node*>(this)=
	      typename Graph2::NodeIt(*gw->Parent2::graph);
	    backward=true;
	  }
	} else {
	  *(static_cast<Graph2Node*>(this))=
	    ++(typename Graph2::NodeIt(*gw->Parent2::graph, *this));
	}
	return *this;
      }
    };

    int id(const Node& n) const { 
      if (!n.backward) 
	return this->Parent1::graph->id(n);
      else
	return this->Parent2::graph->id(n);
    }

    template <typename Value> 
    class NodeMap : public Parent1::template NodeMap<Value>, 
		    public Parent2::template NodeMap<Value> { 
      typedef typename Parent1::template NodeMap<Value> ParentMap1;
      typedef typename Parent2::template NodeMap<Value> ParentMap2;
    public:
      NodeMap(const MergeNodeGraphWrapper<Graph1, Graph2>& gw) : 
	ParentMap1(gw), 
	ParentMap2(gw) { }
      NodeMap(const MergeNodeGraphWrapper<Graph1, Graph2>& gw, 
	      const Value& value)
	: ParentMap1(gw, value), 
	  ParentMap2(gw, value) { }
      NodeMap(const NodeMap& copy)
	: ParentMap1(copy), 
	  ParentMap2(copy) { }
      template <typename TT>
      NodeMap(const NodeMap<TT>& copy)
	: ParentMap1(copy),
	  ParentMap2(copy) { }
      NodeMap& operator=(const NodeMap& copy) {
	ParentMap1::operator=(copy);
	ParentMap2::operator=(copy);
	return *this;
      }
      template <typename TT>
      NodeMap& operator=(const NodeMap<TT>& copy) {
	ParentMap1::operator=(copy);
	ParentMap2::operator=(copy);
	return *this;
      }
      Value operator[](const Node& n) const {
	if (!n.backward) 
	  return ParentMap1::operator[](n);
	else 
	  return ParentMap2::operator[](n);
      }
      void set(const Node& n, const Value& value) {
	if (!n.backward) 
	  ParentMap1::set(n, value);
	else 
	  ParentMap2::set(n, value);
      }
      using ParentMap1::operator[];
      using ParentMap2::operator[];
    };
    
  };

} //namespace lemon

#endif //LEMON_MERGE_NODE_GRAPH_WRAPPER_H
