package com.github.ulisesbocchio.jar.resources;

import lombok.extern.slf4j.Slf4j;

import org.springframework.core.io.Resource;
import org.springframework.util.ResourceUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Ulises Bocchio
 */
@Slf4j
public class JarResource implements Resource {

    private final Resource delegate;
    private final String extractPath;
    private static final Map<URL, File> FILE_REGISTRY = new ConcurrentHashMap<>();

    public JarResource(Resource resource) {
        this(resource, null);

    }

    public JarResource(Resource resource, String extractPath) {
        this.delegate = resource;
        this.extractPath = extractPath;
    }

    @Override
    public boolean exists() {
        return delegate.exists();
    }

    @Override
    public boolean isReadable() {
        return delegate.isReadable();
    }

    @Override
    public boolean isOpen() {
        return delegate.isOpen();
    }

    @Override
    public URL getURL() throws IOException {
        return delegate.getURL();
    }

    @Override
    public URI getURI() throws IOException {
        return delegate.getURI();
    }

    @Override
    public File getFile() throws IOException {
        URL resourceUrl = getURL();
        if(ResourceUtils.isJarURL(resourceUrl)) {
            File tempFile = FILE_REGISTRY.get(resourceUrl);
            if(tempFile == null || !tempFile.exists()) {
                log.debug("Extracting File for URL: {}", resourceUrl);
                tempFile = JarUtils.getFile(delegate, extractPath);
                FILE_REGISTRY.put(resourceUrl, tempFile);
            } else {
                log.debug("File found in registry for URL: {}", resourceUrl);
            }
            return new File(tempFile.toURI());
        }
        return delegate.getFile();
    }

    @Override
    public long contentLength() throws IOException {
        return getFile().length();
    }

    @Override
    public long lastModified() throws IOException {
        return getFile().lastModified();
    }

    @Override
    public Resource createRelative(String relativePath) throws IOException {
        return new JarResource(delegate.createRelative(relativePath), extractPath);
    }

    @Override
    public String getFilename() {
        return delegate.getFilename();
    }

    @Override
    public String getDescription() {
        return delegate.getDescription();
    }

    @Override
    public InputStream getInputStream() throws IOException {
        return delegate.getInputStream();
    }

    /**
     * This implementation returns the description of this resource.
     * @see #getDescription()
     */
    @Override
    public String toString() {
        return getDescription();
    }

    /**
     * This implementation compares description strings.
     * @see #getDescription()
     */
    @Override
    public boolean equals(Object obj) {
        return (obj == this ||
                (obj instanceof Resource && ((Resource) obj).getDescription().equals(getDescription())));
    }

    /**
     * This implementation returns the description's hash code.
     * @see #getDescription()
     */
    @Override
    public int hashCode() {
        return getDescription().hashCode();
    }

}