package eu.dnetlib.utils;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.TreeSet;

import org.apache.commons.collections.list.TreeList;
import org.junit.Test;

import pl.edu.icm.driver.tester.TestDescription;

/**
 * Lists and counts all test methods annotated with @Test annotation.
 * @author mhorst
 *
 */
public class FuncTestMethodsFinder {

	int globalCounter = 0;
	
	/**
	 * Scans all classes accessible from the context class loader which belong
	 * to the given package and subpackages.
	 * 
	 * @param packageName
	 *            The base package
	 * @return The classes
	 * @throws ClassNotFoundException
	 * @throws IOException
	 */
	private Iterable<Class> getClasses(String packageName) throws ClassNotFoundException, IOException
	{
	    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
	    String path = packageName.replace('.', '/');
	    Enumeration<URL> resources = classLoader.getResources(path);
	    List<File> dirs = new ArrayList<File>();
	    while (resources.hasMoreElements())
	    {
	        URL resource = resources.nextElement();
	        dirs.add(new File(resource.getFile()));
	    }
	    List<Class> classes = new ArrayList<Class>();
	    for (File directory : dirs)
	    {
	        classes.addAll(findClasses(directory, packageName));
	    }

	    return classes;
	}

	/**
	 * Recursive method used to find all classes in a given directory and
	 * subdirs.
	 * 
	 * @param directory
	 *            The base directory
	 * @param packageName
	 *            The package name for classes found inside the base directory
	 * @return The classes
	 * @throws ClassNotFoundException
	 */
	private List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException
	{
	    List<Class> classes = new ArrayList<Class>();
	    if (!directory.exists())
	    {
	        return classes;
	    }
	    File[] files = directory.listFiles();
	    for (File file : files)
	    {
	        if (file.isDirectory())
	        {
	            classes.addAll(findClasses(file, packageName + "." + file.getName()));
	        }
	        else if (file.getName().endsWith(".class"))
	        {
	            classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
	        }
	    }
	    return classes;
	}

	protected void processTestMethods(Method[] methods, Collection<String> methodNamesCSV) {
		if (methods!=null) {
			for (Method currentMethod : methods) {
				if (currentMethod.getAnnotation(Test.class)!=null) {
					TestDescription testDescr = currentMethod.getAnnotation(
							TestDescription.class);
					String id = currentMethod.getDeclaringClass().getName() + 
					'#' + currentMethod.getName() + "()";
					StringBuffer csvContent = new StringBuffer();
					csvContent.append(++globalCounter);
					csvContent.append(';');
					csvContent.append(currentMethod.getName());
					csvContent.append(';');
//					optional plan id
					if (testDescr!=null) {
						csvContent.append(testDescr.planId());
					}
					csvContent.append(';');
//					optional description
					if (testDescr!=null) {
						csvContent.append(testDescr.description());
					}
					csvContent.append(';');
					csvContent.append(id);
					methodNamesCSV.add(csvContent.toString());
				}
			}
		}
	}
	
	protected void processPackage(String packageName, Collection<String> methodNamesCSV) throws Exception {
		Iterable<Class> classes = getClasses(packageName);
		for (Class currentClass : classes) {
			if (currentClass.getAnnotation(TestDescription.class)!=null) {
				processTestMethods(currentClass.getMethods(), methodNamesCSV);
			}
		}
	}
	
	public static void main(String[] args) throws Exception {
		String outputFilePath = "/tmp/testCases.csv";
		FuncTestMethodsFinder finder = new FuncTestMethodsFinder();
		String[] packages = new String [] {"eu.dnetlib", "pl.edu"};
		Collection<String> methodNames = new ArrayList<String>();
		for (String currentPackage : packages) {
			finder.processPackage(currentPackage, methodNames);
		}
		System.out.println("test methods found: " + methodNames.size());
		File file = new File(outputFilePath);
		file.createNewFile();

		BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(file));
		for (String currentMethod : methodNames) {
			stream.write(currentMethod.getBytes());
			stream.write("\n".getBytes());
//			System.out.println(currentMethod);
		}
		stream.close();
	}
	
	
}
