package org.bdware.analysis;

import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;

import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;

import org.bdware.analysis.BasicBlock;
import org.bdware.analysis.CFGraph;
import org.bdware.analysis.InsnPrinter;
import org.bdware.analysis.OpInfo;
import org.bdware.analysis.taint.TaintBB;
import org.bdware.analysis.taint.TaintCFG;

public class FrontCF {
	String methodName;
	public List<FrontBB> blocks;
	public List<FrontEdge> edges;
	public String ret;
	public String finalRet;
	transient InsnPrinter printer;
	transient ArrayPs ps;

	static class ArrayPs extends PrintStream {
		List<String> content;

		public ArrayPs() throws FileNotFoundException {
			super("/dev/null");
		}

		public void println(String str) {
			if (content != null)
				content.add(str);
		}
	}

	public FrontCF(CFGraph graph) {
		blocks = new ArrayList<>();
		edges = new ArrayList<>();
		try {
			ps = new ArrayPs();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		printer = new InsnPrinter(Opcodes.ASM4, ps);
		printer.setLabelOrder(graph.getLabelOrder());
	}

	public static class FrontBB {
		String type;
		String name;  //BlockNumber
		List<String> stmts; //BlockInstructions
		String original;
		String result; //preResult
		String blockDep;
	}

	public static class EdgeLabel {
		String label;
	}

	public static class FrontEdge {
		String from, to;
		EdgeLabel label;
	}

	public void addBB(BasicBlock bb, String original, List<Integer> ids, TaintCFG cfg) {
		FrontBB fb = new FrontBB();
		blocks.add(fb);
		fb.name = "B" + bb.blockID;
		List<AbstractInsnNode> insnList = bb.getInsn();
		fb.type = "Continuous";
		fb.original = original;
		
		//added
		TaintBB b = (TaintBB) bb;
		//if(b.preResult != null)
			//b.preResult.printResult();
		fb.result = b.getResult();
		if(ids == null)
			fb.blockDep = "";
		else{
			StringBuilder sb = new StringBuilder();
			//sb.append("Dependence: ");
			for(Integer id : ids) {			
				//sb.append("B"+ id +" ");	
				TaintBB tb = (TaintBB) cfg.getBasicBlockAt(id);
				sb.append(tb.preResult.frame.getStack(0).toReadableTaint());
			}
			fb.blockDep = sb.toString();		
		}
		if (insnList != null && insnList.size() > 0) {
			AbstractInsnNode lastStmt = insnList.get(insnList.size() - 1);
			if (lastStmt.getOpcode() >= 0) {
				OpInfo info = OpInfo.ops[lastStmt.getOpcode()];
				if (info.canBranch() || info.canSwitch()) {
					fb.type = "Branch";
				}
			}
			fb.stmts = new ArrayList<String>();
			ps.content = fb.stmts;
			for (AbstractInsnNode node : insnList) {
				node.accept(printer);
			}
		}
	}

	public void addEdge(BasicBlock bb, BasicBlock suc) {
		FrontEdge edge = new FrontEdge();
		List<AbstractInsnNode> insnList = bb.getInsn();
		edge.label = new EdgeLabel();
		edge.label.label = "e";
		AbstractInsnNode lastStmt = insnList.get(insnList.size() - 1);
		boolean ignore = false;
		if (lastStmt.getOpcode() >= 0) {
			OpInfo info = OpInfo.ops[lastStmt.getOpcode()];
			if (info.canBranch() && info.toString().startsWith("if")) {
				if (suc.blockID == bb.blockID + 1)
					edge.label.label = "false";
				else
					edge.label.label = "true";
			}
			if (info.canThrow() && info.toString().startsWith("invoke")) {
				if (suc.blockID != bb.blockID + 1) {
					ignore = true;
				}
			}
		}
		edge.from = "B" + bb.blockID;
		edge.to = "B" + suc.blockID;
		if (!ignore)
			edges.add(edge);

	}
}
