package me.qwq.doghouse;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 包类扫描器
 * @author Doghole & ChatGPT
 *
 */
public class PackageScanner {

    public static void main(String[] args) {
        String packageName = "me.qwq.doghouse";
        
        // Set the second parameter to true if you want to scan subpackages
        List<String> classNames = getClassesInPackage(packageName, true);

        System.out.println("Classes in package " + packageName + ":");
        for (String className : classNames) {
            System.out.println(className);
        }
    }
    private static Map<String, List<String>> scanResultCache = new HashMap<>();
    private static Map<String, List<String>> scanSubResultCache = new HashMap<>();

    public static List<String> getClassesInPackage(String packageName, boolean scanSubpackages) {
        
        if (scanSubpackages) {
            if (scanSubResultCache.containsKey(packageName)) {
                return scanSubResultCache.get(packageName);
            }
        }
        else {
            if (scanResultCache.containsKey(packageName)) {
                return scanResultCache.get(packageName);
            }
        }
        
        List<String> classNames = new ArrayList<>();
        String packagePath = packageName.replace('.', '/');
        
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        try {
            Enumeration<URL> resources = classLoader.getResources(packagePath);
            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                classNames.addAll(getClassesFromResource(resource, packageName, scanSubpackages));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        if (scanSubpackages) {
            scanSubResultCache.put(packagePath, classNames);
        }
        else {
            scanResultCache.put(packagePath, classNames);
        }

        return classNames;
    }

    private static List<String> getClassesFromResource(URL resource, String packageName, boolean scanSubpackages) {
        List<String> classNames = new ArrayList<>();
        if (resource.getProtocol().equals("jar")) {
            try {
                String jarPath = resource.getPath().substring(5, resource.getPath().indexOf('!'));
                classNames.addAll(getClassesFromJar(jarPath, packageName));
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            // Handle classes in a regular file system (not in a JAR)
            // You may need to adjust this part based on your project structure
            String packagePath = resource.getPath();
            File packageDirectory = new File(packagePath);
            classNames.addAll(findClassNames(packageDirectory, packageName, scanSubpackages));
        }
        return classNames;
    }

    private static List<String> getClassesFromJar(String jarPath, String packageName) throws IOException {
        List<String> classNames = new ArrayList<>();
        try (java.util.jar.JarFile jarFile = new java.util.jar.JarFile(jarPath)) {
            Enumeration<java.util.jar.JarEntry> jarEntries = jarFile.entries();
            while (jarEntries.hasMoreElements()) {
                java.util.jar.JarEntry entry = jarEntries.nextElement();
                if (entry.getName().startsWith(packageName) && entry.getName().endsWith(".class")) {
                    String className = entry.getName().replace('/', '.').replace(".class", "");
                    classNames.add(className);
                }
            }
        }
        return classNames;
    }

    private static List<String> findClassNames(File directory, String packageName, boolean scanSubpackages) {
        List<String> classNames = new ArrayList<>();
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    if (scanSubpackages) {
                        classNames.addAll(findClassNames(file, packageName + "." + file.getName(), true));
                    }
                } else if (file.getName().endsWith(".class")) {
                    String className = packageName + "." + file.getName().replace(".class", "");
                    classNames.add(className);
                }
            }
        }
        return classNames;
    }
}