package org.ntlab.trace;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

public class ThreadInstance {
	private ArrayList<MethodExecution> roots = new ArrayList<MethodExecution>();
	private MethodExecution curMethodExecution = null;
	private String id;
	
	public ThreadInstance(String id) {
		this.id = id;
	}
	
	public void addRoot(MethodExecution root) {
		this.roots.add(root);
		curMethodExecution = root;
	}
	
	public ArrayList<MethodExecution> getRoot() {
		return roots;
	}

	public String getId() {
		return id;
	}
	
	public void callMethod(String signature, String callerSideSignature, String receiverClassName, String receiverObjId, 
			boolean isConstractor, boolean isStatic, String lineNo, long timeStamp) {
		MethodExecution newMethodExecution = new MethodExecution(signature, callerSideSignature, receiverClassName, receiverObjId, isConstractor, isStatic, timeStamp);
		if (curMethodExecution != null) {
			curMethodExecution.addStatement(new MethodInvocation(newMethodExecution, curMethodExecution.getThisClassName(), curMethodExecution.getThisObjId(), lineNo, id));
			newMethodExecution.setCaller(curMethodExecution, curMethodExecution.getStatements().size() - 1);
			curMethodExecution = newMethodExecution;
		} else {
			addRoot(newMethodExecution);
		}
	}
	
	public void setArgments(ArrayList<ObjectReference> arguments) {
		curMethodExecution.setArguments(arguments);
	}
	
	public void returnMethod(ObjectReference returnValue, String thisObjId, boolean isCollectionType) {
		if (curMethodExecution == null) return; 
		curMethodExecution.setReturnValue(returnValue);
		if (curMethodExecution.getThisObjId().equals("0")) {
			curMethodExecution.setThisObjeId(thisObjId);
		}
		curMethodExecution.setCollectionType(isCollectionType);
		curMethodExecution = curMethodExecution.getParent();
	}
	
	public void terminateMethod() {
		if (curMethodExecution == null) return; 
		curMethodExecution.setTerminated(true);
		curMethodExecution = curMethodExecution.getParent();		
	}

	public void fieldAccess(String valueClassName, String valueObjId, String containerClassName, String containerObjId, String thisClassName, String thisId, String lineNo, String threadNo) {
		FieldAccess fieldAccess = new FieldAccess(valueClassName, valueObjId, containerClassName, containerObjId, thisClassName, thisId, lineNo, threadNo);
		if (curMethodExecution != null) curMethodExecution.addStatement(fieldAccess);
	}

	public void fieldUpdate(String valueClassName, String valueObjId, String containerClassName, String containerObjId, String lineNo, String threadNo) {
		FieldUpdate fieldUpdate = new FieldUpdate(valueClassName, valueObjId, containerClassName, containerObjId, lineNo, threadNo);
		if (curMethodExecution != null) curMethodExecution.addStatement(fieldUpdate);
	}
	
	public void traverseMethodExecutionsBackward(IMethodExecutionVisitor visitor) {
		visitor.preVisitThread(this);
		for (int i = 0; i < roots.size(); i++) {
			MethodExecution root = roots.get(i);
			root.traverseMethodExecutionsBackward(visitor);
		}
		visitor.postVisitThread(this);				
	}

	public void traverseMarkedMethodExecutions(IMethodExecutionVisitor visitor, long markStart, long markEnd) {
		visitor.preVisitThread(this);
		for (int i = 0; i < roots.size(); i++) {
			MethodExecution root = roots.get(i);
			if (root.getTimeStamp() <= markEnd) {
				root.traverseMarkedMethodExecutions(visitor, markStart, markEnd);
			} else {
				break;
			}
		}
		visitor.postVisitThread(this);				
	}

	public void getUnmarkedMethodSignatures(HashSet<String> signatures, long markStart, long markEnd) {
		for (int i = 0; i < roots.size(); i++) {
			MethodExecution root = roots.get(i);
			root.getUnmarkedMethodSignatures(signatures, markStart, markEnd);
		}
	}
	
	public MethodExecution getCuurentMethodExecution() {
		return curMethodExecution;
	}
}
