package com.eden.orchid.api.server;

import com.caseyjbrooks.clog.Clog;
import com.eden.orchid.Orchid;
import com.eden.orchid.api.OrchidContext;
import com.sun.nio.file.SensitivityWatchEventModifier;

import javax.inject.Inject;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;

public class FileWatcher {

    private final OrchidContext context;
    private WatchService watcher;
    private Map<WatchKey, Path> keys;

    @Inject
    public FileWatcher(OrchidContext context) {
        this.context = context;
    }

    public void startWatching(String rootDir) {
        try {
            Path root = Paths.get(rootDir);
            watcher = FileSystems.getDefault().newWatchService();
            keys = new HashMap<>();
            registerAll(root);
            processEvents();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void register(Path dir) throws IOException {
        WatchKey key = dir.register(watcher, new WatchEvent.Kind[]{
                StandardWatchEventKinds.ENTRY_CREATE,
                StandardWatchEventKinds.ENTRY_DELETE,
                StandardWatchEventKinds.ENTRY_MODIFY
        }, SensitivityWatchEventModifier.HIGH);
        keys.put(key, dir);
    }

    private void registerAll(final Path start) throws IOException {
        Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
                    throws IOException {
                register(dir);
                return FileVisitResult.CONTINUE;
            }
        });
    }

    private void processEvents() {
        while (true) {

            WatchKey key;
            try {
                key = watcher.take();
            }
            catch (InterruptedException x) {
                return;
            }

            Path dir = keys.get(key);
            if (dir == null) {
                Clog.e("WatchKey not recognized!!");
                continue;
            }

            for (WatchEvent<?> event : key.pollEvents()) {
                WatchEvent.Kind kind = event.kind();

                if (kind == StandardWatchEventKinds.OVERFLOW) {
                    continue;
                }

                WatchEvent<Path> ev = (WatchEvent<Path>) event;
                Path name = ev.context();
                Path child = dir.resolve(name);

                context.broadcast(Orchid.Lifecycle.FilesChanged.fire(this));

                if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
                    try {
                        if (Files.isDirectory(child, LinkOption.NOFOLLOW_LINKS)) {
                            registerAll(child);
                        }
                    }
                    catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            boolean valid = key.reset();
            if (!valid) {
                keys.remove(key);
                if (keys.isEmpty()) {
                    break;
                }
            }
        }
    }

}

@@ -379,24 +379,66 @@
 LinkOption;%0A
+import java.nio.file.NoSuchFileException;%0A
 import java.
@@ -452,16 +452,16 @@
 e.Path;%0A
-
 import j
@@ -1258,28 +1258,71 @@
     
+%7D%0A
     
-processEvents();
+    catch (NoSuchFileException e) %7B%0A            // ignore
 %0A   
@@ -1392,32 +1392,58 @@
 ace();%0A        %7D
+%0A%0A        processEvents();
 %0A    %7D%0A%0A    priv
@@ -2357,28 +2357,16 @@
        %7D
-%0A           
  catch (
@@ -3295,32 +3295,32 @@
               %7D%0A
+
                 
@@ -3324,36 +3324,16 @@
        %7D
-%0A                   
  catch (

Fix bug in serve mode where missing source directory prevents app from being served