// Adding the step-by-step functionality to PostAnalayze.
// To do this:
// 	Change the wrapper file.
//***************************************************************************
//***************************************************************************
// Returns a sequence of vertices with attributes set for each vertex to 
//      greedy or non-greedy step.  Meaning that KLFM placed that vertex in
//      a way greedy would not have.
// Takes three parameters: 1) Graph*        - any graph to be analyzed.
// 			   2) Set<Vertex*>* - the first vertex of a component 
//					      to place in a greedy way.  All
//					      of the neighbors will also be 
//					      placed in a greedy way.
//			   3) int Stepping  - Used to allow PostAnalyze to 
//					      place that number of vertices,
//					      and and for PostAnalyze to be
// 					      able to pick up where left off
//					      at and continue placing.
//					      After vertex is placed, the 
//					      attribute is set and that vertex
//					      will nolonger be considered for
//					      placement until init_post(Graph*)
//					      is called. init_post(Graph*)
//					      must be called first to set
//					      all needed attributes.
//***************************************************************************
//***************************************************************************

#include <LINK/userobj/PostAnalyze.h>

//**************************************
//* This is declared globaly here because this function does not always need to get a
//* Set<Vertex*>, so you need a default.
//**************************************
Set<Vertex*> set_of_doom;


Sequence<Vertex*> 
PostAnalyze(Graph* g, Set<Vertex*> greedy_component, int stepping)
{
	Attribute<VertexInfoPtr> v_info(g, "v_info", (VertexInfoPtr) NULL);
	int placed = 0;
	int total_cuts = 0;
	int graph_order = g->order();
	int Grdy_placement = 0;
	int KL_placement = 0, num_unplaced, rank;
	VertexInfo* candidate = (VertexInfo*)NULL;

	Sequence<Vertex*>  vertex_order;
	VertexInfo *cells = new VertexInfo[graph_order]; // changed

	BucketArray* bucket_array = InitPostAnalyze(g, cells, stepping, 
						    num_unplaced);
	rank = graph_order - num_unplaced;
	int num_to_process;
	if (stepping) 	
		num_to_process = stepping;
	else
		num_to_process = graph_order;
	for (int i= 0; i < num_to_process; i++)
	{ 
		candidate = FindCandidate(g, bucket_array);
		vertex_order.append(candidate->v);
		if (candidate == 0)
		{
			static Sequence<Vertex*> seq;  
			return seq;
		}
		getAttribute((GraphObject*) candidate->v, "partition", 
					KL_placement);
		if (candidate->cost > 0) Grdy_placement = RIGHT;
			else Grdy_placement = LEFT; 
		if (candidate->cost == 0) Grdy_placement = KL_placement;
	
		if (KL_placement == Grdy_placement || 
			(i == 0 && num_unplaced == 0)) 
			setAttribute((GraphObject*)candidate->v, 
						"greediness", GREEDY);
		else {
			setAttribute((GraphObject*)candidate->v, 
						"greediness", NONGREEDY);
		}
		setAttribute((GraphObject*) candidate->v, "rank", rank++);
		setAttribute((GraphObject*) candidate->v, "placed", 1);

		if (greedy_component.memberQ(candidate->v) && 
                    candidate->cost != 0) {
			setAttribute((GraphObject*) candidate->v, 
				     "partition", Grdy_placement);
			setAttribute((GraphObject*) candidate->v, 
				     "greediness", GREEDY);
			Refine(bucket_array, candidate, Grdy_placement);
		} else
			Refine(bucket_array, candidate, KL_placement);
		total_cuts = total_cuts + count_cuts(candidate);
	}
	total_cuts = 0;
	delete bucket_array;
	delete cells;
	return vertex_order;
}




int
count_cuts(VertexInfo* vert_info)
{
	int left_bucket = 0;
	int right_bucket = 0;
	int picked_bucket = 0;
	int count = 0;
	int placement = 0;
	int v_placement = 0;
	int tmp_placed = 0;
	int v_cuts;
	Vertex* v = vert_info->v;
	getAttribute((GraphObject*)v, "partition", v_placement);
	Set<Vertex*> neighbors = vert_info->v->neighbors();
	
	Iterator<Vertex*> get_cuts(&neighbors);
	Vertex* v_tmp;

	while(get_cuts(v_tmp))
	{
		getAttribute((GraphObject*)v_tmp, "partition", placement);
		getAttribute((GraphObject*)v_tmp, "placed", tmp_placed);
		if (tmp_placed) 
			if (placement != v_placement) count++; 
	}

	return count;
}


BucketArray* 
InitPostAnalyze(Graph* g, VertexInfo* cells, int stepping, int &num_unplaced)
{
	
	BucketArray* bucket_array = (BucketArray*)NULL;
	initCells(g, cells);
	int num = 0;
	num_unplaced = 0;

	SortedArray<Vertex*> verts = g->vertices();
	Iterator<Vertex*> get_attrib(&verts);
	Vertex* v_tmp;
	while(get_attrib(v_tmp))  {
		getAttribute((GraphObject*)v_tmp, "placed", num);
		if (num == 0) num_unplaced++;
	}
	int placed;
	int tmp = 0;
	int i = 0;
	bucket_array = new BucketArray(2 * g->maxDegree() + 1);//changed

	int g_order = g->order();

	if (!stepping)  {
		for (i = 0; i < g_order; i++)
		{
			cells[i].cost = 0;
			setAttribute((GraphObject*)cells[i].v, "greediness", GREEDY);
			setAttribute((GraphObject*)cells[i].v, "placed", 0);
			cells[i].lp = bucket_array->insert(0, &cells[i]);
		}
	} else {
		SortedArray<Vertex*> vert_set = g->vertices();
		int vert_cost = 0;
		for (i = 0; i < g_order; i++)
		{
			getAttribute((GraphObject*)vert_set[i], "cost", 
					vert_cost);
			getAttribute((GraphObject*)cells[i].v, "placed", 
					placed);
			cells[i].cost = vert_cost;
			if (!placed) {
				cells[i].lp = 
				   bucket_array->insert(vert_cost, &cells[i]);
			}
			else cells[i].lock = 1;
		}
	}
	return bucket_array;
}



VertexInfo* 
FindCandidate(Graph* g, BucketArray* bucket_array)
{
	int count = 0, cost = 0, placed = 0;
	int maxDegree = 0;
	int next_degree = 0;
	DList<VertexInfo*> maxBucket;

	int max_cost = bucket_array->findMax();
	int min_cost = bucket_array->findMin();

	if (max_cost > abs(min_cost))
		cost = max_cost;
	else cost = min_cost;
	
	maxBucket = bucket_array->bucket(cost);

	int bucketSize = maxBucket.size();

	VertexInfo** Max_degreeV = new VertexInfo*[bucketSize];

	Iterator<VertexInfo*> get_max(&maxBucket);

	VertexInfo* vertex_info_ptr = (VertexInfo*)NULL;

	while(get_max(vertex_info_ptr))
	{
		next_degree = g->degree(vertex_info_ptr->v);
		if (next_degree >= maxDegree)	
		{
			if (next_degree > maxDegree)
				count = 0;
			Max_degreeV[count] = vertex_info_ptr;
			maxDegree = next_degree; 
			count++;
		} 

	}


	if (count > 0)
	{
		int randNum = Link_randomLong();
		randNum = randNum % count; 
		return Max_degreeV[randNum];
	}

delete Max_degreeV;
return 0;
}


void 
Refine(BucketArray* bucket_array, VertexInfo* candidate, int placement)
{
	VertexInfo* Ptr_to_VertexInfo = (VertexInfo*)NULL;
	ContainerNode* candidate_node = (ContainerNode*)NULL;

	int vertex_placement = 0;
	Set<Vertex*> neighbors = candidate->v->neighbors();


	bucket_array->extract(candidate->cost, candidate);
	candidate->lock = 1;

	Iterator<Vertex*> refining_vertices(&neighbors);
	Vertex* vertex;


	while(refining_vertices(vertex))
	{
		getAttribute((GraphObject*) vertex, "v_info",
					 Ptr_to_VertexInfo);


		if (Ptr_to_VertexInfo->lock != 1)
		{
			if (placement == LEFT)
				vertex_placement = Ptr_to_VertexInfo->cost + 1;
			
			else if (placement == RIGHT)
				vertex_placement = Ptr_to_VertexInfo->cost - 1;

			else cerr << "The placement is neither RIGHT or LEFT" << endl;;
			
			setAttribute((GraphObject*) vertex, "cost", 
					vertex_placement);

			candidate_node = bucket_array->remove(Ptr_to_VertexInfo);
			candidate_node = bucket_array->insert(vertex_placement,
								 candidate_node);
		}	
	}
}
