package io.collap.bryg.compiler.resolver;

import java.io.File;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Stack;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class ClassNameFinder {

    private List<String> paths = new ArrayList<> ();

    private ClassNameVisitor visitor;

    public ClassNameFinder (ClassNameVisitor visitor) {
        this.visitor = visitor;

        /* The *-wildcard signals that all jars in the directory ought to be included in the classpath. */
        addPath (System.getProperty ("java.home") + File.separator + "lib" + File.separator + "*");

        /* Add all directories and jars from the classpath. */
        String[] paths = System.getProperty ("java.class.path").split (System.getProperty ("path.separator"));
        for (String path : paths) {
            addPath (path);
        }
    }

    public void addPath (String path) {
        paths.add (path);
    }

    public void crawl () {
        crawlPaths (paths);
    }

    public void crawlPaths (List<String> paths) {
        for (String path : paths) {
            crawlPath (path);
        }
    }

    public void crawlPath (String path) {
        if (path.endsWith ("*")) { /* Wildcard path. */
            path = path.substring (0, path.length () - 1);
            File directory = new File (path);
            if (!directory.isDirectory ()) {
                System.out.println (path + " is not a directory!");
                return;
            }

            /* Crawl jar files. */
            File[] children = directory.listFiles ();
            if (children != null) {
                for (File child : children) {
                    if (child.getName ().endsWith (".jar")) {
                        crawlJarFile (child);
                    }
                }
            }
        }else {
            File file = new File (path);
            if (file.isDirectory ()) {
                crawlDirectory (file);
            }else if (file.getName ().endsWith (".jar")) {
                crawlJarFile (file);
            }
        }
    }

    public void crawlDirectory (File file) {
        crawlDirectory (file, file);
    }

    private void crawlDirectory (File root, File file) {
        if (file.isDirectory ()) {
            File[] files = file.listFiles ();
            if (files != null) {
                for (File child : files) {
                    crawlDirectory (root, child);
                }
            }
        }else {
            if (file.getName ().endsWith (".class")) {
                visitor.visit (createClassName (root, file));
            }
        }
    }

    private String createClassName (File root, File file) {
        Stack<String> names = new Stack<> ();
        String name = file.getName ();
        names.push (name.substring (0, name.lastIndexOf ('.')));

        /* Push names. */
        for (File directory = file.getParentFile ();
                directory != null && !directory.equals (root);
                directory = directory.getParentFile ()) {
            names.push (directory.getName ());
        }

        /* Build the full class name. */
        StringBuilder builder = new StringBuilder (32);
        while (!names.empty ()) {
            builder.append (names.pop ());
            if (!names.empty ()) {
                builder.append ('.');
            }
        }

        return builder.toString();
    }

    public void crawlJarFile (File jarFile) {
        JarFile jar;
        try {
            jar = new JarFile (jarFile);
        } catch (Exception ex) {
            System.out.println ("Jar file " + jarFile.getAbsolutePath () + " not found!");
            ex.printStackTrace ();
            return;
        }

        Enumeration<JarEntry> entries = jar.entries ();
        while (entries.hasMoreElements ()) {
            JarEntry entry = entries.nextElement ();
            String entryPath = entry.getName ();
            int extIndex = entryPath.lastIndexOf (".class");
            if (extIndex > 0) {
                visitor.visit (entryPath.substring (0, extIndex).replace ("/", "."));
            }
        }
    }

}

@@ -224,16 +224,312 @@
 rFile;%0A%0A
+// TODO: Add the options to skip files and folders. Or do the opposite, so the user must allow specific files and folders.%0A// This improvement will reduce the cost of finding the classes by a huge margin (Currently, big libraries might take%0A// the class name finder up to a second to complete).%0A%0A
 public c

Add performance improvement idea as TODO.