/**
 * Copyright (C) . - All Rights Reserved.
 * Unauthorized copying of this file via any medium is
 * strictly prohibited Proprietary and Confidential.
 * Written by .
 */

package pda.core.trace.inst.visitor;

import org.eclipse.jdt.core.dom.*;
import pda.common.conf.Constant;
import pda.common.utils.Identifier;
import pda.core.trace.inst.gen.GenStatement;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author
 *
 */
public class TraceTestMethodInstrumentVisitor extends TraversalVisitor {

	private final static String __name__ = "@NewTestMethodInstrumentVisitor ";

	private Set<Integer> _failedTest;

	public TraceTestMethodInstrumentVisitor(Set<Integer> failedMethods) {
		_failedTest = failedMethods;
		_methodFlag = Constant.INSTRUMENT_K_TEST;
	}

	@Override
	public boolean visit(MethodDeclaration node) {

		String name = node.getName().getFullyQualifiedName();

		if ((name.equals("setUp") || name.equals("countTestCases") || name.equals("createResult") || name.equals("run")
				|| name.equals("runBare") || name.equals("runTest") || name.equals("tearDown")
				|| name.equals("toString") || name.equals("getName") || name.equals("setName"))) {
			return true;
		}

		// filter those functional methods in test class path, test method name
		// starting with "test" in Junit 3 while with annotation as "@Test" in
		// Junit 4,
		// TODO should be optimized since the "contain" method is time consuming
		boolean hasAnnotation = false;
		for (Object object : node.modifiers()) {
			if (object instanceof MarkerAnnotation) {
				if (object.toString().equals("@Test")) {
					hasAnnotation = true;
					break;
				}
			}
		}

		// bug fix: does not instrument test cases with parameters 2018-3-1
		if ((!name.startsWith("test") && !hasAnnotation) || node.parameters().size() > 0) {
			return true;
		}

		String message = buildMethodInfoString(node);
		if (message == null) {
			return true;
		}

		int keyValue = Identifier.getIdentifier(message);

		boolean succTest = true;

		if (_failedTest.contains(keyValue)) {
			succTest = false;
		}

		if (node.getBody() != null) {
			Block body = node.getBody();
			List<ASTNode> backupStatement = new ArrayList<>();
			AST ast = AST.newAST(Constant.AST_LEVEL);

			ASTNode thisOrSuperStatement = null;
			if (body.statements().size() > 0) {
				ASTNode astNode = (ASTNode) body.statements().get(0);
				int startIndex = 0;
				if (astNode instanceof SuperConstructorInvocation
						|| (astNode instanceof ConstructorInvocation && astNode.toString().startsWith("this"))) {
					thisOrSuperStatement = ASTNode.copySubtree(ast, astNode);
					startIndex = 1;
				}
				for (; startIndex < body.statements().size(); startIndex++) {
					ASTNode statement = (ASTNode) body.statements().get(startIndex);
					backupStatement.add(ASTNode.copySubtree(ast, statement));
				}
			}

			body.statements().clear();
			Statement insert = GenStatement.genInstrumentStatementForTestWithResetAndId(keyValue, succTest);
			Block tryBody = ast.newBlock();
			if (thisOrSuperStatement != null) {
				tryBody.statements().add(ASTNode.copySubtree(tryBody.getAST(), thisOrSuperStatement));
			}
			tryBody.statements().add(ASTNode.copySubtree(tryBody.getAST(), insert));
			for (ASTNode statement : backupStatement) {
				tryBody.statements().add(ASTNode.copySubtree(tryBody.getAST(), statement));
			}
			TryStatement tryStatement = ast.newTryStatement();
			tryStatement.setBody(tryBody);
			Statement dumpStatement = GenStatement.genInstrumentStatementForResultDumpAndTrace();
			Block dumpBlock = ast.newBlock();
			dumpBlock.statements().add(ASTNode.copySubtree(dumpBlock.getAST(), dumpStatement));
			tryStatement.setFinally(dumpBlock);
			body.statements().add(ASTNode.copySubtree(body.getAST(), tryStatement));
		}

		return true;
	}

}
