// Copyright 2016 Eivind Vegsundvåg
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


package ninja.eivind.hotsreplayuploader.files.tempwatcher;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.file.*;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.function.Consumer;

import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;

public class BattleLobbyWatcher implements TempWatcher {
    public static final String REPLAY_SERVER_BATTLELOBBY = "replay.server.battlelobby";
    public static final long DELAY = 250L;
    private static final Logger logger = LoggerFactory.getLogger(BattleLobbyWatcher.class);
    private final File heroesDirectory;
    private final FilenameFilter fileNameFilter;
    private Thread watcherThread;
    private Consumer<File> callback;

    public BattleLobbyWatcher(File heroesDirectory) {
        this.heroesDirectory = heroesDirectory;
        fileNameFilter = (dir, name) -> name.contains("TempWriteReplayP");
    }

    @Override
    public void start() {
        logger.info("BattleLobbyWatcher starting...");
        Path path = heroesDirectory.toPath();

        new Thread(getInstantFileChecker()).start();

        Runnable runnable = getRunnable(path);
        watcherThread = new Thread(runnable);
        watcherThread.start();
    }

    private Runnable getInstantFileChecker() {
        return () -> {
            try {
                LocalDateTime end = LocalDateTime.now().plus(10, ChronoUnit.SECONDS);
                while(LocalDateTime.now().isBefore(end)) {
                    File[] files = heroesDirectory.listFiles(fileNameFilter);
                    for (File file : files != null ? files : new File[0]) {
                        File target = new File(file, REPLAY_SERVER_BATTLELOBBY);
                        if (target.exists()) {
                            handleFile(target);
                            return;
                        }
                    }
                    Thread.sleep(DELAY);
                }
            } catch (InterruptedException ignored) {
            }
        };
    }

    private Runnable getRunnable(Path path) {
        return () -> {
            logger.info("BattleLobbyWatcher started");
            try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
                path.register(watchService, ENTRY_CREATE);
                while (true) {
                    WatchKey key = watchService.take();
                    key.pollEvents().forEach(event -> {
                        WatchEvent.Kind<?> kind = event.kind();
                        final WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;
                        final Path pathName = pathEvent.context();
                        logger.info("Received " + kind + " for path " + pathName);
                        if (kind == OVERFLOW) {
                            return;
                        }
                        File file = new File(path.toFile(), pathName.toString());
                        String fileName = file.getName();
                        if (fileNameFilter.accept(path.toFile(), fileName)) {
                            File target = new File(file, REPLAY_SERVER_BATTLELOBBY);
                            handleFile(target);
                        }
                    });
                    if (!key.reset()) {
                        break;
                    }
                }
            } catch (IOException e) {
                logger.error("Watcher threw exception", e);
            } catch (InterruptedException e) {
                logger.info("TempReplayWatcher was interrupted. Winding down.", e);
            }
        };
    }

    private void handleFile(File target) {
        logger.info("Discovered BattleLobby: {}", target);
        new Thread(() -> callback.accept(target)).start();
    }

    @Override
    public void stop() {
        logger.info("Game exited. Stopping BattleLobbyWatcher");
        watcherThread.interrupt();
        watcherThread = null;
    }

    @Override
    public void setCallback(Consumer<File> callback) {
        this.callback = callback;
    }
}

@@ -4516,31 +4516,212 @@
  -%3E 
-callback.accept(target)
+%7B%0A            try %7B%0A                Thread.sleep(250L);%0A                callback.accept(target);%0A            %7D catch (InterruptedException e) %7B%0A                logger.error(%22%22, e);%0A            %7D%0A        %7D
 ).st

fix file is read before it's ready