package org.bdware.analysis.taint;

import org.bdware.analysis.AnalysisResult;
import org.bdware.analysis.InsnPrinter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.analysis.AnalyzerException;
import org.objectweb.asm.tree.analysis.Frame;

public class TaintResult extends AnalysisResult {
	// public HeapObject heapObejct;
	public Frame<TaintValue> frame;
	public TaintValue ret;
	public static int nLocals = 0;
	public static int nStack = 0;
	public static InsnPrinter printer = new InsnPrinter(Opcodes.ASM4, System.out);
	public static TaintInterpreter interpreter = new TaintInterpreter(Opcodes.ASM4);

	public TaintResult() {
		frame = new Frame<>(nLocals, nStack);
		ret = new TaintValue(1);
		ret.isTainted = 0L;
	}

	@Override
	public AnalysisResult merge(AbstractInsnNode insn) {
		interpreter.setCurrentResult(this);
		try {
			if (TaintConfig.isDebug && insn.getOpcode() >= 0) {
				System.out.println("[TaintResult] frameStatus:" + frame2Str());
				insn.accept(printer);
			}
			if (insn.getOpcode() < 0) {
				// System.out.println("[TaintResult] MetLabel:" +
				// insn.getClass().getCanonicalName());
			} else
				frame.execute(insn, interpreter);
			if (TaintConfig.isDebug && insn.getOpcode() >= 0) {
				System.out.println("[TaintResult] frameStatus:" + frame2Str());

			}

		} catch (AnalyzerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// we use suc instead,so we don't create new object here.
		return this;
	}
	

	@Override
	public void printResult() {
		System.out.println("====" + frame2Str() + " Ret:" + ret.toString() + "====");
	}

	public String frame2Str() {
		StringBuilder sb = new StringBuilder();
		sb.append("Local:");
		for (int i = 0; i < frame.getLocals(); i++) { // getLocals����local�����size
			TaintValue t = frame.getLocal(i);
			if (t != null)
				sb.append(t.toString());
			else
				sb.append("0--");
		}
		sb.append(" Stack:");
		for (int i = 0; i < frame.getStackSize(); i++) { // getStackSize����stack�ĵ�ǰsize
			TaintValue t = frame.getStack(i);
			if (t != null)
				sb.append(t.toString());
			else
				sb.append("0--");
		}
		return sb.toString();
	}

	private static boolean cover(TaintValue t1, TaintValue t2) {
		// if (t2 == null || t2.isTainted == 0)
		// return true;
		// TODO whether is cover?
		if (t1 != null && t1.isTainted != 0) {
			if (t2 == null || t1.isTainted != t2.isTainted)
				return true;
		}
		return false;
	}

	/*
	 * private static boolean isTainted(TaintValue t1) { if (t1 != null &&
	 * t1.isTainted) { return true; } return false; }
	 */
	private static boolean isTainted(TaintValue t1) {
		if (t1 != null && t1.isTainted != 0) {
			return true;
		}
		return false;
	}

	@Override
	public boolean covers(AnalysisResult result) {
		TaintResult tr = (TaintResult) result;
		boolean ret = coversInternal(tr);
		return ret;
		// System.out.println("[NaiveTaintResult] Cover:" + ret);
		// System.out.println("[NaiveTaintResult] " + frame.toString());
		// System.out.println("[NaiveTaintResult] " + tr.frame.toString());	
	}

	// preResult cover sucResult means
	// preResult's taint is large or equals sucResult.
	public boolean coversInternal(TaintResult tr) {
		for (int i = 0; i < frame.getLocals() && i < tr.frame.getLocals(); i++)
			if (cover(tr.frame.getLocal(i), frame.getLocal(i)))
				return false;
		// TODO why locals is not equal??
		for (int i = frame.getLocals(); i < tr.frame.getLocals(); i++)
			if (isTainted(tr.frame.getLocal(i)))
				return false;
		for (int i = 0; i < frame.getStackSize() && i < tr.frame.getStackSize(); i++)
			if (cover(tr.frame.getStack(i), frame.getStack(i)))
				return false;
		for (int i = frame.getStackSize(); i < tr.frame.getStackSize(); i++)
			if (isTainted(tr.frame.getStack(i)))
				return false;
		return true;
	}

	@Override
	public void mergeResult(AnalysisResult r) {
		TaintResult from = (TaintResult) r;
		for (int i = 0; i < from.frame.getLocals(); i++) {
			TaintValue target = from.frame.getLocal(i);
			if (frame.getLocals() > i) {
				TaintValue t1 = frame.getLocal(i);
				if (target != null) {
					if (t1 == null) {
						t1 = target.clone();
						frame.setLocal(i, t1);
					} else {
						t1.merge(target);
					}
				}
			} else {
				frame.setLocal(i, target.clone());
			}
		}

		for (int i = 0; i < from.frame.getStackSize(); i++) {
			TaintValue target = from.frame.getStack(i);
			if (frame.getStackSize() > i) {
				TaintValue t1 = frame.getStack(i);
				if (target != null) {
					if (t1 == null) {
						t1 = target.clone();
						frame.setStack(i, t1);
					} else {
						t1.merge(target);
					}
				}

				t1.merge(from.frame.getStack(i));
			} else {
				if (target != null)
					frame.push(target.clone());
				else
					frame.push(new TaintValue(1,0));
			}
		}
		ret.merge(from.ret); 
	}

	@Override
	public AnalysisResult clone() {
		TaintResult ret = new TaintResult();
		ret.frame = new Frame<>(frame);
		for (int i = 0; i < ret.frame.getLocals(); i++) {
			TaintValue t = ret.frame.getLocal(i);
			if (t != null)
				ret.frame.setLocal(i, t.clone());
		}
		for (int i = 0; i < ret.frame.getStackSize(); i++) {
			TaintValue t = ret.frame.getStack(i);
			if (t != null)
				ret.frame.setStack(i, t.clone());
		}
		ret.ret = this.ret.clone();
		return ret;
	}
}
