package TestingSystem.Common.ThreadsPlanner;
import Common.Global;
import Common.Utils.InterruptThread;

import java.util.LinkedHashMap;
import java.util.Vector;
public abstract class ThreadsPlanner {
    //-->
    protected Thread interruptThread = new InterruptThread(5000, () -> {
        try {
            Interrupt();
        } catch (Exception exception) {
            Global.Log.PrintException(exception);
        }
        System.exit(0);
        return null;
    });
    protected int maxKernels;
    protected int kernels;
    //---
    protected int threadMaxId = 0;
    protected int wait_ms;
    protected LinkedHashMap<Integer, Thread> threads = new LinkedHashMap<>();
    protected Vector<Integer> activeThreads = new Vector<>();
    protected Vector<Integer> waitingThreads = new Vector<>();
    //--
    public ThreadsPlanner(int wait_ms_in) {
        wait_ms = wait_ms_in;
    }
    public void setMaxKernels(int maxKernels_in) {
        maxKernels = maxKernels_in;
        kernels = maxKernels;
    }
    public String printThread(Integer id) {
        return "thread id = "+id;
    }
    public String getThreadsSummary() {
        Vector<String> lines = new Vector<>();
        lines.add("Planner summary:");
        lines.add("Waiting: " + waitingThreads.size());
        lines.add("Running: " + activeThreads.size());
        for (Integer id : activeThreads) {
            lines.add(printThread(id));
        }
        lines.add("");
        return String.join("\n", lines);
    }
    //--
    public void Start() {
        Global.Log.Print("Planner started");
        try {
            //--
            while (!waitingThreads.isEmpty() || !activeThreads.isEmpty()) {
                Global.Log.Print(getThreadsSummary());
                checkActiveThreads();
                tryStartThreads();
                Thread.sleep(wait_ms);
            }
            //--
        } catch (Exception exception) {
            Global.Log.PrintException(exception);
        } finally {
            Global.Log.Print("Planner finished");
            finalize();
        }
    }
    public void Interrupt() throws Exception {
    }
    protected void checkActiveThreads() throws Exception {
        Vector<Integer> toExclude = new Vector<>();
        //--
        for (int i : activeThreads) {
            Thread thread = threads.get(i);
            if (!thread.isAlive()) {
                toExclude.add(i);
                kernels++;
            }
        }
        activeThreads.removeAll(toExclude);
        //--
    }
    protected void tryStartThreads() throws Exception {
        Vector<Integer> toExclude = new Vector<>();
        //-
        for (int i : waitingThreads) {
            if (kernels > 0) {
                Thread thread = threads.get(i);
                thread.start();
                activeThreads.add(i);
                kernels--;
                toExclude.add(i);
            } else break;
        }
        waitingThreads.removeAll(toExclude);
    }
    protected void finalize() {
    }
    protected void addThread(Thread thread) {
        threads.put(threadMaxId, thread);
        waitingThreads.add(threadMaxId);
        threadMaxId++;
    }
}
