package org.jacp.javafx.rcp.util;

import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Created with IntelliJ IDEA.
 * User: Andy Moncsek
 * Date: 19.07.13
 * Time: 10:43
 * Find classes for defined packages.
 */
public class ClassFinder {
    /**
     * Defined classpath
     */
    private static final String CLASSPATH = System.getProperty("java.class.path");
    /**
     * List with the jar files on the classpath
     */
    //  private static String[] jarFiles;
    /**
     * List with the directories on the classpath (containing .class files)
     */
    //  private static String[] binDirs;
    private List<Path> binDirs;
    /**
     * All Classpath elements
     */
    private static Path[] classPathDirs = null;
    private final PathMatcher matcher =
            FileSystems.getDefault().getPathMatcher("glob:*.class");



    /**
     * Default constructur initializes the directories indicated by the
     * CLASSPATH, if they are not yet initialized.
     */
    public ClassFinder() {
        if (classPathDirs == null) {
            binDirs = initClassPathDir();
        }
    }

    /**
     * Initialize the directories based on the classpath
     */
    private List<Path> initClassPathDir() {
        final String[] cs = CLASSPATH.split(File.pathSeparator);
        final Stream<String> entries = Stream.of(cs);
        return entries.parallel()
                .map(s -> FileSystems.getDefault().getPath(s))
                .filter(s -> Files.isDirectory(s, LinkOption.NOFOLLOW_LINKS)).collect(Collectors.toList());

    }

    /**
     * Retrive all classes of the indicated package. The package is searched in
     * all classpath directories that are directories
     *
     * @param packageName name of the package as 'ch.sahits.civ'
     * @return Array of found classes
     * @throws ClassNotFoundException
     */
    public Class[] getAll(final String packageName) throws ClassNotFoundException {

        final String packageDir = convertPackege(packageName);
        final List<String> files = new CopyOnWriteArrayList<>();
        final PathMatcher folderMatcher =
                FileSystems.getDefault().getPathMatcher("glob:**"+packageDir+"**");
        final SimpleFileVisitor <Path> visitor =   new CollectingFileVisitor(files,folderMatcher);
        binDirs.parallelStream().forEach(dir -> {
            try {
                Files.walkFileTree(dir, visitor);
            } catch (IOException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
        });

        final List<Class> result = exctractClasses(packageDir, files);

        return result.toArray(new Class[result.size()]);



    }

    private List<Class> exctractClasses(final String packageDir, List<String> files) {

        return files.parallelStream()
                .map(dir -> dir.substring(dir.lastIndexOf(packageDir), dir.length()))
                .map(subdir -> subdir.replace(File.separator, "."))
                .map(className -> className.substring(0, className
                        .lastIndexOf(".class")))
                .filter(classFile -> !classFile.contains("$"))
                .map(cFile -> {
                    try {
                        return ClassLoader.getSystemClassLoader().loadClass(cFile);
                    } catch (Exception e) {
                        return null;
                    }
                })
                .collect(Collectors.toList());

    }


    /**
     * Convert the package name into a relative directory path
     *
     * @param packageName name of the package as 'ch.sahits.civ'
     * @return relativ directory to the package
     */
    private String convertPackege(String packageName) {
        return packageName.replace(".", File.separator);
    }

    private class CollectingFileVisitor extends SimpleFileVisitor<Path>{
        private final List<String> files;
        private final PathMatcher folderMatcher;
        public CollectingFileVisitor(final List<String> files,final PathMatcher folderMatcher) {
            this.files=files;
            this.folderMatcher = folderMatcher;
        }
        @Override
        public FileVisitResult preVisitDirectory(Path dir,
                                                 BasicFileAttributes attrs) {
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
            final boolean matchesFolder =  folderMatcher.matches(file);
            if ( matchesFolder && matcher.matches(file.getFileName())) {
                files.add(file.toString());
            }
            return matchesFolder ? FileVisitResult.CONTINUE : FileVisitResult.SKIP_SUBTREE;
        }

        @Override
        public FileVisitResult visitFileFailed(Path file, IOException e) {
            return FileVisitResult.CONTINUE;
        }
    }


}

@@ -627,24 +627,100 @@
 ass.path%22);%0A
+    private static String OS = System.getProperty(%22os.name%22).toLowerCase();%0A
     /**%0A    
@@ -1178,24 +1178,264 @@
 *.class%22);%0A%0A
+    private static final String FILE_SEPERATOR;%0A%0A    static%7B%0A        FILE_SEPERATOR=isWindows()?File.separator+%22%22+File.separator:File.separator;%0A    %7D%0A%0A    public static boolean isWindows() %7B%0A%0A        return (OS.indexOf(%22win%22) %3E= 0);%0A%0A    %7D
 %0A%0A    /**%0A  
@@ -2754,26 +2754,50 @@
 lob:**%22+
+convertPackageToRegex(
 package
-Dir
+Name)
 +%22**%22);%0A
@@ -3565,17 +3565,17 @@
 .map(sub
-d
+D
 ir -%3E su
@@ -3575,17 +3575,17 @@
 r -%3E sub
-d
+D
 ir.repla
@@ -4359,24 +4359,25 @@
 kageName) %7B%0A
+%0A
         retu
@@ -4420,32 +4420,158 @@
 arator);%0A    %7D%0A%0A
+    private String convertPackageToRegex(String packageName)%7B%0A        return packageName.replace(%22.%22, FILE_SEPERATOR);%0A    %7D%0A%0A
     private clas

update ClassFinder to work with windows