//////////////////////////////////////////////////////////////////////////////
// LINK: Generic Graph Tool and Class Library                               
//      Function name: Dijkstra.cc                                      
//                                                                            
//      Synopsis:  Computes the single source shortest path of a graph.
//                                                                            
//      Description:             					      
//                                                                            
//      Creation:  Patricia Fasel pkf@c3.lanl.gov
//                                                                            
//      Routines used: BinaryHeaps
//                                                                            
//      Related files: Graph.h, Vertex.h, Edge.h, Attribute.h, BinaryHeap.h
//
//      Test suite:                                                           
//                                                                            
//      User documentation:                                                   
//                                                                            
//      Development History:                                                  
//           4/19/93 - murphy@c3.lanl.gov incoporated Binary Heaps to
//                     improve asymptotic running time
//
//           6/3/93  - murphy@c3.lanl.gov fixed it so it would work with
//                     directed graphs 
//
//           7/18/93 - ejohnson@c3.lanl.gov fixed problem that occurs when
//                     opposing edges exist in binary graph
//
//           7/21/93 - ejohnson@c3.lanl.gov added check when adding distance
//                     attribute to ensure that it doesn't already exist
//
//           8/16/93 - ejohnson@c3.lanl.gov changed initialization so
//                     that it works on directed and undirected graphs
//
//      Testing History:                                                      
//           date  - full@email.address.xxx  description of tests done
//                                                                           
//      Code Review:                                                          
//           date  - full@email.address.xxx  any notes you may have 
//
//      Bugs and Deficiencies:                                               
//                                                                            
//////////////////////////////////////////////////////////////////////////////


#include <strstream.h>
#include <stdio.h>
#include <limits.h>
#include <LINK/graph/Graph.h>
#include <LINK/graph/Vertex.h>
#include <LINK/graph/Edge.h>
#include <LINK/graph/Attribute.h>
#include <LINK/basic/BinaryHeap.h>
#include <LINK/algorithm/Algorithms.h>


void animateRelax(Vertex *v, char *graph_view, double new_sp)
{
    if (!graph_view) return; 

    ostrstream oss;
    oss << new_sp << ends;
    char *d_label = oss.str();
    animateVertex(v, graph_view, "color",
			"color", INIT_STEP, (char*)"white", JIGGLE);
    animateVertex(v, graph_view, "label",
			"vertex-label", ALG_STEP, d_label, JIGGLE);
}

void animatePred(Graph* g, char *graph_view, Vertex* vertex, Vertex* new_pred)
{
	if (!graph_view) return;

	Vertex *old_pred;
	Edge *e;
	getAttribute((GraphObject*)vertex, "pred", old_pred);
	if (old_pred) {
		e = g->isEdge(old_pred, vertex);
                animateEdge(e, graph_view, "width","width", 
					INIT_STEP, 2, JIGGLE);
                animateEdge(e, graph_view, "color","color", 
					INIT_STEP, (char*)"black");
	}
	e = g->isEdge(new_pred, vertex);
	animateEdge(e, graph_view, "width", "width", INIT_STEP, 4);
        animateEdge(e, graph_view, "color","color", 
					INIT_STEP, (char*)"red");
}

void
initSingleInput(Graph* graph, char *graph_view, Vertex* source, 
	        BinaryHeap<double,Vertex*> &heap)
{
    ContainerNode* heapPtr;
    double large_val = (double) INT_MAX;

    Vertex** vertices = graph->vertexStart();
    if (!source)
	source = vertices[0];
    int order = graph->order();
    for (int i=0; i < order; i++) {

	// source vertex has a distance of 0
	if (vertices[i] == source) {
            heapPtr = heap.insert(0, vertices[i]);
	    animateVertex(vertices[i], graph_view, "color","color", INIT_STEP,
				(char*)"gray");
	    animateVertex(vertices[i], graph_view, "label","vertex-label", 
			INIT_STEP, (char*)"0");

        } else {
	    animateVertex(vertices[i], graph_view, "color","color", INIT_STEP,
				(char*)"white");
	    animateVertex(vertices[i], graph_view, "label","vertex-label", 
			INIT_STEP, (char*)"-");
            heapPtr = heap.insert(large_val, vertices[i]);
	}
	setAttribute((GraphObject*) vertices[i], "heap_info", heapPtr);
	setAttribute((GraphObject*) vertices[i], "pred", 0);
    }
    	const MSet<Edge*>& edges = graph->edges();
    	Iterator<Edge*> get_edge(&edges);
	Edge *edge;
    	while (get_edge(edge)) {
        	double weight;
        	getAttribute((GraphObject*) edge, "weight", weight);
		setAttribute((GraphObject*) edge, "mark", 0);
		if (graph_view) {
			ostrstream oss;
			char *wgt;
			oss << weight << ends;
			wgt = oss.str();
    			animateEdge(edge, graph_view, "label", 
					      "edge-label", INIT_STEP, wgt,
					      FLASH, 0, 0);
		// LEAK! must incorporate LINK_DYNAMIC
		}
    	}
}


Sequence<double>
Dijkstra(Graph* graph, char *graph_view, Vertex* source_vertex)
{
    Sequence<double> result;
    if (!graph)
	return result;
    Vertex* vertex;
    ContainerNode* heapPtr;
    Attribute<ContainerNode*> bin_heap(graph, "heap_info", (ContainerNode*) 0);

    // create the binary heap to store the distance data
    int num_vertices = graph->order();    
    BinaryHeap<double,Vertex*> heap(num_vertices);

    // initialize the distances of all vertices from the source vertex
    initSingleInput(graph, graph_view, source_vertex, heap);

    // iterate through the processing list until empty, selecting the min vertex
    for (int j=0; j< num_vertices; j++) {
	BinaryHeapNode<double,Vertex*>* heap_node = heap.extractMin();
        vertex = ((BinaryHeapNode<double,Vertex*>*) heap_node)->item;
        animateVertex(vertex, graph_view, "color","color", 
					ALG_STEP, (char*)"black");
        double u_dist = heap_node->key;

	// calculate new distances for all neighbors of the current min vertex
	Set<Edge*> out_edges = graph->outIncidentEdges(vertex);
	Iterator<Edge*> get_incident_edge(&out_edges);
	Edge* inc_edge;
	int mark;
	while (get_incident_edge(inc_edge)) {
	    getAttribute((GraphObject*)inc_edge, "mark", mark);
	    if (mark) continue;
	    setAttribute((GraphObject*)inc_edge, "mark", 1);
	    double e_weight;
	    char *current_color;
	    getAttribute((GraphObject*) inc_edge, "weight", e_weight);
	    getAttribute((GraphObject*) inc_edge, "color", current_color);
            animateEdge(inc_edge, graph_view, "color","color", 
					ALG_STEP, (char*)"black", JIGGLE);
            animateEdge(inc_edge, graph_view, "color","color", 
					ALG_STEP, current_color, JIGGLE);
	    Iterator<Vertex*> get_neighbor(inc_edge->vertices());
	    Vertex* neighbor_vertex;
	    while (get_neighbor(neighbor_vertex))

		if (vertex != neighbor_vertex) {
		    getAttribute((GraphObject*) neighbor_vertex, 
			"heap_info", heapPtr);
		    double v_dist = 
			 ((BinaryHeapNode<double,Vertex*>*) heapPtr)->key;

		    if (v_dist > u_dist + e_weight) {
			animateRelax(neighbor_vertex,graph_view,
					u_dist+e_weight);
			heap.decreaseKey(heapPtr, u_dist+e_weight);
			animatePred(graph, graph_view, neighbor_vertex, vertex);
			setAttribute((GraphObject*)neighbor_vertex,"pred",
						vertex);
		    }
		}
	}
    }

    // put the result into "distance" attribute belonging to each vertex
    Vertex** vertices = graph->vertexStart();
    int order = graph->order();
    for (int i=0; i < order; i++) {
	getAttribute((GraphObject*) vertices[i], "heap_info", heapPtr);
        double dist = ((BinaryHeapNode<double,Vertex*>*) heapPtr)->key;
	setAttribute((GraphObject*) vertices[i], "distance", dist);
	result.append(dist);
    }
    return result;
}

int dummy_instantiations()
{
	Set<char *> s;
	List<char *> l;
	cout << s << l << endl;
	return 0;
}
