/*
 *    Fernflower - The Analytical Java Decompiler
 *    http://www.reversed-java.com
 *
 *    (C) 2008 - 2010, Stiver
 *
 *    This software is NEITHER public domain NOR free software 
 *    as per GNU License. See license.txt for more details.
 *
 *    This software is distributed WITHOUT ANY WARRANTY; without 
 *    even the implied warranty of MERCHANTABILITY or FITNESS FOR 
 *    A PARTICULAR PURPOSE. 
 */

package de.fernflower.modules.decompiler;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

import de.fernflower.modules.decompiler.stats.Statement;
import de.fernflower.util.ListStack;

//  --------------------------------------------------------------------
//    Algorithm
//  -------------------------------------------------------------------- 
//  DFS(G)
//  {
//  make a new vertex x with edges x->v for all v
//  initialize a counter N to zero
//  initialize list L to empty
//  build directed tree T, initially a single vertex {x}
//  visit(x)
//  }
//
//  visit(p)
//  {
//  add p to L
//  dfsnum(p) = N
//  increment N
//  low(p) = dfsnum(p)
//  for each edge p->q
//      if q is not already in T
//      {
//      add p->q to T
//      visit(q)
//      low(p) = min(low(p), low(q))
//      } else low(p) = min(low(p), dfsnum(q))
//
//  if low(p)=dfsnum(p)
//  {
//      output "component:"
//      repeat
//      remove last element v from L
//      output v
//      remove v from G
//      until v=p
//  }
//  }	
//  -------------------------------------------------------------------- 

public class StrongConnectivityHelper {

	private ListStack<Statement> lstack;
	
	private int ncounter; 
	
	private HashSet<Statement> tset;
	private HashMap<Statement, Integer> dfsnummap;
	private HashMap<Statement, Integer> lowmap;
	
	private List<List<Statement>> components; 
	
	private HashSet<Statement> setProcessed; 

	// *****************************************************************************
	// constructors
	// *****************************************************************************

	public StrongConnectivityHelper() {}

	public StrongConnectivityHelper(Statement stat) {
		findComponents(stat);
	}
	
	// *****************************************************************************
	// public methods
	// *****************************************************************************
	
	public List<List<Statement>> findComponents(Statement stat) {
		
		components = new ArrayList<List<Statement>>(); 
		setProcessed = new HashSet<Statement>();
		
		visitTree(stat.getFirst());
		
		for(Statement st: stat.getStats()) {
			if(!setProcessed.contains(st) && st.getPredecessorEdges(Statement.STATEDGE_DIRECT_ALL).isEmpty()) {
				visitTree(st);
			}
		}
		
		// should not find any more nodes! FIXME: ??
		for(Statement st: stat.getStats()) {
			if(!setProcessed.contains(st)) {
				visitTree(st);
			}
		}
		
		return components;
	}

	public static boolean isExitComponent(List<Statement> lst) {
		
		HashSet<Statement> set = new HashSet<Statement>();
		for(Statement stat : lst) {
			set.addAll(stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD));
		}
		set.removeAll(lst);
		
		return (set.size() == 0);
	}
	
	public static List<Statement> getExitReps(List<List<Statement>> lst) {
		
		List<Statement> res = new ArrayList<Statement>();
		
		for(List<Statement> comp : lst) {
			if(isExitComponent(comp)) {
				res.add(comp.get(0));
			}
		}
		
		return res;
	}
	
	// *****************************************************************************
	// private methods
	// *****************************************************************************
	
	private void visitTree(Statement stat) {
		lstack = new ListStack<Statement>(); 
		ncounter = 0;
		tset = new HashSet<Statement>();
		dfsnummap = new HashMap<Statement, Integer>();
		lowmap = new HashMap<Statement, Integer>();

		visit(stat);
		
		setProcessed.addAll(tset);
		setProcessed.add(stat);
	}
	
	private void visit(Statement stat) {
		
		lstack.push(stat);
		dfsnummap.put(stat, ncounter);
		lowmap.put(stat, ncounter);
		ncounter++;

		List<Statement> lstSuccs = stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD); // TODO: set?
		lstSuccs.removeAll(setProcessed);
		
		for(int i=0;i<lstSuccs.size();i++) {
			Statement succ = lstSuccs.get(i);
			int secvalue; 
			
			if(tset.contains(succ)) {
				secvalue = dfsnummap.get(succ);
			} else {
				tset.add(succ);
				visit(succ);
				secvalue = lowmap.get(succ);
			}
			lowmap.put(stat, Math.min(lowmap.get(stat), secvalue));
		}

	
		if(lowmap.get(stat).intValue() == dfsnummap.get(stat).intValue()) {
			List<Statement> lst = new ArrayList<Statement>();
			Statement v;
			do {
				v = lstack.pop();
				lst.add(v);
			} while(v!=stat);
			components.add(lst);
		}
	}

	
	// *****************************************************************************
	// getter and setter methods
	// *****************************************************************************
	
	public List<List<Statement>> getComponents() {
		return components;
	}

	public void setComponents(List<List<Statement>> components) {
		this.components = components;
	}
	
}
