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

import lombok.experimental.UtilityClass;
import net.ranides.assira.reflection.util.ClassLoaderUtils;
import net.ranides.assira.text.Charsets;
import net.ranides.assira.text.IOStrings;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.MissingResourceException;

/**
 * Utility methods for reading resource files
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
@UtilityClass public class ResourceUtils {

    /**
     * Extracts resource and saves it into specified file
     * It uses {@linkplain ClassLoaderUtils#getDefault() default class loader}.
     *
     * @param resource resource
     * @param target target
     * @throws IOException on error
     * @throws MissingResourceException if there is no resource
     */
    public static void extract(String resource, File target) throws IOException, MissingResourceException {
        extract(ClassLoaderUtils.getDefault(), resource, target);
    }

    /**
     * Extracts resource and saves it into specified file
     *
     * @param loader loader
     * @param resource resource
     * @param target target
     * @throws IOException on error
     * @throws MissingResourceException if there is no resource
     */
    public static void extract(ClassLoader loader, String resource, File target) throws IOException, MissingResourceException {
        try(InputStream istream = stream(loader, resource)) {
            try(OutputStream ostream = new BufferedOutputStream(new FileOutputStream(target))) {
                IOStreams.copy(istream, ostream);
            }
        }
    }

    /**
     * Returns URL to provided resource.
     * Most probably returned URL will use JAR scheme, or FILE (in unit tests)
     * It uses {@linkplain ClassLoaderUtils#getDefault() default class loader}.
     *
     * @param resource resource
     * @return URL
     * @throws MissingResourceException if there is no resource
     */
    public static URL url(String resource) throws MissingResourceException {
        return url(ClassLoaderUtils.getDefault(), resource);
    }

    /**
     * Returns URL to provided resource.
     * Most probably returned URL will use JAR scheme, or FILE (in unit tests)
     *
     * @param loader loader
     * @param resource resource
     * @return URL
     * @throws MissingResourceException if there is no resource
     */
    public static URL url(ClassLoader loader, String resource) throws MissingResourceException {
        URL url = loader.getResource(resource);
        if(null==url) {
            throw new MissingResourceException("Resource not found", "ResourceUtils", resource);
        }
        return url;
    }

    /**
     * Returns URL to provided resource.
     * Most probably returned URL will use JAR scheme, or FILE (in unit tests)
     * It uses parent class loader of provided class.
     *
     * @param loader loader
     * @param resource resource
     * @return URL
     * @throws MissingResourceException if there is no resource
     */
    public static URL url(Class<?> loader, String resource) throws MissingResourceException {
        URL url = loader.getResource(resource);
        if(null==url) {
            throw new MissingResourceException("Resource not found", "ResourceUtils", resource);
        }
        return url;
    }

    /**
     * Opens stream for provided resource.
     * It uses {@linkplain ClassLoaderUtils#getDefault() default class loader}.
     *
     * @param resource resource
     * @return stream
     * @throws MissingResourceException if there is no resource
     */
    public static InputStream stream(String resource) throws MissingResourceException {
        return stream(ClassLoaderUtils.getDefault(), resource);
    }

    /**
     * Opens stream for provided resource.
     *
     * @param loader loader
     * @param resource resource
     * @return stream
     * @throws MissingResourceException if there is no resource
     */
    public static InputStream stream(ClassLoader loader, String resource) throws MissingResourceException {
        InputStream stream = loader.getResourceAsStream(resource);
        if(null==stream) {
            throw new MissingResourceException("Resource not found", "ResourceUtils", resource);
        }
        return stream;
    }

    /**
     * Opens stream for provided resource.
     * It uses parent class loader of provided class.
     *
     * @param loader loader
     * @param resource resource
     * @return stream
     * @throws MissingResourceException if there is no resource
     */
    public static InputStream stream(Class<?> loader, String resource) throws MissingResourceException {
        InputStream stream = loader.getResourceAsStream(resource);
        if(null==stream) {
            throw new MissingResourceException("Resource not found", "ResourceUtils", resource);
        }
        return stream;
    }

    /**
     * Opens reader for provided resource, it uses "charset" for encoding.
     * It uses {@linkplain ClassLoaderUtils#getDefault() default class loader}.
     *
     * @param resource resource
     * @param charset charset
     * @return reader
     * @throws MissingResourceException if there is no resource
     */
    public static Reader reader(String resource, Charset charset) throws MissingResourceException {
        return reader(ClassLoaderUtils.getDefault(), resource, charset);
    }

    /**
     * Opens reader for provided resource, it uses "charset" for encoding.
     *
     * @param loader loader
     * @param resource resource
     * @param charset charset
     * @return reader
     * @throws MissingResourceException if there is no resource
     */
    public static Reader reader(ClassLoader loader, String resource, Charset charset) throws MissingResourceException {
        return new InputStreamReader(stream(loader, resource), charset);
    }

    /**
     * Opens reader for provided resource, it uses "UTF-8" for encoding.
     * It uses {@linkplain ClassLoaderUtils#getDefault() default class loader}.
     *
     * @param resource resource
     * @return reader
     * @throws MissingResourceException if there is no resource
     */
    public static Reader reader(String resource) throws MissingResourceException {
        return reader(resource, Charsets.UTF8);
    }

    /**
     * Opens reader for provided resource, it uses "UTF-8" for encoding.
     *
     * @param loader loader
     * @param resource resource
     * @return reader
     * @throws MissingResourceException if there is no resource
     */
    public static Reader reader(ClassLoader loader, String resource) throws MissingResourceException {
        return reader(loader, resource, Charsets.UTF8);
    }

    /**
     * Reads all text content from provided resource using "charset" encoding.
     * It uses {@linkplain ClassLoaderUtils#getDefault() default class loader}.
     *
     * @param resource resource
     * @param charset charset
     * @return string
     * @throws IOException on error
     * @throws MissingResourceException if there is no resource
     */
    public static String text(String resource, Charset charset) throws IOException, MissingResourceException {
        return IOStrings.read(reader(resource, charset));
    }

    /**
     * Reads all text content from provided resource using "charset" encoding.
     *
     * @param loader loader
     * @param resource resource
     * @param charset charset
     * @return string
     * @throws IOException on error
     * @throws MissingResourceException if there is no resource
     */
    public static String text(ClassLoader loader, String resource, Charset charset) throws IOException, MissingResourceException {
        return IOStrings.read(reader(loader, resource, charset));
    }

    /**
     * Reads all text content from provided resource using "UTF-8" encoding.
     * It uses {@linkplain ClassLoaderUtils#getDefault() default class loader}.
     *
     * @param resource resource
     * @return string
     * @throws IOException on error
     * @throws MissingResourceException if there is no resource
     */
    public static String text(String resource) throws IOException, MissingResourceException {
        return text(resource, Charsets.UTF8);
    }

    /**
     * Reads all text content from provided resource using "UTF-8" encoding.
     *
     * @param loader loader
     * @param resource resource
     * @return string
     * @throws IOException on error
     * @throws MissingResourceException if there is no resource
     */
    public static String text(ClassLoader loader, String resource) throws IOException, MissingResourceException {
        return text(loader, resource, Charsets.UTF8);
    }

}
