#include<iostream>
#include<hugo/smart_graph.h>
#include<hugo/skeletons/graph.h>
#include<../work/alpar/list_graph.h>

/*
This test makes consistency checks of list graph structures.

G.addNode(), G.addEdge(), G.valid(), G.tail(), G.head()

*/

using namespace hugo;

// void check(bool rc, const char *msg) {
//   if(!rc) {
//     std::cerr << msg << std::endl;
//     exit(1);
//   }
// }

#define check(rc, msg) \
  if(!rc) { \
    std::cerr << __FILE__ ":" << __LINE__ << ": error: " << msg << std::endl; \
    exit(1); \
  } else { } \


template<class Graph> void checkCompile(Graph &G) 
{
  typedef typename Graph::Node Node;
  typedef typename Graph::NodeIt NodeIt;
  typedef typename Graph::Edge Edge;
  typedef typename Graph::EdgeIt EdgeIt;
  typedef typename Graph::InEdgeIt InEdgeIt;
  typedef typename Graph::OutEdgeIt OutEdgeIt;
  
  {
    Node i; Node j(i); Node k(INVALID);
    i=j;
    bool b=G.valid(i); b=b;
    b=(i==j); b=(i!=j); b=(i<j);
  }
  {
    NodeIt i; NodeIt j(i); NodeIt k(INVALID); NodeIt l(G);
    i=j;
    j=G.first(i);
    j=G.next(i);
    bool b=G.valid(i); b=b;
    Node n(i);
    n=i;
    b=(i==j); b=(i!=j); b=(i<j);
  }
  {
    Edge i; Edge j(i); Edge k(INVALID);
    i=j;
    bool b=G.valid(i); b=b;
    b=(i==j); b=(i!=j); b=(i<j);
  }
  {
    EdgeIt i; EdgeIt j(i); EdgeIt k(INVALID); EdgeIt l(G);
    i=j;
    j=G.first(i);
    j=G.next(i);
    bool b=G.valid(i); b=b;
    Edge e(i);
    e=i;
    b=(i==j); b=(i!=j); b=(i<j);
  }
  {
    Node n;
    InEdgeIt i; InEdgeIt j(i); InEdgeIt k(INVALID); InEdgeIt l(G,n);
    i=j;
    j=G.first(i,n);
    j=G.next(i);
    bool b=G.valid(i); b=b;
    Edge e(i);
    e=i;
    b=(i==j); b=(i!=j); b=(i<j);
  }
  {
    Node n;
    OutEdgeIt i; OutEdgeIt j(i); OutEdgeIt k(INVALID); OutEdgeIt l(G,n);
    i=j;
    j=G.first(i,n);
    j=G.next(i);
    bool b=G.valid(i); b=b;
    Edge e(i);
    e=i;
    b=(i==j); b=(i!=j); b=(i<j);
  }

  Node n,m;
  n=G.addNode();
  Edge e;
  e=G.addEdge(n,m);
  n=G.tail(e);
  n=G.head(e);

  //aNode, bNode ?

  // id tests
  { int i=G.id(n); i=i; }
  { int i=G.id(e); i=i; }
  
  G.clear();

  //NodeMap tests
  {
    Node k;
    typename Graph::template NodeMap<int> m(G);
    typename Graph::template NodeMap<int> const &cm = m;  //Const map
    //Inicialize with default value
    typename Graph::template NodeMap<int> mdef(G,12);
    typename Graph::template NodeMap<int> mm(cm);   //Copy
    typename Graph::template NodeMap<double> dm(cm); //Copy from another type
    int v;
    v=m[k]; m[k]=v; m.set(k,v);
    v=cm[k];
    
    m=cm;  
    dm=cm; //Copy from another type
  }  
  { //bool NodeMap
    Node k;
    typename Graph::template NodeMap<bool> m(G);
    typename Graph::template NodeMap<bool> const &cm = m;  //Const map
    //Inicialize with default value
    typename Graph::template NodeMap<bool> mdef(G,12);
    typename Graph::template NodeMap<bool> mm(cm);   //Copy
    typename Graph::template NodeMap<int> dm(cm); //Copy from another type
    bool v;
    v=m[k]; m[k]=v; m.set(k,v);
    v=cm[k];
    
    m=cm;  
    dm=cm; //Copy from another type
    m=dm; //Copy to another type
  }
  //EdgeMap tests
  {
    Edge k;
    typename Graph::template EdgeMap<int> m(G);
    typename Graph::template EdgeMap<int> const &cm = m;  //Const map
    //Inicialize with default value
    typename Graph::template EdgeMap<int> mdef(G,12);
    typename Graph::template EdgeMap<int> mm(cm);   //Copy
    typename Graph::template EdgeMap<double> dm(cm); //Copy from another type
    int v;
    v=m[k]; m[k]=v; m.set(k,v);
    v=cm[k];
    
    m=cm;  
    dm=cm; //Copy from another type
  }  
  { //bool EdgeMap
    Edge k;
    typename Graph::template EdgeMap<bool> m(G);
    typename Graph::template EdgeMap<bool> const &cm = m;  //Const map
    //Inicialize with default value
    typename Graph::template EdgeMap<bool> mdef(G,12);
    typename Graph::template EdgeMap<bool> mm(cm);   //Copy
    typename Graph::template EdgeMap<int> dm(cm); //Copy from another type
    bool v;
    v=m[k]; m[k]=v; m.set(k,v);
    v=cm[k];
    
    m=cm;  
    dm=cm; //Copy from another type
    m=dm; //Copy to another type
  }
  
}



template<class Graph> void addPetersen(Graph &G)
{
  std::vector<typename Graph::Node> outer, inner;
  
  for(int i=0;i<5;i++) {
    outer.push_back(G.addNode());
    inner.push_back(G.addNode());
  }

 for(int i=0;i<5;i++) {
    G.addEdge(outer[i],inner[i]);
    G.addEdge(outer[i],outer[(i+1)%5]);
    G.addEdge(inner[i],inner[(i+2)%5]);
  }
}

template<class Graph> void checkNodeList(Graph &G, int nn)
{
  typename Graph::NodeIt n(G);
  for(int i=0;i<nn;i++) {
    check(G.valid(n),"Wrong Node list linking.");
    G.next(n);
  }
  check(!G.valid(n),"Wrong Node list linking.");
}

template<class Graph> void checkEdgeList(Graph &G, int nn)
{
  typedef typename Graph::EdgeIt EdgeIt;

  EdgeIt e(G);
  for(int i=0;i<nn;i++) {
    check(G.valid(e),"Wrong Edge list linking.");
    G.next(e);
  }
  check(!G.valid(e),"Wrong Edge list linking.");
}

template<class Graph> void checkOutEdgeList(Graph &G,
					    typename Graph::Node n,
					    int nn)
{
  typename Graph::OutEdgeIt e(G,n);
  for(int i=0;i<nn;i++) {
    check(G.valid(e),"Wrong OutEdge list linking.");
    G.next(e);
  }
  check(!G.valid(e),"Wrong OutEdge list linking.");
}

template<class Graph> void checkInEdgeList(Graph &G,
					    typename Graph::Node n,
					    int nn)
{
  typename Graph::InEdgeIt e(G,n);
  for(int i=0;i<nn;i++) {
    check(G.valid(e),"Wrong InEdge list linking.");
    G.next(e);
  }
  check(!G.valid(e),"Wrong InEdge list linking.");
}

//Checks head(), tail() as well;
template<class Graph> void bidirPetersen(Graph &G)
{
  typedef typename Graph::Edge Edge;
  typedef typename Graph::EdgeIt EdgeIt;
  
  checkEdgeList(G,15);
  
  std::vector<Edge> ee;
  
  for(EdgeIt e(G);G.valid(e);G.next(e)) ee.push_back(e);

  for(typename std::vector<Edge>::iterator p=ee.begin();p!=ee.end();p++)
    G.addEdge(G.head(*p),G.tail(*p));
}

template<class Graph> void checkPetersen(Graph &G)
{
  typedef typename Graph::Node Node;

  typedef typename Graph::EdgeIt EdgeIt;
  typedef typename Graph::NodeIt NodeIt;

  checkNodeList(G,10);
  checkEdgeList(G,30);

  for(NodeIt n(G);G.valid(n);G.next(n)) {
    checkInEdgeList(G,n,3);
    checkOutEdgeList(G,n,3);
    G.next(n);
  }  
}

template void checkCompile<GraphSkeleton>(GraphSkeleton &);
template void checkCompile<SmartGraph>(SmartGraph &);
template void checkCompile<SymSmartGraph>(SymSmartGraph &);
template void checkCompile<ListGraph>(ListGraph &);
template void checkCompile<SymListGraph>(SymListGraph &);
//Due to some mysterious and some conceptual problems it does not work.
//template void checkCompile<EdgeSet <ListGraph> >(EdgeSet <ListGraph> &);

int main() 
{
  {
    SmartGraph G;
    addPetersen(G);
    bidirPetersen(G);
    checkPetersen(G);
  }
  {
    ListGraph G;
    addPetersen(G);
    bidirPetersen(G);
    checkPetersen(G);
  }
  {
    SymSmartGraph G;
    addPetersen(G);
    checkPetersen(G);
  }
  {
    SymListGraph G;
    addPetersen(G);
    checkPetersen(G);
  }

  //\todo map tests.
  //\todo copy constr tests.

  std::cout << __FILE__ ": All tests passed.\n";

}
