// Copyright (C) 1996 DIMACS Center, Rutgers, The State University of New Jersey
//               and Elon College, North Carolina 27244
// Author(s): Jonathan Berry
// This software is copyrighted by the DIMACS Center at Rutgers, The State
// University of New Jersey and Elon College, North Carolina.  
// IT IS PROVIDED AS IS, AND THE AUTHORS, DIMACS, AND
// RUTGERS, THE STATE UNIVERSITY OF NEW JERSEY  AND ELON COLLEGE 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-Feb-1998
// 
// 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-Feb-1998
// 
#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/basic/Stack.h>
#include <LINK/basic/Queue.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;

void AnimateBlock(Set<Edge*> bc_component, Queue<Vertex*> comp_verts,
                  char *graph_view)
{
    int width;
    Iterator<Edge*> get_edge(&bc_component);
    Edge *e;
    char *color = RandomTkColor(); // LEAK - need attribute deletion option
    while (get_edge(e)) {
		getAttribute((GraphObject*)e, "width", width);
		animateEdge(e, graph_view,"width", "width",
					      INIT_STEP, width*3, FLASH,0,0);
		animateEdge(e, graph_view,"color", "color",
					      INIT_STEP, color, FLASH, 0, 0);
    }
}

void PopIfComponent(Graph *g, char *graph_view, Vertex *parent, Vertex *child, 
		  Edge *parent_child,
		  Stack<Vertex*>& comp_verts, 
		  Stack<Edge*>& comp_edges, 
	          Set<Set<Edge*> >& bcc)
{
    Queue<Vertex*> block_verts;
    int parent_dfs_num, child_low;
    getAttribute((GraphObject*)parent, "starttime", parent_dfs_num);
    getAttribute((GraphObject*)child, "low", child_low);
    Set<Edge*> bc_component;
    if (child_low >= parent_dfs_num)  { // v is an articulation point
	if (!parent_child)
		return;
	bc_component.insert(parent_child);
	Edge *popped;
	while ((popped=comp_edges.pop())) {
		cout << "popped->name: " << popped->name() << endl;
		cout << "parent_child->name: " << parent_child->name() << endl;
	    	if (popped->name() == parent_child->name()) 
			break;
		bc_component.insert(popped);
	}
	bcc.insert(bc_component);
	Vertex *poppedv;
	while ((poppedv=comp_verts.pop()) != parent) 
		block_verts.enqueue(poppedv);
	comp_verts.push(parent);
	block_verts.enqueue(parent);
	if (!graph_view)
		return;
    } else
	return;
    AnimateBlock(bc_component, block_verts, graph_view);
}
//
// visit all the neighbors of the vertex 
//
static void
DFSvisit(Graph* graph, Vertex* v, 
	 int& time, 
	 int &low,   // min {back(v), low(c: c is a child of v)}
         int &dfs_children,
	 char* graph_view, int height, int dfs_tree,
	 Stack<Vertex*>& comp_verts,
	 Stack<Edge*>& comp_edges,
	 Set<Set<Edge*> >& bcc)
{
    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",
					      ALG_STEP, (char *)"gray",
					      JIGGLE, 5, 10);
    if (graph_view) {
    	ostrstream oss;
    	oss << time << "/" << ends;
    	label = oss.str();
    	animateVertex(v, graph_view, 
		      "label", "vertex-label", ALG_STEP, 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) { 
            comp_edges.push(incEdge);
	    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)) {
		comp_verts.push(adjVertex);
		int child_low;
	        int vmark;
	        getAttribute((GraphObject*) adjVertex, "mark", vmark);
                switch (vmark) {
		case white_mark: // tree edge
                    dfs_children++;
		    setAttribute((GraphObject*)adjVertex, "pred", v);
		    edgeColor = "red";
		    edgeType = "tree";
		    animateEdge(incEdge, graph_view, "color", 
				"color", INIT_STEP, edgeColor, FLASH, 1, 1);
                    int dfs_children;
		    DFSvisit(graph, adjVertex, time, child_low, 
			     dfs_children, graph_view, ++height, dfs_tree,
			     comp_verts, comp_edges, bcc);
		    setAttribute((GraphObject*) incEdge, 
				 "type", TREE_EDGE_TYPE);
		    if (child_low < lowest_of_childrens_lows)
			lowest_of_childrens_lows = child_low;
		    cout << "BCC: about to pop, visiting " << *v
                         << " and processing its neighbor "<< *adjVertex <<endl;
		    cout << "vertex stack: " << comp_verts << endl;
		    cout << "edge stack: " << comp_edges << endl;
		    PopIfComponent(graph, graph_view,
                         v, adjVertex, incEdge, comp_verts, comp_edges, bcc);
		    break;
		    
		case gray_mark: // back edge
		    int neigh_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;
		    
	        }
            }
	    if (strcmp(edgeType, "tree")!=0) {
            	animateEdge(incEdge, graph_view, "color", "color", 
			    INIT_STEP, edgeColor, FLASH, 1, 1);
	    }
	}
    }
    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++;
    
    if (graph_view) {
	ostrstream oss;
    	oss << label << low << ends;
	delete[] label;
    	label = oss.str();
    	animateVertex(v, graph_view, 
		      "label","vertex-label", ALG_STEP, label);
	delete[] label;
    }
}
//
// depth first search returns an ordered sequence of vertices
//
Set<Set<Edge*> >
BiconnectedComponents(Graph* graph, char *graph_view)
{
    Set<Set<Edge*> >   result;
    if (!graph)
	return result;
    Stack<Edge*> comp_edges;
    Stack<Vertex*> comp_verts;
    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]); 
	i++;
    }
    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);
	
    while(vertex_list.size()) {
    	v = vertex_list.get();
	comp_verts.push(v);
    	int color, height=0, dfs_children=0;
    	getAttribute((GraphObject*) v, "mark", color);
    	if (color == white_mark) {
		DFSvisit(graph, v, time, low, dfs_children,
                     graph_view, height, dfs_tree++, comp_verts,
		     comp_edges, result);
		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;
}
