/*
 * Capsule
 * Copyright (c) 2014-2015, Parallel Universe Software Co. All rights reserved.
 * 
 * This program and the accompanying materials are licensed under the terms 
 * of the Eclipse Public License v1.0, available at
 * http://www.eclipse.org/legal/epl-v10.html
 */

package co.paralleluniverse.common;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.ProviderNotFoundException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;

/**
 *
 * @author pron
 */
public class PathClassLoader extends FlexibleClassLoader {
    private final Object[] paths;

    public PathClassLoader(Path[] paths, ClassLoader parent, boolean childFirst) throws IOException {
        super(parent, childFirst);
        this.paths = process(paths);
    }

    public PathClassLoader(Path[] paths, boolean childFirst) throws IOException {
        super(childFirst);
        this.paths = process(paths);
    }

    public PathClassLoader(Path[] paths, ClassLoader parent) throws IOException {
        this(paths, parent, false);
    }

    public PathClassLoader(Path[] paths) throws IOException {
        this(paths, false);
    }

    private static Object[] process(Path[] paths) throws IOException {
        try {
            final Object[] os = new Object[paths.length];
            for (int i = 0; i < paths.length; i++) {
                final Path p = paths[i];
                final Object o;
                if (Files.isRegularFile(p))
                    o = FileSystems.newFileSystem(p, null);
                else
                    o = p;
                os[i] = o;
            }
            return os;
        } catch (ProviderNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    public final void close() {
        IOException ex = null;
        for (Object p : paths) {
            if (p instanceof FileSystem) {
                try {
                    ((FileSystem) p).close();
                } catch (IOException e) {
                    if (ex == null)
                        ex = e;
                    else
                        ex.addSuppressed(e);
                }
            }
        }
    }

    private Path resolve(Object o, String name) {
        final Path p;
        if (o instanceof FileSystem)
            p = ((FileSystem) o).getPath(name);
        else
            p = ((Path) o).resolve(name);
        return p;
    }

    @Override
    protected URL findResource1(String name) {
        try {
            for (Object o : paths) {
                final Path p = resolve(o, name);
                if (Files.exists(p))
                    return p.toUri().toURL();
            }
            return null;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected Enumeration<URL> findResources1(String name) {
        try {
            final List<URL> urls = new ArrayList<>();
            for (Object o : paths) {
                final Path p = resolve(o, name);
                if (Files.exists(p))
                    urls.add(p.toUri().toURL());
            }
            return Collections.enumeration(urls);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected InputStream findResourceAsStream(String name) {
        try {
            for (Object o : paths) {
                final Path p = resolve(o, name);
                if (isFileResource(p))
                    return Files.newInputStream(p);
            }
            return null;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected byte[] readResource(String name) {
        try {
            for (Object o : paths) {
                final Path p = resolve(o, name);
                if (isFileResource(p))
                    return Files.readAllBytes(p);
            }
            return null;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private boolean isFileResource(Path p) {
        return Files.exists(p) && !Files.isDirectory(p);
    }
}
