package nxt.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public final class ThreadPool {

    private static ScheduledExecutorService scheduledThreadPool;
    private static Map<Runnable,Integer> backgroundJobs = new HashMap<>();
    private static List<Runnable> beforeStartJobs = new ArrayList<>();
    private static List<Runnable> lastBeforeStartJobs = new ArrayList<>();

    public static synchronized void runBeforeStart(Runnable runnable, boolean runLast) {
        if (scheduledThreadPool != null) {
            throw new IllegalStateException("Executor service already started");
        }
        if (runLast) {
            lastBeforeStartJobs.add(runnable);
        } else {
            beforeStartJobs.add(runnable);
        }
    }

    public static synchronized void scheduleThread(Runnable runnable, int delay) {
        if (scheduledThreadPool != null) {
            throw new IllegalStateException("Executor service already started, no new jobs accepted");
        }
        backgroundJobs.put(runnable, delay);
    }

    public static synchronized void start() {
        if (scheduledThreadPool != null) {
            throw new IllegalStateException("Executor service already started");
        }

        Logger.logDebugMessage("Running " + beforeStartJobs.size() + " tasks...");
        runAll(beforeStartJobs);
        beforeStartJobs = null;

        Logger.logDebugMessage("Running " + lastBeforeStartJobs.size() + " final tasks...");
        runAll(lastBeforeStartJobs);
        lastBeforeStartJobs = null;

        Logger.logDebugMessage("Starting " + backgroundJobs.size() + " background jobs");
        scheduledThreadPool = Executors.newScheduledThreadPool(backgroundJobs.size());
        for (Map.Entry<Runnable,Integer> entry : backgroundJobs.entrySet()) {
            scheduledThreadPool.scheduleWithFixedDelay(entry.getKey(), 0, entry.getValue(), TimeUnit.SECONDS);
        }
        backgroundJobs = null;
    }

    public static synchronized void shutdown() {
        Logger.logDebugMessage("Stopping background jobs...");
        shutdownExecutor(scheduledThreadPool);
        scheduledThreadPool = null;
        Logger.logDebugMessage("...Done");
    }

    public static void shutdownExecutor(ExecutorService executor) {
        executor.shutdown();
        try {
            executor.awaitTermination(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        if (! executor.isTerminated()) {
            Logger.logMessage("some threads didn't terminate, forcing shutdown");
            executor.shutdownNow();
        }
    }

    private static void runAll(List<Runnable> jobs) {
        List<Thread> threads = new ArrayList<>();
        for (Runnable runnable : jobs) {
            Thread thread = new Thread(runnable);
            thread.start();
            threads.add(thread);
        }
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    private ThreadPool() {} //never

}

@@ -418,23 +418,20 @@
 unnable,
-Integer
+Long
 %3E backgr
@@ -1054,24 +1054,192 @@
 nt delay) %7B%0A
+        scheduleThread(runnable, delay, TimeUnit.SECONDS);%0A    %7D%0A%0A    public static synchronized void scheduleThread(Runnable runnable, int delay, TimeUnit timeUnit) %7B%0A
         if (
@@ -1419,22 +1419,41 @@
 nnable, 
+timeUnit.toMillis(
 delay)
+)
 ;%0A    %7D%0A
@@ -2163,15 +2163,12 @@
 ble,
-Integer
+Long
 %3E en
@@ -2295,32 +2295,37 @@
 lue(), TimeUnit.
+MILLI
 SECONDS);%0A      

allow task scheduling in milliseconds too