package com.ibm.wala.ipa.callgraph.pruned;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;

import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.util.intset.BitVectorIntSet;
import com.ibm.wala.util.intset.IntSet;

public class PrunedCallGraph implements CallGraph {
	
	private CallGraph cg;
	private Set<CGNode> keep;
	
	public PrunedCallGraph(CallGraph cg, Set<CGNode> keep) {
		this.cg = cg;
		this.keep = keep;
	}

	public void removeNodeAndEdges(CGNode n) throws UnsupportedOperationException {
		cg.removeNodeAndEdges(n);
		keep.remove(n);
	}

	public Iterator<CGNode> iterator() {
		Iterator<CGNode> tmp = cg.iterator();
		Collection<CGNode> col = new LinkedList<CGNode>();
		while (tmp.hasNext()) {
			CGNode n = tmp.next();
			if (keep.contains(n)) {
				col.add(n);
			}
		}
		
		return col.iterator();
	}

	public int getNumberOfNodes() {
		return keep.size();
	}

	public void addNode(CGNode n) {
		cg.addNode(n);
		keep.add(n);
	}

	public void removeNode(CGNode n) throws UnsupportedOperationException {
		cg.removeNode(n);
		keep.remove(n);
	}

	public boolean containsNode(CGNode n) {
		return cg.containsNode(n) && keep.contains(n);
	}

	public Iterator<CGNode> getPredNodes(CGNode n) {
		Iterator<CGNode> tmp = cg.getPredNodes(n);
		Collection<CGNode> col = new LinkedList<CGNode>();
		while (tmp.hasNext()) {
			CGNode no = tmp.next();
			if (keep.contains(no)) {
				col.add(no);
			}
		}
		
		return col.iterator();
	}

	public int getPredNodeCount(CGNode n) {
		Iterator<CGNode> tmp = cg.getPredNodes(n);
		int cnt = 0;
		while (tmp.hasNext()) {
			CGNode no = tmp.next();
			if (keep.contains(no)) {
				cnt++;
			}
		}
		return cnt;
	}


	public Iterator<CGNode> getSuccNodes(CGNode n) {
		Iterator<CGNode> tmp = cg.getSuccNodes(n);
		Collection<CGNode> col = new LinkedList<CGNode>();
		while (tmp.hasNext()) {
			CGNode no = tmp.next();
			if (keep.contains(no)) {
				col.add(no);
			}
		}
		
		return col.iterator();
	}


	public int getSuccNodeCount(CGNode n) {
		Iterator<CGNode> tmp = cg.getSuccNodes(n);
		int cnt = 0;
		while (tmp.hasNext()) {
			CGNode no = tmp.next();
			if (keep.contains(no)) {
				cnt++;
			}
		}
		return cnt;
	}


	public void addEdge(CGNode src, CGNode dst) {
		if (keep.contains(src) && keep.contains(dst)){
			cg.addEdge(src, dst);
		}
	}


	public void removeEdge(CGNode src, CGNode dst) throws UnsupportedOperationException {
		cg.removeEdge(src, dst);
	}


	public void removeAllIncidentEdges(CGNode node) throws UnsupportedOperationException {
		cg.removeAllIncidentEdges(node);
	}


	public void removeIncomingEdges(CGNode node) throws UnsupportedOperationException {
		cg.removeIncomingEdges(node);
	}


	public void removeOutgoingEdges(CGNode node) throws UnsupportedOperationException {
		cg.removeOutgoingEdges(node);
	}


	public boolean hasEdge(CGNode src, CGNode dst) {
		return cg.hasEdge(src, dst) && keep.contains(src) &&  keep.contains(dst);
	}


	public int getNumber(CGNode N) {
		if (keep.contains(N)) {
			return cg.getNumber(N);
		} else {
			return -1;
		}
		
	}


	public CGNode getNode(int number) {
		if(keep.contains(cg.getNode(number))) {
			return cg.getNode(number);
		} else {
			return null;
		}
	}


	public int getMaxNumber() {
		return cg.getMaxNumber();
	}


	public Iterator<CGNode> iterateNodes(IntSet s) {
		Iterator<CGNode> tmp = cg.iterateNodes(s);
		Collection<CGNode> col = new LinkedList<CGNode>();
		while (tmp.hasNext()) {
			CGNode n = tmp.next();
			if (keep.contains(n)) {
				col.add(n);
			}
		}
		
		return col.iterator();
	}


	public IntSet getSuccNodeNumbers(CGNode node) {
		if (!keep.contains(node)){
			return null;
		}
		IntSet tmp = cg.getSuccNodeNumbers(node);
		BitVectorIntSet kp = new BitVectorIntSet();
		for (CGNode n : keep) {
			kp.add(getNumber(n));
		}
		return tmp.intersection(kp);
	}


	public IntSet getPredNodeNumbers(CGNode node) {
		if (!keep.contains(node)){
			return null;
		}
		if (!keep.contains(node)){
			return null;
		}
		IntSet tmp = cg.getPredNodeNumbers(node);
		BitVectorIntSet kp = new BitVectorIntSet();
		for (CGNode n : keep) {
			kp.add(getNumber(n));
		}
		return tmp.intersection(kp);
	}


	public CGNode getFakeRootNode() {
		if (keep.contains(cg.getFakeRootNode())) {
			return cg.getFakeRootNode();
		} else {
			return null;
		}
	}


	public Collection<CGNode> getEntrypointNodes() {
		Collection<CGNode> tmp = cg.getEntrypointNodes();
		Set<CGNode> ret = new HashSet<CGNode>();
		for (CGNode n : tmp) {
			if (keep.contains(n)) {
				ret.add(n);
			}
		}
		return ret;
	}


	public CGNode getNode(IMethod method, Context C) {
		if(keep.contains(cg.getNode(method, C))) {
			return cg.getNode(method, C);
		} else {
			return null;
		}
	}


	public Set<CGNode> getNodes(MethodReference m) {
		Set<CGNode> tmp = cg.getNodes(m);
		Set<CGNode> ret = new HashSet<CGNode>();
		for (CGNode n : tmp) {
			if (keep.contains(n)) {
				ret.add(n);
			}
		}
		return ret;
	}


	public IClassHierarchy getClassHierarchy() {
		return cg.getClassHierarchy();
	}


	public Set<CGNode> getPossibleTargets(CGNode node, CallSiteReference site) {
		if (!keep.contains(node)){
			return null;
		}
		Set<CGNode> tmp = cg.getPossibleTargets(node, site);
		Set<CGNode> ret = new HashSet<CGNode>();
		for (CGNode n : tmp) {
			if (keep.contains(n)) {
				ret.add(n);
			}
		}
		return ret;
	}


	public int getNumberOfTargets(CGNode node, CallSiteReference site) {
		if (!keep.contains(node)){
			return -1;
		}
		return getPossibleTargets(node, site).size();
	}


	public Iterator<CallSiteReference> getPossibleSites(CGNode src,	CGNode target) {
		if (!(keep.contains(src) && keep.contains(target))){
			return null;
		}
		return cg.getPossibleSites(src, target);
	}

}
