package org.ggp.base.util.reflection;

import java.io.File;
import java.io.FilenameFilter;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;

import com.google.common.collect.Lists;
import org.ggp.base.player.gamer.Gamer;
import org.ggp.base.util.configuration.ProjectConfiguration;


public class ProjectSearcher {
	public static void main(String[] args)
	{
		System.out.println(getAllClassesThatAre(Gamer.class));
	}

	public static List<Class<?>> getAllClassesThatAre(Class<?> ofThisType)
	{
		return getAllClassesThatAre(ofThisType, true);
	}

	public static List<Class<?>> getAllClassesThatAre(Class<?> ofThisType, boolean mustBeConcrete)
	{
		List<Class<?>> rval = new ArrayList<Class<?>>();
        findClassesInList(allClasses, ofThisType, mustBeConcrete, rval);
        findClassesInList(injectedClasses, ofThisType, mustBeConcrete, rval);
        return rval;
	}

    private static void findClassesInList(List<String> listToSearch, Class<?> ofThisType,
                                          boolean mustBeConcrete, List<Class<?>> rval) {
        for(String name : allClasses) {
            if(name.contains("Test_"))
                continue;

            Class<?> c = null;
            try {
                c = Class.forName(name);
            } catch (ClassNotFoundException ex)  {
                throw new RuntimeException(ex);
            }

            if(ofThisType.isAssignableFrom(c) && (!mustBeConcrete || !Modifier.isAbstract(c.getModifiers())) )
                rval.add(c);
        }
    }

    private static List<String> allClasses = findAllClasses();
    private static List<String> injectedClasses = Lists.newArrayList();

    public static <T> void injectClass(Class<T> klass) {
        injectedClasses.add(klass.getCanonicalName());
    }

	private static List<String> findAllClasses()
	{
		FilenameFilter filter = new FilenameFilter() {
	        public boolean accept(File dir, String name) {
	            return !name.startsWith(".");
	        }
	    };

		List<String> rval = new ArrayList<String>();
		Stack<File> toProcess = new Stack<File>();
		for(String classDirName : ProjectConfiguration.classRoots)
		    toProcess.add(new File(classDirName));
		while(!toProcess.empty())
		{
			File f = toProcess.pop();
			if(!f.exists())
				System.out.println("Could not find expected file: [" + f + "] when running ProjectSearcher.");
			if(f.isDirectory())
				toProcess.addAll(Arrays.asList(f.listFiles(filter)));
			else
			{
				if(f.getName().endsWith(".class"))
				{
					String fullyQualifiedName = f.getPath();
					for(String classDirName : ProjectConfiguration.classRoots) {
					    fullyQualifiedName = fullyQualifiedName.replaceAll("^" + classDirName.replace(File.separatorChar, '.'), "");
					}
					fullyQualifiedName = fullyQualifiedName.replaceAll("\\.class$","");
					fullyQualifiedName = fullyQualifiedName.replaceAll("^[\\\\/]", "");
					fullyQualifiedName = fullyQualifiedName.replaceAll("[\\\\/]", ".");
					rval.add(fullyQualifiedName);
				}
			}
		}

		return rval;
	}
}

@@ -145,92 +145,9 @@
 til.
-ArrayList;%0D%0Aimport java.util.Arrays;%0D%0Aimport java.util.List;%0D%0Aimport java.util.Stack
+*
 ;%0D%0A%0D
@@ -188,16 +188,56 @@
 Lists;%0D%0A
+import com.google.common.collect.Sets;%0D%0A
 import o
@@ -985,19 +985,18 @@
 sInList(
-Lis
+Se
 t%3CString
@@ -997,20 +997,23 @@
 String%3E 
-list
+classes
 ToSearch
@@ -1152,26 +1152,31 @@
  name : 
-allC
+c
 lasses
+ToSearch
 ) %7B%0D%0A   
@@ -1625,35 +1625,34 @@
  private static 
-Lis
+Se
 t%3CString%3E allCla
@@ -1688,35 +1688,34 @@
  private static 
-Lis
+Se
 t%3CString%3E inject
@@ -1730,25 +1730,22 @@
 s = 
-Lis
+Se
 ts.new
-ArrayLis
+HashSe
 t();
@@ -1887,19 +1887,18 @@
  static 
-Lis
+Se
 t%3CString
@@ -2097,19 +2097,18 @@
 %7D;%0D%0A%0D%0A%09%09
-Lis
+Se
 t%3CString
@@ -2120,29 +2120,23 @@
 l = 
-new ArrayList%3CString%3E
+Sets.newHashSet
 ();%0D

use Set to prevent duplicates