package Detectors;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.core.PackageFragmentRoot;

import Analysis.printer;
import Smells.Commonmethod;
import Smells.LargeClass;
import Smells.LongMethod;
import Smells.SMELLTYPE;
import Smells.Smell;

public class CommonMethodsDector extends Detector {
    public  String allComparedMethods;
	public CommonMethodsDector(IResource iresource, CompilationUnit Punit) {
		super(iresource, Punit);
		// TODO Auto-generated constructor stub
	}

	public List<Smell> DetectSmells() {
		List types = unit.types();
		smells = new ArrayList<Smell>();
		for (Object obj : types) {
			if (!(obj instanceof TypeDeclaration))
				continue;
			TypeDeclaration typeDec = (TypeDeclaration) obj;
			// IProject s=iresource.getProject();

			DetectCommonMethods(typeDec);
		} // 引入的目标文件typeDec
		return smells;
	}

	public void DetectCommonMethods(TypeDeclaration aType) {
		Findmethod n = new Findmethod(resource, unit);
		n.find(aType);
       }
		
	

	class Findmethod {
		IResource Iresource;
		CompilationUnit punit;
		

		Findmethod(IResource iresource, CompilationUnit Punit) {

			Iresource = iresource;
			punit = Punit;
		}

		void find(TypeDeclaration editingType) {
		    IJavaElement javaElement = JavaCore.create(Iresource);
            IPackageFragment[] JS;
			try {
				IJavaProject A = javaElement.getJavaProject();
				JS = A.getPackageFragments();
				MethodDeclaration[] methods = editingType.getMethods(); // 类type中的所有方法
				for (MethodDeclaration methodToBeCompared : methods) {
                      ArrayList<List<String>> detectedCommonMethods2= new ArrayList<List<String>>();
					char[] f = methodToBeCompared.toString().toCharArray();
					// printer.println("get Source: " +f);
					ArrayList charArrayA = new ArrayList();
					for (char c : f) {
						charArrayA.add(c);
					}

					for (IPackageFragment J : JS) {

						if (J.getKind() != IPackageFragmentRoot.K_SOURCE)
							continue;

						ICompilationUnit[] r = J.getCompilationUnits(); // find
																		// common
																		// method

						for (ICompilationUnit m : r) {

							if (m.getPath().toString().trim().equalsIgnoreCase(
									punit.getJavaElement().getPath().toString()
											.trim()))
								continue; // 除去本身
							IType[] types = m.getAllTypes(); // 所有类组成的数组
							Type supertypeOfEditingType = editingType.getSuperclassType();
							
							for (IType comparedType : types) {
								
									String superclassNameofComparedType= comparedType.getSuperclassName();
									
								    if(supertypeOfEditingType==null)
								    {
								    	if(superclassNameofComparedType!=null)
								    		continue;
								    	
								    }
								    else //  supertypeOfEditingType!=null
								     { 
								    	if(superclassNameofComparedType==null) continue;
									
								        if (!superclassNameofComparedType.equalsIgnoreCase(supertypeOfEditingType.toString()))
										
										continue;
								     }
								    IMethod[] ms = comparedType.getMethods(); // 一个类中所有方法组成的数组
	                           	for (IMethod g : ms) {
	                           		
	                           	int numberOfParameters1=methodToBeCompared.parameters().size();
	                           	int numberOfParameters2= g.getParameters().length; 
	                           	
	                         //  	System.out.println("numberOfParameters1==="+numberOfParameters1);
	                          // 	System.out.println("numberOfParameters2==="+numberOfParameters2);
	                           	
	                           	
	                           	
	                         	if(!(numberOfParameters1==numberOfParameters2))continue;
	                           	
      
	                         	ILocalVariable[] paramerTyp2 = g.getParameters();
                             String[] paramerType=g.getParameterTypes();
                             int size=methodToBeCompared.parameters().size();
							
                          
;                            ArrayList signal=new ArrayList();
	                              
								for(int y=0;y<size;y++)
                          		
                              {      int signal2=0;
                                	
                                	
                                	    String parameterBeCompared=  methodToBeCompared.parameters().get(y).toString().split(" ")[0];
                                	    for(int z=0;z<size;z++)
                                	 { 
                                	    	String typeOfParamer=paramerTyp2[z].toString().split(" ")[0];
                                            if(typeOfParamer.equals(parameterBeCompared)){signal2=1;}   
                                	    else continue;
                                            
                                	 
                                	 }
                                	  if(signal2==1)  signal.add(1);
                                	
                                            
                                }
                           	     if(signal.size()<size)continue;
                      
							         
   	                              	String code1 = g.getSource(); 
									ArrayList charArray = new ArrayList();
									for (int i = 0; i < code1.length(); i++) {
										charArray.add(code1.charAt(i));
									}
 
								

									for (int i = 0; i < charArray.size()
											&& i < charArrayA.size(); i++) {
										if (charArray.get(i) != charArrayA
												.get(i))
											break;

									}

									Diff diff = new Diff(charArray, charArrayA);
									diff.diff();
									int totalDiff = diff.AmoutofDiffernce();
									int length = g.getSource().length();
									double ratio = 2*totalDiff
											/ ((double) length + (double) methodToBeCompared
													.getLength());

								//	printer.println("length is " + length);

								//	printer
								//			.println("totalDiff is "
									//				+ totalDiff);
								//	printer.println(" the differece  ratio is "
									//		+ ratio);
								//	System.out.println(" ratio is " + ratio
										//	* 100);

									if(length<100 && ratio>0.01) continue;
								    if(methodToBeCompared.getLength()<100) continue ;
				                    	
								    if ((ratio * 100) > THRESHOLDS.getCOMMONMETHODTIME()) continue;
				                    	
										ArrayList<String> methodPathNameAndRatio=new ArrayList<String>();
										methodPathNameAndRatio.add(g.getPath().toString());
										methodPathNameAndRatio.add(g.getElementName().toString());
										methodPathNameAndRatio.add(Double.toString(ratio * 100));  
                                        detectedCommonMethods2.add(methodPathNameAndRatio);
 
										
						            
				                    	
                             	      }	
								

							}

						
					}
					}
				
					if(detectedCommonMethods2.size()!=0)
					{Smell asmell = new Commonmethod();

					asmell.resource = Iresource;
					asmell.unit = this.punit;
					asmell.associatedNode = methodToBeCompared;
					asmell.setLength(methodToBeCompared.getLength());
					asmell.setStartPoint( methodToBeCompared.getStartPosition());
				    ArrayList<ArrayList<String>> List1=new ArrayList<ArrayList<String>>();
					   
					
					for(int i=0;i<detectedCommonMethods2.size();i++)      //一个类中保留一个commonMethod
					{   
					    String theNameOfMethod;
					    int signal=0;
					
					        String pathName= detectedCommonMethods2.get(i).get(0);
					        
					        ArrayList<ArrayList<String>> List=new ArrayList<ArrayList<String>>();//同一类中的所有commonMethods
					       
					        
					         for(int j=i+1;j<detectedCommonMethods2.size();j++)
					         {    String methodName=detectedCommonMethods2.get(j).get(1);
					              String ratio=detectedCommonMethods2.get(j).get(2);
					        	 String pathName2=detectedCommonMethods2.get(j).get(0);      //路径是否一致
					                      if(pathName.equals(pathName2))
					                      {   ArrayList<String> nameAndRationList=new ArrayList<String>();   //存放方法名和Ratio
									    	
											 nameAndRationList.add(methodName);
					                         nameAndRationList.add(ratio);
					                         nameAndRationList.add(pathName2);
					                         List.add(nameAndRationList);
					                         detectedCommonMethods2.remove(detectedCommonMethods2.get(j));
					                         signal=1;
					                    	  
					                    	 }  
					                    	  
					           }          	  
					                     
					       if (List.size() != 0) {
					    	   ArrayList<String> nameAndRationList=new ArrayList<String>();   //detectedCommonMethods2get(i)本身
						    	
								 nameAndRationList.add(detectedCommonMethods2.get(i).get(1));
		                         nameAndRationList.add(detectedCommonMethods2.get(i).get(2));
		                         nameAndRationList.add(pathName);
		                         List.add(nameAndRationList);
								theNameOfMethod = List.get(0).get(0);
								double ratio = Double.parseDouble(List.get(0).get(1));
								String  MethodsPath = List.get(0).get(2) ; 
								for (int k = 1; k < List.size(); k++) {                
									if (Double.parseDouble(List.get(k).get(1)) < ratio) {
										theNameOfMethod = List.get(k).get(0);
									
										
										MethodsPath = List.get(k).get(2).toString();
										
									}

								}
								ArrayList<String> list=new ArrayList<String>();
								list.add(MethodsPath);
								list.add(theNameOfMethod);
								List1.add(list);
							}
					       
					        if(signal==0)
					        {
					        	ArrayList<String> list=new ArrayList<String>();
					        	
					        list.add(pathName);
					        list.add(detectedCommonMethods2.get(i).get(1).toString());
					        List1.add(list);
					        
					      }
					    
					        
					        
					   }     
					// System.out.println("下一个");
					String all = null;    
					  for(int r1=0;r1<List1.size();r1++)   
					  { System.out.println(List1.get(r1).get(0));
					   int length=List1.get(r1).get(0).substring(1).split("/").length;
						  all=all+List1.get(r1).get(0).substring(1).split("/")[length-1].replace(".java", "")+"."+List1.get(r1).get(1)+"==";
						//  System.out.println("所有的common"+all);
		  
					  }
					 if(all.startsWith("null"))all=all.substring(4);
					int length= punit.getJavaElement().getPath().toString().substring(1).split("/").length;
				
	asmell.explaination=punit.getJavaElement().getPath().toString().substring(1).split("/")[length-1].replace(".java", "")+"."+methodToBeCompared.getName().toString()
						                   +"== "+all+"> are common methods.";
	asmell.method=punit.getJavaElement().getPath().toString().substring(1).split("/")[length-1].replace(".java", "")+"."+methodToBeCompared.getName().toString();
					
					smells.add(asmell);
					
											
					}
				
				
				}
				
							
				
			} catch (JavaModelException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

}
