package codemining.js.codedata.metrics;

import java.io.File;
import java.io.IOException;
import java.util.logging.Logger;

import org.eclipse.wst.jsdt.core.dom.ASTNode;
import org.eclipse.wst.jsdt.core.dom.ASTVisitor;
import org.eclipse.wst.jsdt.core.dom.CatchClause;
import org.eclipse.wst.jsdt.core.dom.ConditionalExpression;
import org.eclipse.wst.jsdt.core.dom.DoStatement;
import org.eclipse.wst.jsdt.core.dom.EnhancedForStatement;
import org.eclipse.wst.jsdt.core.dom.ForStatement;
import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration;
import org.eclipse.wst.jsdt.core.dom.IfStatement;
import org.eclipse.wst.jsdt.core.dom.SwitchCase;
import org.eclipse.wst.jsdt.core.dom.WhileStatement;

import codemining.js.codeutils.JavascriptASTExtractor;

/**
 * Compute McCabe's Cyclomatic Complexity.
 * 
 * @author Miltos Allamanis
 * 
 */
public class JavascriptCyclomaticCalculator implements
		IJavascriptFileMetricRetriever {

	/**
	 * Visit all "junctions" in an AST and increment complexity.
	 * 
	 */
	private static class JunctionVisitor extends ASTVisitor {
		int complexity = 0;

		@Override
		public boolean visit(final CatchClause arg0) {
			complexity++;
			return super.visit(arg0);
		}

		@Override
		public boolean visit(final ConditionalExpression arg0) {
			complexity++;
			return super.visit(arg0);
		}

		@Override
		public boolean visit(final DoStatement arg0) {
			complexity++;
			return super.visit(arg0);
		}

		@Override
		public boolean visit(final EnhancedForStatement arg0) {
			complexity++;
			return super.visit(arg0);
		}

		@Override
		public boolean visit(final ForStatement arg0) {
			complexity++;
			return super.visit(arg0);
		}

		@Override
		public boolean visit(final IfStatement arg0) {
			complexity++;
			return super.visit(arg0);
		}

		@Override
		public boolean visit(final FunctionDeclaration arg0) {
			/*
			 * if (isConcrete(arg0)) { complexity.startMethod(); return
			 * super.visit(arg0); } return false;
			 */
			complexity++; // TODO: Not exactly true, but we'll use that
			return super.visit(arg0);
		}

		@Override
		public boolean visit(final SwitchCase arg0) {
			complexity++;
			return super.visit(arg0);
		}

		@Override
		public boolean visit(final WhileStatement arg0) {
			complexity++;
			return super.visit(arg0);
		}
	}

	private static final Logger LOGGER = Logger
			.getLogger(JavascriptCyclomaticCalculator.class.getName());

	public int getComplexity(final File file) throws IOException {
		final JavascriptASTExtractor ast = new JavascriptASTExtractor(false);
		final JunctionVisitor visitor = new JunctionVisitor();
		ast.getAST(file).accept(visitor);
		return visitor.complexity;
	}

	@Override
	public double getMetricForASTNode(final ASTNode node) {
		final JunctionVisitor visitor = new JunctionVisitor();
		node.accept(visitor);
		return visitor.complexity;
	}

	@Override
	public double getMetricForFile(final File file) throws IOException {
		return getComplexity(file);
	}
}