package soot.poc.livevariables;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import soot.Unit;
import soot.Value;
import soot.ValueBox;
import soot.jimple.FieldRef;
import soot.poc.flowsets.ValueArraySparseSet;
import soot.toolkits.graph.DirectedGraph;
import soot.toolkits.scalar.ArraySparseSet;
import soot.toolkits.scalar.BackwardFlowAnalysis;
import soot.toolkits.scalar.FlowSet;

public class SimpleLiveVariables implements LiveVariables
{

	private HashMap<Unit,List<Value>> unitToVariablesAfter;
	private HashMap<Unit,List<Value>> unitToVariablesBefore;

	public SimpleLiveVariables(DirectedGraph<Unit> graph)
	{
		SimpleLiveVariablesAnalysis analysis = new SimpleLiveVariablesAnalysis(graph);

		this.unitToVariablesAfter = new HashMap<Unit, List<Value>>();
		this.unitToVariablesBefore = new HashMap<Unit, List<Value>>();
		
		for (Unit unit: graph)
		{
			FlowSet set = (FlowSet) analysis.getFlowBefore(unit);
			this.unitToVariablesBefore.put(unit,
					Collections.unmodifiableList(set.toList()));

			set = (FlowSet) analysis.getFlowAfter(unit);
			this.unitToVariablesAfter.put(unit,
					Collections.unmodifiableList(set.toList()));
		}
	}

	public List<Value> getLiveVariablesAfter(Unit _unit) {
		return this.unitToVariablesAfter.get(_unit);
	}


	public List<Value> getLiveVariablesBefore(Unit _unit) {
		return this.unitToVariablesBefore.get(_unit);
	}

}

class SimpleLiveVariablesAnalysis extends BackwardFlowAnalysis<Unit, FlowSet>
{
	private ValueArraySparseSet emptySet;

	public SimpleLiveVariablesAnalysis(DirectedGraph<Unit> _graph) {
		super(_graph);

		this.emptySet = new ValueArraySparseSet();

		super.doAnalysis();
	}

	@Override
	protected void copy(FlowSet _source, FlowSet _dest) {
		_source.copy(_dest);
	}

	@Override
	protected void merge(FlowSet _source1, FlowSet _source2, FlowSet _dest) {
		_source1.union(_source2, _dest);
	}

	@Override
	protected FlowSet entryInitialFlow() {
		return this.emptySet.clone();
	}

	@Override
	protected FlowSet newInitialFlow() {
		return this.emptySet.clone();
	}
	
	@Override
	protected void flowThrough(FlowSet _source, Unit _unit, FlowSet _dest) {
//		System.out.println("[flowThrough] " + _unit.toString());
//		System.out.println("[flowThrough] in -> " + _source);
//		System.out.println("[flowThrough] out -> " + _dest);
		kill(_source, _unit, _dest);
		gen(_dest, _unit);
//		System.out.println("[flowThrough] in -> " + _source);
//		System.out.println("[flowThrough] out -> " + _dest);
//		System.out.println("[flowThrough] ---------- ");
	}
	
	private void kill(FlowSet _source, Unit _unit, FlowSet _dest)
	{
		FlowSet kills = this.emptySet.clone();
		
		for (ValueBox valueBox: _unit.getDefBoxes()) {
			Value value = valueBox.getValue();
		
//			if (value instanceof Local) {
			if (value instanceof FieldRef) {
				kills.add(value);
			}
		}
//		System.out.println("[kills] " + _unit.toString());
//		System.out.println("[kills] " + kills);
		
		_source.difference(kills, _dest);
	}
	
	private void gen(FlowSet _dest, Unit _unit)
	{
		for (ValueBox valueBox: _unit.getUseBoxes()) {
			Value value = valueBox.getValue();

//			System.out.println("[gen] " + _unit.toString());
//			System.out.print("[gen] ");
//			if (value instanceof Local) {
			if (value instanceof FieldRef) {
				_dest.add(value);
//				System.out.print(value.toString() + " ");
			}
//			System.out.println();
		}	
	}
}
