package antipatternsrecovery.metrics;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.MethodInvocation;

import antipatternsrecovery.parsingElements.FieldAccessVisitor;
import antipatternsrecovery.parsingElements.MethodInvocationsVisitor;
import antipatternsrecovery.parsingElements.Parser;

/*
 * The coupling between object classes (CBO) metric represents the number of classes coupled to a given class (efferent couplings, Ce). 
 * This coupling can occur through method calls, field accesses, inheritance, arguments, return types, and exceptions.
 */
public class CBO {
	IJavaProject javaProject;

	public int computeCBO() {
		return 0;	
	}
	
	public int computeSoftCBO(ICompilationUnit pClass) {
		try {
			return pClass.getImports().length;
		} catch (JavaModelException e) {
			e.printStackTrace();
		}
		return 0;
	}
	
	public int computeCBO(ICompilationUnit pClass) {
		this.javaProject=pClass.getJavaProject();
		int cbo=0;

		try {
			for(IPackageFragment pack: this.javaProject.getPackageFragments()){
				for(ICompilationUnit cu: pack.getCompilationUnits()) {
					if(cu.exists()) {
						cbo=cu.getImports().length;
						/*boolean isCoupled=this.checkCoupling(pClass, cu);

						if(isCoupled) 
							cbo++;*/
					}
				}
			}
		} catch (JavaModelException e) {
			e.printStackTrace();
		}

		return cbo;
	}

	private boolean checkCoupling(ICompilationUnit pClass, ICompilationUnit pCurrentClass) {
		IType type=pClass.getType(pClass.getElementName().substring(0, pClass.getElementName().length()-5));
		IType currentType=pCurrentClass.getType(pCurrentClass.getElementName().substring(0, pCurrentClass.getElementName().length()-5));

		if(currentType.exists()) {
			// Method calls
			MethodInvocationsVisitor methodInvocationVisitor=new MethodInvocationsVisitor();

			CompilationUnit unit = null;

			try {
				Parser parser = new Parser(pCurrentClass.getSource());
				unit = parser.createParser();

				unit.accept(methodInvocationVisitor);

				for(MethodInvocation methodInvocation: methodInvocationVisitor.getMethods()) {
					for(IMethod classMethod: type.getMethods()) {
						if(methodInvocation.toString().equals(classMethod.getElementName()))
							return true;
					}
				}

			} catch (JavaModelException e) {
				e.printStackTrace();
			}


			// Field accesses
			FieldAccessVisitor fieldAccessVisitor=new FieldAccessVisitor();

			unit.accept(fieldAccessVisitor);

			for(FieldAccess fieldAccess: fieldAccessVisitor.getFields()) {
				try {
					for(IField classField: type.getFields()) {
						if(fieldAccess.toString().equals(classField.getElementName()))
							return true;
					}
				} catch (JavaModelException e) {
					e.printStackTrace();
				}
			}

			// Inheritance
			try {
				if(currentType.getSuperclassName()!=null) {
					if(currentType.getSuperclassName().equals(type.getElementName())) {
						return true;
					}
				}
			} catch (JavaModelException e) {
				e.printStackTrace();
			}

			// Arguments
			try {
				for(IMethod method: currentType.getMethods()) {
					for(String param: method.getParameterTypes()) {
						if(param.equals("Q"+type.getElementName()+";")) 
							return true;
					}
				}
			} catch (JavaModelException e) {
				e.printStackTrace();
			}

			// Return types
			try {
				for(IMethod method: currentType.getMethods()) {
					method.getReturnType().equals(type.getElementName());
				}
			} catch (JavaModelException e) {
				e.printStackTrace();
			}
		}
		return false;

	}
}