package antipatternsrecovery.beans;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Vector;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
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.CompilationUnit;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.internal.core.PackageFragment;

import antipatternsrecovery.parsingElements.InvocationParser;
import antipatternsrecovery.parsingElements.MethodDeclarationsVisitor;
import antipatternsrecovery.parsingElements.MethodInvocationsVisitor;
import antipatternsrecovery.parsingElements.Parser;

@SuppressWarnings("restriction")
public class BeanConverter {

	/**
	 * The function convert the ICompilationUnit to ClassBean;
	 * 
	 * @param pClassToSplit 
	 * 				the class to split;
	 * 
	 * @return 
	 * 				the ClassBean converted;
	 * 
	 * @throws JavaModelException
	 * 				exception in the Java Model;
	 */
	public static ClassBean castCUToClassBean(ICompilationUnit pClassToSplit) throws JavaModelException {
		ClassBean classBean=null;
		PackageBean pb = new PackageBean();
		try {
			pb.setName(pClassToSplit.getPackageDeclarations()[0].getElementName());
		}catch(ArrayIndexOutOfBoundsException ex){}
		
		ArrayList<ClassBean> allClassInPackage=new ArrayList<ClassBean>();
		PackageFragment pf=(PackageFragment) pClassToSplit.getAncestor(IJavaElement.PACKAGE_FRAGMENT);
		
		try {
			@SuppressWarnings("unchecked")
			ArrayList<ICompilationUnit> array=pf.getChildrenOfType(IJavaElement.COMPILATION_UNIT);
			for(ICompilationUnit c:array) {
				ClassBean cb=new ClassBean();
				cb.setName(c.getElementName());
				buildMethodBean(c, cb);
				if (c.getElementName().equals(pClassToSplit.getElementName()))
				{
					classBean=cb;
				}
				cb.setBelongingPackage(pb);
				cb.setTextContent(pClassToSplit.getSource());
				allClassInPackage.add(cb);
			}
		} catch (JavaModelException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		pb.setClasses(allClassInPackage);
		return classBean;
	}

	public static ClassBean softCastCUToClassBean(ICompilationUnit pClassToSplit) throws JavaModelException {
		ClassBean classBean=null;
		PackageBean pb = new PackageBean();
		if(pClassToSplit.getPackageDeclarations().length!=0) { 
			if(pClassToSplit.getPackageDeclarations()[0].getElementName()!=null)
				pb.setName(pClassToSplit.getPackageDeclarations()[0].getElementName());
		}
		//ArrayList<ClassBean> allClassInPackage=new ArrayList<ClassBean>();
		PackageFragment pf=(PackageFragment) pClassToSplit.getAncestor(IJavaElement.PACKAGE_FRAGMENT);
		try {
			@SuppressWarnings("unchecked")
			ArrayList<ICompilationUnit> array=pf.getChildrenOfType(IJavaElement.COMPILATION_UNIT);
			for(ICompilationUnit c:array)
			{
				ClassBean cb=new ClassBean();
				cb.setName(c.getElementName());
				if (c.getElementName().equals(pClassToSplit.getElementName()))
				{
					classBean=cb;
				}
				if(pClassToSplit.getPackageDeclarations().length!=0) { 
					cb.setBelongingPackage(pb);
				}
				//cb.setTextContent(pClassToSplit.getSource());
				//allClassInPackage.add(cb);
			}
		} catch (JavaModelException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//pb.setClasses(allClassInPackage);
		return classBean;
	}

	public static  MethodBean castIMToMethodBean(IMethod iMethodToCast){
		MethodBean methodBean = new MethodBean();
		methodBean.setName(iMethodToCast.getElementName());
		//methodBean.setTextContent(iMethodToCast.getSource());
		ICompilationUnit classOrigin=(ICompilationUnit)iMethodToCast.getAncestor(IJavaElement.COMPILATION_UNIT);
		IPackageFragment packageFragment=(IPackageFragment)classOrigin.getAncestor(IJavaElement.PACKAGE_FRAGMENT);
		PackageBean pack=new PackageBean();
		pack.setName(packageFragment.getElementName());
		ClassBean c=new ClassBean();
		c.setName(classOrigin.getElementName());
		c.setBelongingPackage(pack);
		methodBean.setBelongingClass(c);
		return methodBean;
	}

	public static ICompilationUnit castClassBeanToCu(ClassBean pClassBean, IJavaProject pJavaProject){

		try {
			for(IPackageFragment pack: pJavaProject.getPackageFragments()){
				for(ICompilationUnit cu: pack.getCompilationUnits()){
					if(cu.getElementName().substring(0, cu.getElementName().length()-5).equals(pClassBean.getName()))
						return cu;
				}
			}
		} catch (JavaModelException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	public static IType castCUToIType(ICompilationUnit pClass){
		try {
			for(IType type: pClass.getTypes()){
				if (type.getElementName().equals(pClass.getElementName().substring(0, pClass.getElementName().length()-5))) return type;
			}
		} catch (JavaModelException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	public static InstanceVariableBean castIFieldToIVB(IField pField){
		InstanceVariableBean variable=new InstanceVariableBean();
		variable.setName(pField.getElementName());
		try {
			variable.setBelongingClass(BeanConverter.softCastCUToClassBean(pField.getCompilationUnit()));
		} catch (JavaModelException e) {
			e.printStackTrace();
		}
		return variable;
	}

	private static void buildMethodBean(ICompilationUnit pClassToSplit,ClassBean classBean) {
		MethodBean methodBean;
		try {
			for(IType iType : pClassToSplit.getTypes()) {
				for(IMethod method: iType.getMethods()) {
					methodBean = new MethodBean();
					if((method.isConstructor()) && (method.getParameterNames().length>0))
						methodBean.setDefaultCostructor(false);
					else methodBean.setDefaultCostructor(true);

					methodBean.setName(method.getElementName());
					methodBean.setTextContent(method.getSource());
					
					MethodDeclaration md=convertToAstNode(method);
					MethodInvocationsVisitor invocationVisitor=new MethodInvocationsVisitor();
					if(md!=null) {
						md.accept(invocationVisitor);
						Collection<MethodBean> invocationBeans = new Vector<MethodBean>();
						for (MethodInvocation invocation : invocationVisitor.getMethods()) {
							invocationBeans.add(InvocationParser.parse(invocation));
						}
						
						methodBean.setMethodCalls(invocationBeans);	
					}
					
					methodBean.setBelongingClass(classBean);

					classBean.addMethod(methodBean);
				}
			}
		} catch (JavaModelException e) {
			e.printStackTrace();
		}
	}
	
	public static MethodDeclaration convertToAstNode(final IMethod method) throws JavaModelException
	{
	    final ICompilationUnit compilationUnit = method.getCompilationUnit();
		Parser parser=new Parser(compilationUnit.getSource());
	    final CompilationUnit compilationUnitNode = parser.createParser();
	    
	    MethodDeclarationsVisitor visitor=new MethodDeclarationsVisitor();
	    compilationUnitNode.accept(visitor);
	   
	    for(MethodDeclaration md: visitor.getMethods()) {
	    	if(md.getName().getIdentifier().equals(method.getElementName())) {
	    		return md;
	    	}
	    }
	    
	    return null;
	}
}
