package antipatternsrecovery.antipatterns;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map.Entry;
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 antipatternsrecovery.parsingElements.ClassParser;

public class LongMethod {
	private IJavaProject projectToAnalyze;
	private ClassParser parser;
	private HashMap<IMethod, Integer> methodLoc;
	private HashMap<IMethod, Integer> candidateLongMethods;

	public LongMethod(IJavaProject pProject){
		this.setProjectToAnalyze(pProject);
		this.parser=new ClassParser();
		this.setClassLoc(new HashMap<IMethod, Integer>());
	}

	public HashMap<IMethod, Integer> searchCandidateLongMethods() {
		candidateLongMethods=new HashMap<IMethod, Integer>();
		try {
			for(IPackageFragment pack: this.projectToAnalyze.getPackageFragments()){
				for(ICompilationUnit cu: pack.getCompilationUnits()){	
					if(cu.getElementName().contains(".java")) {
						for(IType t: cu.getTypes()){
							for(IMethod method:t.getMethods()){
								methodLoc.put(method, this.LOC(method));
							}
						}
					}
				}
			}
		} catch (JavaModelException e) {
			e.printStackTrace();
		}
		int max=this.max();
		if(max!=-1){
			double upperHinge=this.upperHinge();

			for(Entry<IMethod, Integer> value: methodLoc.entrySet())
				if((value.getValue()>upperHinge) && (value.getValue()<max)) {
					if(value.getValue().intValue()>150)
						candidateLongMethods.put(value.getKey(), value.getValue());	
				}
		}
		return candidateLongMethods;
	}

	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;
	}

	public HashMap<IMethod, Integer> getClassLoc() {
		return methodLoc;
	}

	public void setClassLoc(HashMap<IMethod, Integer> classLoc) {
		this.methodLoc = classLoc;
	}

	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) {
			return loc;
		}
		return loc+1;
	}

	private Double median(ArrayList<Integer> values) {
		Collections.sort(values);	
		if((values.size()%2==0))
			return (double) ((values.get((values.size()/2)-1) + (values.get(values.size()/2))))/2;
		else return (double) (values.get(values.size()/2));
	}

	private int max() { 
		if(methodLoc.values().size()>0)
			return Collections.max(methodLoc.values());
		else return -1;
	}

	private Double upperHinge() {
		ArrayList<Integer> values=new ArrayList<Integer>();
		ArrayList<Integer> valuesForUpperHinge=new ArrayList<Integer>();

		for(Integer i: this.methodLoc.values())
			values.add(i);

		int max=this.max();
		double median=this.median(values);

		for(Entry<IMethod, Integer> value: methodLoc.entrySet())
			if((value.getValue()>median) && (value.getValue()<max)) valuesForUpperHinge.add(value.getValue());

		return this.median(valuesForUpperHinge);
	}

	public String toString() { 
		String toReturn="";
		for(Entry<IMethod, Integer> value: candidateLongMethods.entrySet()){
			try {
				if((value.getKey().getCompilationUnit().getPackageDeclarations().length!=0) && (value.getKey().getCompilationUnit().getPackageDeclarations()[0]!=null))
					toReturn+=value.getKey().getElementName()+"; " +value.getKey().getCompilationUnit().getElementName()+"; "+ value.getKey().getCompilationUnit().getPackageDeclarations()[0].getElementName() + "; " + value.getValue().intValue() +";\n";
				else 
					toReturn+=value.getKey().getElementName()+"; "+value.getKey().getCompilationUnit().getElementName() +"; " + value.getValue().intValue() +";\n";
			} catch (JavaModelException e) {
				e.printStackTrace();
			}
		}
		return toReturn;
	}
}