/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.io.uri.impl;

import net.ranides.assira.collection.iterators.EnumerationUtils;
import net.ranides.assira.generic.Ref;
import net.ranides.assira.io.uri.URIHandle;
import net.ranides.assira.io.uri.URIHandler;
import net.ranides.assira.io.uri.URIResolver;
import net.ranides.assira.io.uri.URIUtils;
import net.ranides.assira.reflection.util.ClassLoaderUtils;
import net.ranides.assira.text.StringUtils;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;

/**
 * This URIHandler loads files from resources in classpath
 *
 * Supported schemes: classpath, cp
 *
 * I/O operations:
 * Please note, that CClassPathHandler does not use classloader to create I/O streams.
 * It only converts "classpath://" uri into physical location, by calling {@link ClassLoader#getResources}
 * I/O operations will be delegated to connected URIResolver.
 * For example, file located inside JAR will be handled by {@link CJarHandler}.
 *
 * Search algorithm:
 * Handler chooses last result from collection returned by default classloader ({@link ClassLoaderUtils#getDefault()}).
 *
 * This strategy is exactly opposite to {@link ClassLoader#getResource}
 * which prefers parent, system and bootstrap loaders. This way, we select most specific version of resource,
 * from JAR file specified as last in classpath.
 *
 * Please note:
 * As every URIHandler, it should not be constructed directly, but connected with some instance of URIResolver.
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public class CClassPathHandler implements URIHandler {

    private final URIResolver resolver;

    /**
     * Creates default handler. It is used by default URIResolver
     */
    public CClassPathHandler() {
        this(URIResolver.DEFAULT);
    }

    /**
     * Creates handler connected with specified resolver.
     *
     * @param resolver resolver
     */
    public CClassPathHandler(URIResolver resolver) {
        this.resolver = resolver;
    }

    @Override
    public URIHandle resolve(URI uri) throws IOException {
        String path = StringUtils.ltrim(URIUtils.getFile(uri), '/');
        ClassLoader loader = ClassLoaderUtils.getDefault();
        Ref<URL> file = EnumerationUtils.last(loader.getResources(path));

        if(!file.isPresent()) {
            return new CInvalidHandle(resolver, uri);
        }
        try {
            return resolver.resolve(file.get().toURI());
        } catch (URISyntaxException e) {
            throw new IOException(e);
        }
    }

    @Override
    public Collection<String> schemes() {
        return Arrays.asList("classpath", "cp");
    }

}
