// Stanis³aw G¹siorowski G1ISI

#include <iostream>
#include <string>
#include <boost/graph/graphml.hpp>

using namespace std;
using namespace boost;

// definiujemy typ grafu
typedef adjacency_list< vecS, vecS, directedS,
    property< vertex_name_t, string >,
    property< edge_weight_t, int >
    > Graph;

void CreateGraph(Graph &g, dynamic_properties &dp)
{
	// nazwy wierzcholkow
	const char* name[] = { "0", "1", "2", "3", "4", "5",
						   "6", "7", "8", "9", "10" };

	// definiujemy typ krawedzi
	typedef std::pair<int,int> Edge;

	// tworzymy krawedzie grafu
    Edge my_edges[] = {
		Edge(0, 1),
        Edge(1, 2), Edge(1, 5), Edge(1, 8),
        Edge(2, 3),
        Edge(3, 2), Edge(3, 4), Edge(3, 6),
        Edge(6, 7),
		Edge(7, 8),
        Edge(8, 7),
        Edge(9, 10),
        Edge(10, 9)
     };

	// tworzymy wagi odpowienich krawedzi
	int my_wages[] = {
		4,
		2, 1, 5,
		3,
		3, 1, 2,
		9,
		2,
		2,
		6,
		6
	};

	// ilosc krawedzi
    const int nedges = sizeof(my_edges)/sizeof(Edge);

	// tworzymy graf z wierzcholkow
    g = Graph(my_edges, my_edges + nedges, 10);

	// ustawiamy wartosci wierzcholkow (tutaj np. stringi)
    graph_traits<Graph>::vertex_iterator v, v_end;
    for (tie(v,v_end) = vertices(g); v != v_end; ++v)
        put(vertex_name_t(), g, *v, name[*v]);

	// ustawiamy wagi krawedzi
    graph_traits<Graph>::edge_iterator e, e_end;
	int index = 0;
    for (tie(e,e_end) = edges(g); e != e_end; ++e, ++index)
        put(edge_weight_t(), g, *e, my_wages[index]);

	// ustawiamy, ze wierzcholki beda mialy pole name a krawedzie weight
    dp.property("name", get(vertex_name_t(), g));
    dp.property("weight", get(edge_weight_t(), g));
}

void SaveGraphML(Graph &g, dynamic_properties &dp, const char *filename)
{
	ofstream ofile(filename);

	// skladnia:
	//void write_graphml(std::ostream& out, const Graph& g, const dynamic_properties& dp,
    //          bool ordered_vertices=false)

	// zapis do strumienia
    write_graphml(ofile, g, dp, true);

    ofile.close();
}

void LoadGraphML(Graph &g, dynamic_properties &dp, const char *filename)
{
    ifstream ifile(filename);

	// musimy dodac wlasnosci recznie
    dp.property("name", get(vertex_name_t(), g));
    dp.property("weight", get(edge_weight_t(), g));

	// lapiemy mozliwe wyjatki
	try
	{
		read_graphml(ifile, g, dp);
	}
	catch (bad_parallel_edge &ge)
	{
		cout << "bad_parallel_edge from " << ge.from << " to " << ge.to << endl;
	}
	catch (directed_graph_error&)
	{
		cout << "directed_graph_error" << endl;
	}
	catch (undirected_graph_error&)
	{
		cout << "undirected_graph_error" << endl;
	}
	catch (parse_error&)
	{
		cout << "parse_error" << endl;
	}
	catch (property_not_found &ge)
	{
		cout << "property_not_found " << ge.property << endl;
	}

    ifile.close();
}

void SaveGraphVIZ(Graph &g, dynamic_properties &dp, const char *filename)
{
	ofstream ofile(filename);

	// zapis do strumienia, "name" jako identyfikator wezla
	write_graphviz(ofile, g, dp, string("name"));

    ofile.close();
}

void LoadGraphVIZ(Graph &g, dynamic_properties &dp, const char *filename)
{
	ifstream ifile(filename);

	// nalezy recznie dodac wlasnosci
	dp.property("name", get(vertex_name_t(), g));
    dp.property("weight", get(edge_weight_t(), g));

	// skladnia:
	// bool read_graphviz(std::istream& in, MutableGraph& graph,
    //               dynamic_properties& dp,
    //               std::string const& node_id = "node_id") 

	// lapiemy mozliwe wyjatki
	try
	{
		// odczyt ze strumienia, "name" jako identyfikator wezla
		read_graphviz(ifile, g, dp, "name");
	}
	catch (bad_parallel_edge &ge)
	{
		cout << "bad_parallel_edge from " << ge.from << " to " << ge.to << endl;
	}
	catch (directed_graph_error&)
	{
		cout << "directed_graph_error" << endl;
	}
	catch (undirected_graph_error&)
	{
		cout << "undirected_graph_error" << endl;
	}
	catch (parse_error&)
	{
		cout << "parse_error" << endl;
	}
	catch (property_not_found &ge)
	{
		cout << "property_not_found " << ge.property << endl;
	}

    ifile.close();
}

void CheckGraphs(Graph &g1, Graph &g2)
{
	assert(num_vertices(g2) == num_vertices(g1));
    assert(num_edges(g2) == num_edges(g1));

	graph_traits<Graph>::vertex_iterator v, v_end;
    for (tie(v,v_end) = vertices(g1); v != v_end; ++v)
        assert(get(vertex_name_t(), g1, *v) == get(vertex_name_t(), g2, *v));

    graph_traits<Graph>::edge_iterator e, e_end;
    for (tie(e,e_end) = edges(g1); e != e_end; ++e)
        assert(get(edge_weight_t(), g1, *e) == get(edge_weight_t(), g2, *e));
}

int main(int argc, char *argv[])
{
	Graph g;
	dynamic_properties dp;
	CreateGraph(g, dp);

	// zapisujemy graph do pliku
	SaveGraphML(g, dp, "graph.xml");

	// odczytujemy nowy graf
	Graph g2;
	dynamic_properties dp2;   
	LoadGraphML(g2, dp2, "graph.xml");

	// sprawdzenie czy dobrze zostalo zapisane i wczytane
	CheckGraphs(g, g2);

	// --------------------------------------------------

	// zapisujemy graph do pliku
	SaveGraphVIZ(g, dp, "graph_viz.dot");

	Graph g3;
	dynamic_properties dp3;   
	LoadGraphVIZ(g3, dp3, "graph_viz.dot");

	// sprawdzenie czy dobrze zostalo zapisane i wczytane
	CheckGraphs(g, g3);

	// wypisujemy na ekran
	write_graphml(cout, g3, dp3, true);

	return 0;
}

