package org.xbib.classloader.jar;

import org.xbib.classloader.AbstractResourceHandle;
import org.xbib.classloader.ResourceHandle;
import org.xbib.classloader.url.AbstractURLResourceLocation;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipException;

public class JarResourceLocation extends AbstractURLResourceLocation {

    private JarFile jarFile;

    private byte content[];

    public JarResourceLocation(URL codeSource, File cacheFile) throws IOException {
        super(codeSource);
        try {
            jarFile = new JarFile(cacheFile);
        } catch (ZipException ze) {
            // We get this exception on windows when the
            // path to the jar file gets too long (Bug ID: 6374379)
            InputStream is = null;
            try {
                is = new FileInputStream(cacheFile);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                byte[] buffer = new byte[2048];
                int bytesRead = -1;
                while ((bytesRead = is.read(buffer)) != -1) {
                    baos.write(buffer, 0, bytesRead);
                }
                this.content = baos.toByteArray();
            } finally {
                if (is != null) {
                    is.close();
                }
            }
        }
    }

    public ResourceHandle getResourceHandle(String resourceName) {
        if (jarFile != null) {
            JarEntry jarEntry = jarFile.getJarEntry(resourceName);
            if (jarEntry != null) {
                try {
                    URL url = new URL(getCodeSource(), resourceName);
                    return new JarResourceHandle(jarFile, jarEntry, getCodeSource());
                } catch (MalformedURLException e) {
                    // ignore
                }
            }
        } else {
            try {
                final JarInputStream is = new JarInputStream(new ByteArrayInputStream(this.content));
                JarEntry jarEntry = null;
                while ((jarEntry = is.getNextJarEntry()) != null) {
                    if (jarEntry.getName().equals(resourceName)) {
                        try {
                            URL url = new URL(getCodeSource(), resourceName);
                            return new JarEntryResourceHandle(jarEntry, is);
                        } catch (MalformedURLException e) {
                            // skip
                        }
                    }
                }
            } catch (IOException e) {
                // ignore
            }
        }
        return null;
    }

    public Manifest getManifest() throws IOException {
        if (jarFile != null) {
            return jarFile.getManifest();
        } else {
            try {
                JarInputStream is = new JarInputStream(new ByteArrayInputStream(this.content));
                return is.getManifest();
            } catch (IOException e) {
            }
        }
        return null;
    }

    @Override
    public void close() {
        if (jarFile != null) {
            if (jarFile != null) {
                try {
                    jarFile.close();
                } catch (Exception ignored) {
                }
            }
        }
    }

    private class JarEntryResourceHandle extends AbstractResourceHandle {

        private final JarEntry jarEntry2;
        private final JarInputStream is;

        public JarEntryResourceHandle(JarEntry jarEntry2, JarInputStream is) {
            this.jarEntry2 = jarEntry2;
            this.is = is;
        }

        public String getName() {
            return jarEntry2.getName();
        }

        public URL getUrl() {
            try {
                return new URL("jar", "", -1, getCodeSource() + "!/" + jarEntry2.getName());
            } catch (MalformedURLException e) {
                throw new RuntimeException(e);
            }
        }

        public boolean isDirectory() {
            return jarEntry2.isDirectory();
        }

        public URL getCodeSourceUrl() {
            return getCodeSource();
        }

        public InputStream getInputStream() throws IOException {
            return is;
        }

        public int getContentLength() {
            return (int) jarEntry2.getSize();
        }
    }
}