package jakarta.mail.util;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ResourceLoader {

    private static final Logger logger = Logger.getLogger(ResourceLoader.class.getName());

    private ResourceLoader() {
    }

    public static ClassLoader[] getClassLoaders(final Class<?>... classes) {
        ClassLoader[] loaders = new ClassLoader[classes.length];
        int w = 0;
        for (Class<?> k : classes) {
            ClassLoader cl;
            if (k == Thread.class) {
                cl = Thread.currentThread().getContextClassLoader();
            } else if (k == System.class) {
                cl = ClassLoader.getSystemClassLoader();
            } else {
                cl = k.getClassLoader();
            }
            if (cl != null) {
                loaders[w++] = cl;
            }
        }
        if (loaders.length != w) {
            loaders = Arrays.copyOf(loaders, w);
        }
        return loaders;
    }

    public static InputStream getResourceAsStream(final Class<?> c, final String name) throws IOException {
        try {
            return c.getClassLoader().getResourceAsStream(name);
        } catch (RuntimeException e) {
            throw new IOException("getResourceAsStream for class " + c + " name = " + name + " failed");
        }
    }

    public static URL[] getResources(final ClassLoader cl, final String name) {
        URL[] ret = null;
        try {
            List<URL> v = Collections.list(cl.getResources(name));
            if (!v.isEmpty()) {
                ret = new URL[v.size()];
                v.toArray(ret);
            }
        } catch (IOException ioex) {
            logger.log(Level.WARNING, ioex.getMessage());
        }
        return ret;
    }

    public static URL[] getSystemResources(final String name) {
        URL[] ret = null;
        try {
            List<URL> v = Collections.list(ClassLoader.getSystemResources(name));
            if (!v.isEmpty()) {
                ret = new URL[v.size()];
                v.toArray(ret);
            }
        } catch (IOException ioex) {
            logger.log(Level.WARNING, ioex.getMessage());
        }
        return ret;
    }

    /**
     * Load from the named file.
     */
    public static void loadFile(String name, StreamLoader loader) {
        try (InputStream clis = new BufferedInputStream(new FileInputStream(name))) {
            loader.load(clis);
            logger.log(Level.CONFIG, "successfully loaded file: {0}", name);
        } catch (FileNotFoundException fex) {
            // ignore it
        } catch (IOException e) {
            if (logger.isLoggable(Level.CONFIG))
                logger.log(Level.CONFIG, "not loading file: " + name, e);
        }
        // ignore it
    }

    /**
     * Load from the named resource.
     */
    public static void loadResource(String name, Class<?> cl, StreamLoader loader, boolean expected) {
        try (InputStream clis = ResourceLoader.getResourceAsStream(cl, name)) {
            if (clis != null) {
                loader.load(clis);
                logger.log(Level.CONFIG, "successfully loaded resource: {0}", name);
            } else {
                if (expected)
                    logger.log(Level.WARNING, "expected resource not found: {0}", name);
            }
        } catch (IOException e) {
            logger.log(Level.CONFIG, "Exception loading resource", e);
        }
        // ignore it
    }

    /**
     * Load all of the named resource.
     */
    public static void loadAllResources(String name, Class<?> cl, StreamLoader loader) {
        boolean anyLoaded = false;
        try {
            URL[] urls;
            ClassLoader cld = cl.getClassLoader();
            if (cld != null)
                urls = ResourceLoader.getResources(cld, name);
            else
                urls = ResourceLoader.getSystemResources(name);
            if (urls != null) {
                for (URL url : urls) {
                    logger.log(Level.CONFIG, "URL {0}", url);
                    try (InputStream clis = url.openStream()) {
                        if (clis != null) {
                            loader.load(clis);
                            anyLoaded = true;
                            logger.log(Level.CONFIG,
                                    "successfully loaded resource: {0}", url);
                        } else {
                            logger.log(Level.CONFIG,
                                    "not loading resource: {0}", url);
                        }
                    } catch (FileNotFoundException fex) {
                        // ignore it
                    } catch (IOException ioex) {
                        logger.log(Level.CONFIG, "Exception loading resource",
                                ioex);
                    }
                }
            }
        } catch (Exception ex) {
            logger.log(Level.CONFIG, "Exception loading resource", ex);
        }

        // if failed to load anything, fall back to old technique, just in case
        if (!anyLoaded) {
		    /*
		    logger.config("!anyLoaded");
		    */
            loadResource("/" + name, cl, loader, false);
        }
    }
}
