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.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;

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

public class GodClass {
	private IJavaProject projectToAnalyze;
	private ClassParser parser;
	private HashMap<ClassBean, Integer> classLoc;
	private HashMap<ClassBean, Integer> candidateBlobs;

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

	public HashMap<ClassBean, Integer> searchCandidateBlobs() {
		this.candidateBlobs=new HashMap<ClassBean, Integer>();
		try {
			for(IPackageFragment pack: this.projectToAnalyze.getPackageFragments()) {
				for(ICompilationUnit cu: pack.getCompilationUnits()) {	 	
					if(cu.getElementName().contains(".java") && (cu.getTypes().length!=0)) {
						IType type=cu.getType(cu.getElementName().substring(0, cu.getElementName().length()-5));
						if(type.getFields().length>6) {
							int loc=this.LOC(cu);
							if(loc>1000)
								this.classLoc.put(BeanConverter.softCastCUToClassBean(cu), loc);
						}
					}
				}
			}
		} catch (JavaModelException e) {
			e.printStackTrace();
		}
		int max=this.max();
		if(max!=-1) {
			double upperHinge=this.upperHinge();
			for(Entry<ClassBean, Integer> value: this.classLoc.entrySet())
				if((value.getValue()>upperHinge) && (value.getValue()<max)) candidateBlobs.put(value.getKey(), value.getValue());	
		}
		return this.candidateBlobs;
	}

	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<ClassBean, Integer> getClassLoc() {
		return classLoc;
	}

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

	private int LOC(ICompilationUnit pClass){
		int loc=0;
		try {
			String source=pClass.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;
	}

	private Double median(ArrayList<Integer> values) {
		Collections.sort(values);	
		if(values.size()==0) return 0.0;
		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(this.classLoc.values().size()>0)
			return Collections.max(this.classLoc.values());
		else return -1;
	}

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

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

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

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

		return this.median(valuesForUpperHinge);
	}

	public String toString() { 
		String toReturn="";
		for(Entry<ClassBean, Integer> value: candidateBlobs.entrySet()){
			if(value.getKey().getBelongingPackage()!=null)
				toReturn+=value.getKey().getBelongingPackage().getName()+"."+value.getKey().getName()+";\n";
			else 
				toReturn+=value.getKey().getName()+";\n";
		}
		return toReturn;
	}
}