package antipatternsrecovery.antipatterns;

import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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.JavaModelException;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.TypeDeclaration;

import antipatternsrecovery.beans.BeanConverter;
import antipatternsrecovery.beans.ClassBean;
import antipatternsrecovery.parsingElements.ClassParser;
import antipatternsrecovery.parsingElements.MethodInvocationsVisitor;
import antipatternsrecovery.parsingElements.Parser;

public class MiddleMan {
	private IJavaProject projectToAnalyze;
	private ClassParser parser;
	private Vector<ClassBean> candidateMiddleMen;
	
	public MiddleMan(IJavaProject pProject){
		this.setProjectToAnalyze(pProject);
		this.parser=new ClassParser();
	}
	
	public Vector<ClassBean> searchMiddleMen() {
		candidateMiddleMen=new Vector<ClassBean>();
		Vector<IType> types=new Vector<IType>();
		
		try {
			for(IPackageFragment p: this.projectToAnalyze.getPackageFragments()) {
				for(ICompilationUnit cu: p.getCompilationUnits()){
					if(cu.getElementName().contains(".java")) {
						IType type=cu.getType(cu.getElementName().substring(0, cu.getElementName().length()-5));
						if(! type.isInterface())
							types.add(type);
					}
				}
			}
		} catch (JavaModelException e) {
			return candidateMiddleMen;
		}

		for(IType t: types) {
			double counterDelegation=0.0;
			try {
				for(IMethod m: t.getMethods()){
					if(this.isDelegation(m)) counterDelegation++;
				}
			} catch (JavaModelException e) {
				return candidateMiddleMen;
			}
			
			try {
				double percentage=counterDelegation/t.getMethods().length;
				if(percentage>0.7) candidateMiddleMen.add(BeanConverter.softCastCUToClassBean(t.getCompilationUnit()));
			} catch (JavaModelException e) {
				return candidateMiddleMen;
			}
		}
	
		
		return candidateMiddleMen;
	}
	
	public IJavaProject getProjectToAnalyze() {
		return this.projectToAnalyze;
	}

	public void setProjectToAnalyze(IJavaProject pProjectToAnalyze) {
		this.projectToAnalyze = pProjectToAnalyze;
	}

	public ClassParser getParser() {
		return this.parser;
	}

	public void setParser(ClassParser parser) {
		this.parser = parser;
	}
	
	private boolean isDelegation(IMethod pMethodToAnalyze) {
		Parser parser = new Parser();
		TypeDeclaration block;
		MethodInvocationsVisitor methodInvocationVisitor = new MethodInvocationsVisitor();

		if(this.LOC(pMethodToAnalyze)<10) {
			try {
				block = parser.createParser(pMethodToAnalyze.getSource(), ASTParser.K_CLASS_BODY_DECLARATIONS);
				block.accept(methodInvocationVisitor);
			} catch (Exception e) {
				return false;
			}
			if (methodInvocationVisitor.getMethods().size() == 1) return true;
		}
		return false;
	}
	
	private int LOC(IMethod pMethod){
		int loc=0;
		try {
			String source=pMethod.getSource();
			String regex="[\n]";
			Pattern pattern=Pattern.compile(regex);
			Matcher matcher = pattern.matcher(source);
			while (matcher.find()) 
			      loc++;		    
		} catch (JavaModelException e) {
			e.printStackTrace();
		}
		return loc+1;
	}
	
	public String toString() { 
		String toReturn="";
		for(ClassBean classBean: this.candidateMiddleMen) {
			if(classBean.getBelongingPackage()!=null)
				toReturn+=classBean.getName()+"; " +classBean.getBelongingPackage().getName() +";\n";
			else toReturn+=classBean.getName() +";\n";
		}
		return toReturn;
	}
}