package com.majing.learning.elasticsearch.utils;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * @author : jingma2
 * @date : 2018/7/21
 * @description
 */
public class ResourceUtils {
    /** 加锁工具 */
    private static final byte[] BYTES = new byte[0];

    /** {@link ClassLoader} */
    private static ClassLoader classLoader;

    /**
     * 从项目,jar或文件系统中读取指定路径的文件<br />
     * 与loadResources()区别是本方法在有返回值时默认只返回一条记录,其余丢弃
     * <p>
     * @param locationPattern
     * <br/>
     *            0. 路径寻址前缀请参见{@link ResourceConstants}<br />
     *            1. 使用file,classpath和classpath*做路径开头<br />
     *            2. classpath寻址项目中的文件<br />
     *            3. classpath*既寻址项目,也寻址jar包中的文件<br />
     *            4. file寻址文件系统中的文件<br />
     *            5. 默认是classpath 6.
     *            例如:classpath*:log/log4j.xml;file:/home/ydhl/
     *            abc.sh;classpath:log/log4j.xml
     * @return 以URL返回结果
     * @throws IOException
     * @throws URISyntaxException
     */
    public static URL loadResource(String locationPattern) throws IOException, URISyntaxException {
        URL[] urlArray = loadResources(locationPattern);

        return ArrayUtils.isEmpty(urlArray) ? null : urlArray[0];
    }

    /**
     * 从项目,jar或文件系统中读取指定路径的文件<br />
     * <p>
     * @param locationPattern
     * <br/>
     *            0. 路径寻址前缀请参见{@link ResourceConstants}<br />
     *            1. 使用file,classpath和classpath*做路径开头<br />
     *            2. classpath寻址项目中的文件<br />
     *            3. classpath*既寻址项目,也寻址jar包中的文件<br />
     *            4. file寻址文件系统中的文件<br />
     *            5. 默认是classpath 6.
     *            例如:classpath*:log/log4j.xml;file:/home/ydhl/
     *            abc.sh;classpath:log/log4j.xml
     * @return 以URL返回结果
     * @throws IOException
     * @throws URISyntaxException
     *             中文路径支持
     */
    public static URL[] loadResources(String locationPattern) throws IOException, URISyntaxException {
        if (locationPattern.startsWith(ResourceConstants.CLASSPATH_ALL_URL_PREFIX.getValue())) {
            return load1(locationPattern);
        } else if (locationPattern.startsWith(ResourceConstants.CLASSPATH_URL_PREFIX.getValue())) {
            return load2(locationPattern);
        } else if (locationPattern.startsWith(ResourceConstants.FILE_URL_PREFIX.getValue())) {
            return load3(locationPattern);
        } else {
            // 默认为文件系统路径。
            locationPattern = "file:" + locationPattern;
            return ResourceUtils.loadResources(locationPattern);
        }
    }

    private static URL[] load1(String locationPattern) throws IOException, URISyntaxException {
        String location = locationPattern.substring(ResourceConstants.CLASSPATH_ALL_URL_PREFIX.getValue().length());
        if (location.startsWith(ResourceConstants.FOLDER_SEPARATOR.getValue())) {
            location = location.substring(1);
        }

        Enumeration<URL> resourceUrls = getDefaultClassLoader().getResources(location);
        Set<URL> result = new LinkedHashSet<URL>(16);
        while (resourceUrls.hasMoreElements()) {
            URL url = resourceUrls.nextElement();
            result.add(PathUtils.cleanPath(url));
        }
        return result.toArray(new URL[result.size()]);
    }

    private static URL[] load2(String locationPattern) throws URISyntaxException, IOException {
        String location = locationPattern.substring(ResourceConstants.CLASSPATH_URL_PREFIX.getValue().length());
        if (location.startsWith(ResourceConstants.FOLDER_SEPARATOR.getValue())) {
            location = location.substring(1);
        }

        String cleanPath = PathUtils.cleanPath(location);
        // 只支持文件的通配符匹配,不支持文件夹的通配符匹配
        // 如需实现文件夹的通配符匹配,请参照spring.utils包,较复杂
        if (StringUtils.contains(cleanPath, PunctuationConstants.STAR.getValue())
                || StringUtils.contains(cleanPath, PunctuationConstants.QUESTION_MARK.getValue())) {
            String directoryPath = StringUtils.substringBeforeLast(locationPattern,
                    ResourceConstants.FOLDER_SEPARATOR.getValue());
            File directory = new File(ResourceUtils.loadResource(directoryPath).toURI().getPath());

            String filePattern = StringUtils.substringAfter(cleanPath, ResourceConstants.FOLDER_SEPARATOR.getValue());
            while (filePattern.contains(ResourceConstants.FOLDER_SEPARATOR.getValue())) {
                filePattern = StringUtils.substringAfter(filePattern, ResourceConstants.FOLDER_SEPARATOR.getValue());
            }

            Set<URL> result = new LinkedHashSet<URL>(16);
            Iterator<File> iterator = FileUtils.iterateFiles(directory, new WildcardFileFilter(filePattern), null);
            while (iterator.hasNext()) {
                result.add(iterator.next().toURI().toURL());
            }

            return result.toArray(new URL[result.size()]);
        } else {
            // a single resource with the given name
            URL url = getDefaultClassLoader().getResource(cleanPath);
            // if (url == null) {
            // throw new UnsupportedOperationException(cleanPath);
            // }
            return url == null ? null : new URL[] { PathUtils.cleanPath(url) };
        }
    }

    private static URL[] load3(String locationPattern) throws MalformedURLException {
        if (StringUtils.contains(locationPattern, PunctuationConstants.STAR.getValue())
                || StringUtils.contains(locationPattern, PunctuationConstants.QUESTION_MARK.getValue())) {
            String directoryPath = StringUtils.substringBeforeLast(locationPattern,
                    ResourceConstants.FOLDER_SEPARATOR.getValue());
            directoryPath = StringUtils.substringAfter(directoryPath, ResourceConstants.FILE_URL_PREFIX.getValue());
            File directory = new File(directoryPath);
            String filePattern = StringUtils.substringAfter(locationPattern,
                    ResourceConstants.FOLDER_SEPARATOR.getValue());
            while (filePattern.contains(ResourceConstants.FOLDER_SEPARATOR.getValue())) {
                filePattern = StringUtils.substringAfter(filePattern, ResourceConstants.FOLDER_SEPARATOR.getValue());
            }

            Set<URL> result = new LinkedHashSet<URL>(16);
            Iterator<File> iterator = FileUtils.iterateFiles(directory, new WildcardFileFilter(filePattern), null);
            while (iterator.hasNext()) {
                result.add(iterator.next().toURI().toURL());
            }

            return result.toArray(new URL[result.size()]);
        } else {
            // a single resource with the given name
            URL url = new URL(locationPattern);
            return new File(url.getFile()).exists() ? new URL[] { url } : null;
        }
    }

    /**
     * 获取运行时classloader,首选线程上下文classloader,其次选择类classloader
     * <p>
     *
     * @author chen.chen.9, 2014-3-20
     * @return {@link ClassLoader}
     */
    public static ClassLoader getDefaultClassLoader() {
        if (classLoader != null) {
            return classLoader;
        }

        synchronized (BYTES) {
            if (classLoader == null) {
                ClassLoader tempClassLoader = null;
                try {
                    tempClassLoader = Thread.currentThread().getContextClassLoader();
                } catch (Exception ex) {
                    // Cannot access thread context ClassLoader - falling back
                    // to system class loader...
                }

                if (tempClassLoader == null) {
                    // No thread context class loader -> use class loader of
                    // this class.
                    tempClassLoader = ResourceUtils.class.getClassLoader();
                }

                classLoader = tempClassLoader;
            }
        }

        return classLoader;
    }
}