package antipatternsrecovery.metrics;

import java.util.ArrayList;
import java.util.HashMap;

import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IMethod;
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 org.eclipse.jdt.core.dom.VariableDeclaration;

import antipatternsrecovery.beans.ClassBean;
import antipatternsrecovery.parsingElements.Parser;
import antipatternsrecovery.parsingElements.VariableDeclarationVisitor;

public class LCOM {
	private int counterIntersect;
	private int conterNonIntersect;
	private ArrayList<IType> classes;
	private ArrayList<ClassBean> classesBean;
	private boolean isType;
	private HashMap<String, Double> lcomValues;
	private double sumOfClassesValue;

	public double LCOM(IType pClass) throws JavaModelException {
		this.counterIntersect=0;
		this.conterNonIntersect=0;
		
		if(pClass.exists()) {
			this.lcomValues=new HashMap<String, Double>();
			
			IField[] fields=pClass.getFields();
			IMethod[] methods=pClass.getMethods();
			ArrayList<String> usedFields = null;
			ArrayList<ArrayList<String>> allUses=new ArrayList<ArrayList<String>>();

			// Get all used fields by all methods
			for(IMethod method: methods) {
				usedFields=new ArrayList<String>();
				for(IField field: fields) {
					if(this.isUsed(field, method)){
						usedFields.add(field.getElementName());
					}
				}
				allUses.add(usedFields);
			}

			if(allUses.size()>1){
				int halfCounterIntersect=0;
				ArrayList<String> firstMethodToCompare;
				ArrayList<String> secondMethodToCompare;

				for(int i=0; i<allUses.size()-1; i++){
					for(int j=i+1; j<(allUses.size()); j++){

						firstMethodToCompare=allUses.get(i);
						secondMethodToCompare=allUses.get(j);

						halfCounterIntersect=0;
						for(String s: firstMethodToCompare){
							for(String s1: secondMethodToCompare){
								if(s.equals(s1)) {
									halfCounterIntersect++;
								}
							}
						}

						if(halfCounterIntersect>0) {
							this.counterIntersect++;
						} else if(halfCounterIntersect==0) this.conterNonIntersect++;
					}
				}
			}


			if(this.conterNonIntersect>this.counterIntersect){
				this.lcomValues.put(pClass.getElementName(), this.normalizedValue(this.conterNonIntersect-this.counterIntersect, pClass));
			} else {
				this.lcomValues.put(pClass.getElementName(), 0.0);
			}
		}
		return this.conterNonIntersect-this.counterIntersect;
	}

	public int getCounterIntersect() {
		return this.counterIntersect;
	}

	public void setCounterIntersect(int pCounterIntersect) {
		this.counterIntersect = pCounterIntersect;
	}

	public int getConterNonIntersect() {
		return this.conterNonIntersect;
	}

	public void setConterNonIntersect(int pConterNonIntersect) {
		this.conterNonIntersect = pConterNonIntersect;
	}

	private Double normalizedValue(int pValue, IType pClass) throws JavaModelException {
		if(pClass.getMethods().length==0) return 0.0;
		if(pClass.getMethods().length==1) return 0.0;
		Double max=((double) (pClass.getMethods().length)) * (((double) pClass.getMethods().length)-1.0);

		return ((max - (double) pValue)/max);

	}	

	// This method allows to check if a field is used in a method;
	public boolean isUsed(IField pField, IMethod pMethod) throws JavaModelException {
		boolean existLocalVariable=false;
		Parser methodParser=new Parser();
		VariableDeclarationVisitor localVariableDeclarationVisitor=new VariableDeclarationVisitor();
		try{
			TypeDeclaration block=methodParser.createParser(pMethod.getSource(), ASTParser.K_CLASS_BODY_DECLARATIONS);
			block.accept(localVariableDeclarationVisitor);	

			for(VariableDeclaration vd: localVariableDeclarationVisitor.getFields()){
				if((vd.getName().getIdentifier()).equals(pField.getElementName())) 
					existLocalVariable=true;
			}

			String variableName="";

			if(existLocalVariable) variableName="this."+pField.getElementName();
			else variableName=pField.getElementName();

			String method = pMethod.getSource();

			if(method.contains(variableName)){
				int index=method.indexOf(variableName);
				if(!this.isCharacter(method.charAt(index+variableName.length())))
					return true;
			}
		}catch(Exception ex){
			return false;
		}

		return false;
	} 
	// This method allows to check if a char passed for parameter is a character;
	private boolean isCharacter(char pCharToCheck){
		if((pCharToCheck!='q') && (pCharToCheck!='w') && (pCharToCheck!='e') && (pCharToCheck!='r') && (pCharToCheck!='t') && (pCharToCheck!='y') && (pCharToCheck!='u')
				&& (pCharToCheck!='i') && (pCharToCheck!='o') && (pCharToCheck!='p') && (pCharToCheck!='a') && (pCharToCheck!='s')
				&& (pCharToCheck!='d') && (pCharToCheck!='f') && (pCharToCheck!='g') && (pCharToCheck!='h') && (pCharToCheck!='j') && (pCharToCheck!='k') && (pCharToCheck!='l')
				&& (pCharToCheck!='z') && (pCharToCheck!='x') && (pCharToCheck!='c')
				&& (pCharToCheck!='v') && (pCharToCheck!='b') && (pCharToCheck!='n') && (pCharToCheck!='m') && (pCharToCheck!='1') && (pCharToCheck!='2') && (pCharToCheck!='3')
				&& (pCharToCheck!='4') && (pCharToCheck!='5') && (pCharToCheck!='6') && (pCharToCheck!='7') && (pCharToCheck!='8') && (pCharToCheck!='9')
				&& (pCharToCheck!='$')) return false;

		return true;
	}
}
