package org.testcontainers.utility;

import lombok.NonNull;
import org.slf4j.Logger;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import static org.slf4j.LoggerFactory.getLogger;

/**
 * Filesystem operation utility methods.
 */
public class PathUtils {

    private static final Logger log = getLogger(PathUtils.class);

    /**
     * Recursively delete a directory and all its subdirectories and files.
     * @param directory path to the directory to delete.
     */
    public static void recursiveDeleteDir(final @NonNull Path directory) {
        try {
            Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                    Files.delete(dir);
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    Files.delete(file);
                    return FileVisitResult.CONTINUE;
                }
            });
        } catch (IOException ignored) {
        }
    }

    /**
     * Make a directory, plus any required parent directories.
     *
     * @param directory the directory path to make
     */
    public static void mkdirp(Path directory) {
        boolean result = directory.toFile().mkdirs();
        if (!result) {
            throw new IllegalStateException("Failed to create directory at: " + directory);
        }
    }
    
    /**
     * Create a MinGW compatible path based on usual Windows path
     * @param path a usual windows path 
     * @return a MinGW compatible path
     */
    public static String createMinGWPath(String path) {
    	String mingwPath = path.replace('\\', '/');
    	int driveLetterIndex = 1;
        if(mingwPath.matches("^[a-zA-Z]:\\/.*")) {
        	driveLetterIndex = 0;
        }
        
        // drive-letter must be lower case
        mingwPath = "//" + Character.toLowerCase(mingwPath.charAt(driveLetterIndex)) +
        		mingwPath.substring(driveLetterIndex+1);
        mingwPath = mingwPath.replace(":","");
        return mingwPath;
    }

    /**
     * Extract a file or directory tree from a JAR file to a temporary location.
     * This allows Docker to mount classpath resources as files.
     * @param hostPath the path on the host, expected to be of the format 'file:/path/to/some.jar!/classpath/path/to/resource'
     * @return the path of the temporary file/directory
     */
    public static String extractClassPathResourceToTempLocation(final String hostPath) {
        File tmpLocation = new File(".testcontainers-tmp-" + Base58.randomString(5));
        //noinspection ResultOfMethodCallIgnored
        tmpLocation.delete();

        String jarPath = hostPath.replaceFirst("file:", "").replaceAll("!.*", "");
        String internalPath = hostPath.replaceAll("[^!]*!/", "");

        try (JarFile jarFile = new JarFile(jarPath)) {
            Enumeration<JarEntry> entries = jarFile.entries();

            while (entries.hasMoreElements()) {
                JarEntry entry = entries.nextElement();
                final String name = entry.getName();
                if (name.startsWith(internalPath)) {
                    log.debug("Copying classpath resource(s) from {} to {} to permit Docker to bind",
                            hostPath,
                            tmpLocation);
                    copyFromJarToLocation(jarFile, entry, internalPath, tmpLocation);
                }
            }

        } catch (IOException e) {
            throw new RuntimeException("Failed to process JAR file when extracting classpath resource: " + hostPath, e);
        }

        return tmpLocation.getAbsolutePath();
    }


    @SuppressWarnings("ResultOfMethodCallIgnored")
    private static void copyFromJarToLocation(final JarFile jarFile,
                                              final JarEntry entry,
                                              final String fromRoot,
                                              final File toRoot) {

        String destinationName = entry.getName().replaceFirst(fromRoot, "");
        File newFile = new File(toRoot, destinationName);

        if (!entry.isDirectory()) {
            // Create parent directories
            newFile.mkdirs();
            newFile.delete();
            newFile.deleteOnExit();

            try (InputStream is = jarFile.getInputStream(entry)) {
                Files.copy(is, newFile.toPath());
            } catch (IOException e) {
                 throw new RuntimeException("Failed to extract classpath resource " + entry.getName() + " from JAR file " + jarFile.getName(), e);
            }
        }
    }
}

@@ -181,118 +181,9 @@
 ile.
-FileVisitResult;%0Aimport java.nio.file.Files;%0Aimport java.nio.file.Path;%0Aimport java.nio.file.SimpleFileVisitor
+*
 ;%0Aim
@@ -3962,32 +3962,143 @@
  e);%0A        %7D%0A%0A
+        // Mark temporary files/dirs for deletion at JVM shutdown%0A        deleteOnExit(tmpLocation.toPath());%0A%0A
         return t
@@ -4134,17 +4134,16 @@
 %0A    %7D%0A%0A
-%0A
     @Sup
@@ -4932,33 +4932,32 @@
                 
- 
 throw new Runtim
@@ -5099,11 +5099,990 @@
  %7D%0A    %7D
+%0A%0A    public static void deleteOnExit(final Path path) %7B%0A        Runtime.getRuntime().addShutdownHook(new Thread(() -%3E deleteRecursively(path)));%0A    %7D%0A%0A%0A    public static void deleteRecursively(final Path path) %7B%0A        if (! Files.exists(path)) %7B%0A            return;%0A        %7D%0A%0A        try %7B%0A            Files.walkFileTree(path, new SimpleFileVisitor%3CPath%3E() %7B%0A%0A                @Override%0A                public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException %7B%0A                    Files.deleteIfExists(dir);%0A                    return super.postVisitDirectory(dir, exc);%0A                %7D%0A%0A                @Override%0A                public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException %7B%0A                    Files.deleteIfExists(file);%0A                    return super.visitFile(file, attrs);%0A                %7D%0A%0A            %7D);%0A        %7D catch (IOException ignored) %7B%0A        %7D%0A    %7D
 %0A%7D%0A

Add fully functional cleanup code to remove temporary copies of classpath resources that have been extracted from JAR files.