/*
 * Copyright (c) 1999 Johnny C. Lam.
 * All rights reserved.
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  The author makes no representations
 * about the suitability of this software for any purpose.  It is
 * provided "as is" without express or implied warranty.
 */

#include <iostream>
#include <fstream>
#include <list>
#include <numeric>
#include <graph>
#include <graphalg>

#include <GTL_connectivity.h>

using namespace std;

template <class Graph>
struct EdgeWeight {
    Graph const & G;

    EdgeWeight(Graph const & g) : G(g) {}

    graph_traits<Graph>::weight_type
    operator ()( graph_traits<Graph>::edge_type const & e ) const {
	return G.weight(e);
    }
};

template <class Graph>
void
PrintGraph( Graph const &g )
{
    typedef graph_traits<Graph>::value_type	value_type;
    typedef graph_traits<Graph>::weight_type	weight_type;

    cout << "Size:       " << g.size() << endl;
    cout << "Num. nodes: " << g.n_size() << endl;
    cout << "Num. edges: " << g.e_size() << endl;

    EdgeWeight<Graph>	edge_weight(g);
    weight_type wgt = weight( g, edge_weight, static_cast<weight_type>(0) );
    cout << "Weight 1:   " << wgt << endl
	 << "Weight 2:   " << weight( g ) << endl;

    typename graph_traits<Graph>::graph_type::const_node_iterator 	i;
    typename graph_traits<Graph>::node_type::out_edges_iterator		j;
    for ( i = g.n_begin(); i != g.n_end(); ++i ) {
	typename graph_traits<Graph>::node_type const &n = *i;
	cout << "Node " << g.value(n) << ":" << endl;
	for ( j = n.out_edges_begin(); j != n.out_edges_end(); ++j ) {
	    cout << "\t" << "---(" << g.weight(*j) << ")---> "
		 << g.value((*j).target()) << endl;
	}
    }
}

template <class Graph>
void
PrintNode(Graph const &g, typename graph_traits<Graph>::node_type const &n)
{
    cout << "\t\t" << "Node " << g.value(n) << ":" << endl;
    typename graph_traits<Graph>::node_type::out_edges_iterator	oi;
    cout << "\t\t\t" << "out:"; cout.flush();
    for ( oi = n.out_edges_begin(); oi != n.out_edges_end(); ++oi ) {
	cout << "  " << g.weight(*oi); cout.flush();
    }
    cout << endl
	 << "\t\t\t" << "in: "; cout.flush();
    typename graph_traits<Graph>::node_type::in_edges_iterator	ii;
    for ( ii = n.in_edges_begin(); ii != n.in_edges_end(); ++ii ) {
	cout << "  " << g.weight(*ii); cout.flush();
    }
    cout << endl
	 << "\t\t" << "End Node." << endl;
}

int
main ()
{
    typedef stl_graph<int, double> Graph;
    typedef graph_traits<Graph> traits;
    typedef typename traits::node_type node_type;
    typedef typename traits::edge_type edge_type;

    Graph *g, *h, *G, *H;
    
    cout << "Empty graph:" << endl;
    h = new Graph();
    delete h;
    cout << "End empty graph." << endl;
    
    cout << "Graph:" << endl;
    g = new Graph;
    
    cout << "\t" << "Adding nodes and edges:" << endl;
    node_type	n1 = g->add_node( 11 );
    node_type	n2 = g->add_node( 22 );
    node_type	n3 = g->add_node( 33 );
    
    edge_type	e1 = g->add_edge( n2, n1, 4 );
    edge_type	e2 = g->add_edge( n3, n2, 1 );
    
    cout << "n1.value() =  " << g->value(n1) << endl;
    cout << "e1.weight() = " << g->weight(e1) << endl;
    
    PrintNode( *g, n1 );
    
    g->add_node( 44 );
    g->add_node( 55 );
    
    g->add_node( 77 );
    g->add_node( 88 );
    g->add_node( 99 );

    g->add_edge( g->find_node( 22 ), g->find_node( 44 ), 6 );
    g->add_edge( g->find_node( 33 ), g->find_node( 44 ), 2 );
    g->add_edge( g->find_node( 11 ), g->find_node( 33 ), 5 );
    g->add_edge( g->find_node( 33 ), g->find_node( 55 ), 3 );
    
    g->add_edge( g->find_node( 77 ), g->find_node( 88 ), 1 );
    g->add_edge( g->find_node( 88 ), g->find_node( 99 ), 2 );
    g->add_edge( g->find_node( 99 ), g->find_node( 77 ), 3 );

    PrintNode( *g, n1 );
    
    cout << "\t" << "End adding nodes and edges." << endl;
    
    cout << "=======================" << endl;
    PrintGraph( *g );
    cout << "=======================" << endl;
    
    cout << "=======================" << endl;
    cout << *g << endl;
    cout << "=======================" << endl;

    connectivity comp_alg;
    comp_alg.scan_whole_graph(true);
    comp_alg.calc_comp_num(true);
    comp_alg.run(g->topology());

    Graph::node_iterator n = g->n_begin();
    Graph::node_iterator end = g->n_end();
    cout << "DFS:" << endl;
    while (n != end) {
	cout << "\t" << g->value(*n)
	     << ": " << comp_alg.dfs_num(*n) << endl;
	++n;
    }
    n = g->n_begin();
    cout << "Components:" << endl;
    while (n != end) {
	cout << "\t" << g->value(*n)

	     << ": " << comp_alg.component_num(*n) << endl;
	++n;
    }
    for (int i = 0; i < comp_alg.number_of_components(); i++) {
	cout << "Component " << i << ":";
	connectivity::component_iterator c = comp_alg.component_begin(i);
	connectivity::component_iterator end = comp_alg.component_end(i);
	while (c != end) {
	    cout << "  " << g->value(*c);
	    ++c;
	}
	cout << endl;
    }
    H = new Graph();
    H->add_node_range(*g, comp_alg.component_begin(0), comp_alg.component_end(0));
    cout << "=======================" << endl;
    cout << *H << endl;
    cout << "=======================" << endl;
    delete H;

    list<edge_type>	tree;
    node_map<double>	dist;
    Dijkstra(*g, g->find_node(22), &dist, back_inserter(tree));

    cout << "Distances:" << endl;
    for (Graph::node_iterator n = g->n_begin(); n != g->n_end(); ++n) {
	cout << "\t" << g->value(*n) << " :\t" << dist[*n] << endl;
    }
    
    cout << "Tree:" << endl;
    cout << "\t" << "tree.size() = " << tree.size() << endl;
    list<edge_type>::iterator l;
    for (l = tree.begin(); l != tree.end(); ++l) {
	cout << "\t" << g->value(l->source()); cout.flush();
	cout << " <---(" << g->weight(*l) << ")--- "; cout.flush();
	cout << g->value(l->target()) << endl; cout.flush();
    }
    
    H = new Graph();
    H->add_edge_range(*g, tree.begin(), tree.end());
    cout << "======== Tree =========" << endl;
    cout << *H << endl;
    cout << "=======================" << endl;
    cout << "== pstree-style printout:" << endl;
    WriteTreeTopology(cout, *H, H->find_node(22), " ");
    cout << "=======================" << endl;
    cout << "=== table printout ====" << endl;
    WriteRootedTree(cout, *H, H->find_node(22));
    cout << "=======================" << endl;
    delete H;

    cout << "\t" << "Duplicating by copying edge range:" << endl;
    G = new Graph();
    G->add_edge_range(*g, g->e_begin(), g->e_end());
    cout << "=======================" << endl;
    PrintGraph(*G);
    cout << "=======================" << endl;
    delete G;
    cout << "\t" << "End duplicating." << endl;
    
    cout << "\t" << "Erasing edges:" << endl;
    g->erase( e1 );
    cout << "\t\t" << "e1 gone" << endl;
    g->erase( e2 );
    cout << "\t\t" << "e2 gone" << endl;
    cout << "\t" << "End erasing edges." << endl;
    
    cout << "=======================" << endl;
    PrintGraph(*g);
    cout << "=======================" << endl;
    
    cout << "\t" << "Erasing nodes:" << endl;
    g->erase( n1 );
    cout << "\t\t" << "n1 gone" << endl;
    g->erase( n2 );
    cout << "\t\t" << "n2 gone" << endl;
    g->erase( n3 );
    cout << "\t\t" << "n3 gone" << endl;
    cout << "\t" << "End erasing nodes." << endl;
    
    cout << "=======================" << endl;
    PrintGraph( *g );
    cout << "=======================" << endl;
    
    g->e_clear();
    
    cout << "=======================" << endl;
    cout << *g << endl;
    cout << "=======================" << endl;
    
    g->clear();
    
    cout << "=======================" << endl;
    PrintGraph( *g );
    cout << "=======================" << endl;
    
    delete g;
    cout << "End graph." << endl;
    
    cout << "Reading graph:" << endl;
    G = new Graph();
    ifstream istr( "test/graph.file" );
    istr >> *G;
    istr.close();
    cout << "End reading graph." << endl;
    
    cout << "=======================" << endl;
    cout << *G << endl;
    cout << "=======================" << endl;
    
    delete G;
    
    return 0;
}
