package net.techcable.sonarpet;

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;

import net.techcable.sonarpet.maven.LocalRepository;

/**
 * A shutdown hook that recursively deletes registered files.
 *
 * Can be used to cleanup temporary directories and resources.
 * Although failures will be logged, failure of one deletion won't affect others.
 */
public class RecursiveDeleteOnExitHook implements Runnable {
    @Nullable
    private static RecursiveDeleteOnExitHook INSTANCE;
    private final List<Path> toDelete = new ArrayList<>();

    @Override
    public void run() {
        // NOTE: Iterate backwards so last added is first deleted
        // For some reason this is important (see JDK DeleteOnExitHook(
        for (int i = toDelete.size() - 1; i >= 0; i--) {
            Path target = toDelete.get(i);
            try {
                if (Files.isDirectory(target, LinkOption.NOFOLLOW_LINKS)) {
                    Files.walkFileTree(target, new RecursiveDirectoryDeleter());
                } else {
                    Files.deleteIfExists(target);
                }
            } catch (IOException e) {
                //noinspection UseOfSystemOutOrSystemErr - VM is shutting down
                System.err.println("Unable to delete " + toDelete + ": " + e);
            }
        }
    }

    public static synchronized void addTarget(Path toDelete) {
        RecursiveDeleteOnExitHook hook;
        if ((hook = INSTANCE) == null) {
            hook = new RecursiveDeleteOnExitHook();
            Runtime.getRuntime().addShutdownHook(new Thread(hook));
            INSTANCE = hook;
        }
        hook.toDelete.add(toDelete);
    }

    private static class RecursiveDirectoryDeleter extends SimpleFileVisitor<Path> {
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
            Files.deleteIfExists(file);
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
            if (exc != null) throw exc;
            Files.deleteIfExists(dir);
            return FileVisitResult.CONTINUE;
        }
    }
}