// Copyright (C) 1996 DIMACS Center, Rutgers, The State University of New Jersey
// Author(s): Patricia K. Fasel (Los Alamos Nat. Lab.), Jonathan Berry,
//	      Chris Burrows (Trenton State College)
// This software is copyrighted by the DIMACS Center at Rutgers, The State
// University of New Jersey.  IT IS PROVIDED AS IS, AND THE AUTHORS, DIMACS, AND
// RUTGERS, THE STATE UNIVERSITY OF NEW JERSEY  DISCLAIM
// ALL LIABILITY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
// DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
// IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
// NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
// MODIFICATIONS.
// The authors hereby grant permission to use, copy, modify, distribute,
// and license this software and its documentation for any purpose, provided
// that existing copyright notices are retained in all copies and that this
// notice is included verbatim in any distributions. No written agreement,
// license, or royalty fee is required for any of the authorized uses.
// Modifications to this software may be copyrighted by their authors
// and need not follow the licensing terms described here, provided that
// the new terms are clearly indicated on the first page of each file where
// they apply.
// Last File Update: 21-Jul-1997
// 
#include <iostream>
#include <string>
#include <list>
#include <stdio.h>
#include <iostream.h>
#include <strstream.h>
#include <LINK/graph/Graph.h>
#include <LINK/graph/Vertex.h>
#include <LINK/graph/Edge.h>
#include <LINK/graph/Attribute.h>
#include <LINK/basic/Sequence.h>
#include <LINK/algorithm/Algorithms.h>
static char white[] = "white"; 
static char gray[]  = "gray"; 
static char black[] = "black"; 
const static int white_mark = 0;
const static int gray_mark  = 1;
const static int black_mark = 2;
//
// visit all the neighbors of the vertex 
//
static void
DFSvisit(Graph* graph, Vertex* v, Sequence<Vertex*>& result, 
	 int& time, 
	 int &low,   // min {back(v), low(c: c is a child of v)}
	 char* graph_view, int height, int dfs_tree)
{
    time++;
    int back=time;    // back will be the starttime of the earliest vertex
                      // to which v is connected via a back edge

    // the "type" attribute stores the dfs tree number (used by SCC)
    setAttribute((GraphObject*) v, "type", dfs_tree);
    setAttribute((GraphObject*) v, "mark", gray_mark);
    setAttribute((GraphObject*) v, "distance", height);
    setAttribute((GraphObject*) v, "starttime", time);
    char* label;
    animateVertex(v, graph_view, "color", "color", INIT_STEP, (char*)gray);
    if (graph_view) {
    	ostrstream oss;
    	oss << time << "/" << ends;
    	label = oss.str();
	char *lab;
	getAttribute((GraphObject*)v, "label", lab);
    	animateVertex(v, graph_view, 
		      "label", "vertex-label", ALG_STEP, label);
	getAttribute((GraphObject*)v, "label", label);
    }
    Set<Edge*> outIncEdges = graph->outIncidentEdges(v);
    Iterator<Edge*> get_edge(&outIncEdges);
    Edge* incEdge;
    
    int lowest_of_childrens_lows = time;
    while (get_edge(incEdge)) {
	int emark;
	getAttribute((GraphObject*) incEdge, "mark", emark);
        if (!emark) { 
	    
	    Sequence<Vertex*> neighbors = incEdge->otherVertices(v);
	    Iterator<Vertex*> get_neighbor(&neighbors);
	    Vertex* adjVertex;
    
	    setAttribute((GraphObject*) incEdge, "mark", 1);
	    
            char *edgeColor = black;
            char *edgeType = "none";
            while (get_neighbor(adjVertex)) {
		int child_low;
	        int vmark;
	        getAttribute((GraphObject*) adjVertex, "mark", vmark);
                switch (vmark) {
		case white_mark: // tree edge
		    setAttribute((GraphObject*)adjVertex, "pred", v);
		    result.append(adjVertex);
		    edgeColor = "red";
		    edgeType = "tree";
		    animateEdge(incEdge, graph_view, "color", 
				"color", INIT_STEP, edgeColor);
		    animateEdge(incEdge, graph_view, "label", 
				"edge-label", ALG_STEP, edgeType);
		    DFSvisit(graph, adjVertex, result, time, child_low, 
			     graph_view, ++height, dfs_tree);
		    setAttribute((GraphObject*) incEdge, 
				 "type", TREE_EDGE_TYPE);
		    if (child_low < lowest_of_childrens_lows)
			lowest_of_childrens_lows = child_low;
		    break;
		    
		case gray_mark: // back edge
		    int neigh_dtime, v_dtime;
		    getAttribute((GraphObject*) v, "starttime",
				 v_dtime);
		    getAttribute((GraphObject*) adjVertex, "starttime",
				 neigh_dtime);
		    if (neigh_dtime < back)
			back = neigh_dtime;
		    // the following comparison prevents hyperedges
		    // from being re-typed as neighbors are processed 
		    if (strcmp(edgeType, "tree")!=0) {
			edgeColor = "blue";  // backedge
			edgeType  = "back";
			setAttribute((GraphObject*) incEdge, 
				     "type", BACK_EDGE_TYPE);
		    }
		    break;
		    
		case black_mark:  // cross edge or forward edge
		    getAttribute((GraphObject*) v, "starttime",
				 v_dtime);
		    getAttribute((GraphObject*) adjVertex, "starttime",
				 neigh_dtime);
		    // the following comparison prevents hyperedges
		    // from being re-typed as neighbors are processed 
		    if (strcmp(edgeType, "tree")!=0) {
			if (v_dtime < neigh_dtime) {
			    edgeColor = "green";  // forward edge
			    edgeType = "forward";
			    setAttribute((GraphObject*) incEdge, 
					 "type", FORWARD_EDGE_TYPE);
			    
			} else {
			    edgeColor = "black";  // cross edge
			    edgeType = "cross";
			    setAttribute((GraphObject*) incEdge, 
					 "type", CROSS_EDGE_TYPE);
			    
			}
		    }
		    break;
	        }
            }
	    if (strcmp(edgeType, "tree")!=0) {
            	animateEdge(incEdge, graph_view, "color", "color", 
			    INIT_STEP, edgeColor);
            	animateEdge(incEdge, graph_view, "label", "edge-label", 
			    ALG_STEP, edgeType);
	    }
	}
    }
    low = back < lowest_of_childrens_lows ? back : lowest_of_childrens_lows; 
    setAttribute((GraphObject*) v, "low", low);
    setAttribute((GraphObject*) v, "mark", black_mark);
    animateVertex(v, graph_view, "color", "color", INIT_STEP, 
		  (char*)black);
    
    time++;
    setAttribute((GraphObject*) v, "finishtime", time);
    
    if (graph_view) {
	char *label2;
	ostrstream oss;
    	oss << label << time << ends;

    	label2 = oss.str();
	//if (label) { 			LEAK!!!!  Putting this delete
	//	delete label;           back in cases corrupted strings
	//	label = 0;              to be put into the "backwards"
	//}                             animation steps.
    	animateVertex(v, graph_view, 
		      "label","vertex-label", ALG_STEP, label2);
    }
}
//
// depth first search returns an ordered sequence of vertices
//
Sequence<Vertex*>
DepthFirstSearch(Graph* graph, VertexComparison compare, char *graph_view)
{
    Sequence<Vertex*> result;
    if (!graph)
	return result;

    int dfs_tree = 0;
    char **vcolor  = new char*[graph->order()];
    char **vlabel  = new char*[graph->order()];
    char **ecolor  = new char*[graph->size()];
    char **elabel  = new char*[graph->size()];
    int low;
    Iterator<Vertex*> verts(&graph->vertices());
    Iterator<Edge*> edgs(&graph->edges());
    Vertex* v;
    Edge* e;
    int i=0;
    while (verts(v)) {
		getAttribute((GraphObject*)v, "color", vcolor[i]); 
		getAttribute((GraphObject*)v, "label", vlabel[i]); 
		animateVertex(v,graph_view,"color","color",INIT_STEP,(char*)white);	
		i++;
    }
    Iterator<Vertex*> get_vert(&graph->vertices());
    Vertex *vv;
    while (get_vert(vv))
    	
   i=0;
    while (edgs(e)) {
	getAttribute((GraphObject*)e, "color", ecolor[i]); 
	getAttribute((GraphObject*)e, "label", elabel[i]); 
	i++;
    }
    ResetAttributes(graph);
    MSet<Edge*> edges = graph->edges();
    Iterator<Edge*> get_edge(&edges);
    while(get_edge(e))
	setAttribute((GraphObject*) e, "mark", 0);
    MSet<Vertex*> vertices = graph->vertices();
    Iterator<Vertex*> get_vertex(&vertices);
    
    while(get_vertex(v)) {
	setAttribute((GraphObject*) v, "mark", white_mark);
	setAttribute((GraphObject*) v, "pred", (Vertex*) 0);
    }
    int time = 0;
    get_vertex.reset();
	
    List<Vertex*> vertex_list;
    while(get_vertex(v))
    	vertex_list.append(v);
    if(compare) 
	vertex_list.sort(compare);
	
    while(vertex_list.size()) {
    	v = vertex_list.get();
    	int color, height=0;
    	getAttribute((GraphObject*) v, "mark", color);
    	if (color == white_mark) {
		result.append(v);
		DFSvisit(graph, v, result, time, low,
                     graph_view, height, dfs_tree++);
		setAttribute((GraphObject*) v, "low", low);
    	}
    }
    verts.reset();
    edgs.reset();
    i=0;
    while (verts(v)) {
	setAttribute((GraphObject*)v, "color", vcolor[i]); 
	setAttribute((GraphObject*)v, "label", vlabel[i]); 
	i++;
    }
    i=0;
    while (edgs(e)) {
	setAttribute((GraphObject*)e, "color", ecolor[i]); 
	setAttribute((GraphObject*)e, "label", elabel[i]); 
	i++;
    }
    delete [] vcolor;
    delete [] vlabel;
    delete [] ecolor;
    delete [] elabel;

    return result;
}
