/* -*- 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 MergeNodeGraphWrapperBase : 
    public GraphWrapperBase<_Graph1>, public GraphWrapperBase<_Graph2> {
  public:
    typedef _Graph1 Graph1;
    typedef _Graph2 Graph2;
    typedef GraphWrapperBase<_Graph1> Parent1;
    typedef GraphWrapperBase<_Graph2> Parent2;
    typedef typename Parent1::Node Graph1Node;
    typedef typename Parent2::Node Graph2Node;
  protected:
    MergeNodeGraphWrapperBase() { }
  public:
    template <typename _Value> class NodeMap;

    class Node : public Graph1Node, public Graph2Node {
      friend class MergeNodeGraphWrapperBase<_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));
      }
    };

    //typedef void Edge;
    class Edge { };
    
    void first(Node& i) const {
      Parent1::graph->first(*static_cast<Graph1Node*>(&i));
      i.backward=false;
      if (*static_cast<Graph1Node*>(&i)==INVALID) {
	Parent2::graph->first(*static_cast<Graph2Node*>(&i));
	i.backward=true;
      }
    }
    void next(Node& i) const {
      if (!(i.backward)) {
	Parent1::graph->next(*static_cast<Graph1Node*>(&i));
	if (*static_cast<Graph1Node*>(&i)==INVALID) {
	  Parent2::graph->first(*static_cast<Graph2Node*>(&i));
	  i.backward=true;
	}
      } else {
	Parent2::graph->next(*static_cast<Graph2Node*>(&i));
      }
    }

    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:
      typedef _Value Value;
      typedef Node Key;
      NodeMap(const MergeNodeGraphWrapperBase<_Graph1, _Graph2>& gw) : 
	ParentMap1(gw), ParentMap2(gw) { }
      NodeMap(const MergeNodeGraphWrapperBase<_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[];
    };

  };


  template <typename _Graph1, typename _Graph2, typename Enable=void>
  class MergeNodeGraphWrapper : public 
  IterableGraphExtender<MergeNodeGraphWrapperBase<_Graph1, _Graph2, Enable> > {
  public:
    typedef _Graph1 Graph1;
    typedef _Graph2 Graph2;
    typedef IterableGraphExtender<
      MergeNodeGraphWrapperBase<_Graph1, _Graph2, Enable> > Parent;
  protected:
    MergeNodeGraphWrapper() { }
  public:
    MergeNodeGraphWrapper(_Graph1& _graph1, _Graph2& _graph2) { 
      Parent::Parent1::setGraph(_graph1);
      Parent::Parent2::setGraph(_graph2);
    }
  };

} //namespace lemon

#endif //LEMON_MERGE_NODE_GRAPH_WRAPPER_H
