package cloudburst.rejects.utils.portscanner;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.*;
import java.util.function.Consumer;

public class PScanRunner {
    public boolean running = true;
    public int portsScanned = 0;
    ExecutorService es;
    List<Future<PortScannerManager.ScanResult>> futures = new ArrayList<>();
    Thread runner;

    public PScanRunner(InetAddress address, int threads, int threadDelay, int timeoutMS, Collection<Integer> ports,
                       Consumer<List<PortScannerManager.ScanResult>> callback) {
        runner = new Thread(() -> {
            es = Executors.newFixedThreadPool(threads);
            ports.forEach(port -> {
                futures.add(isPortOpen(es, address.getHostAddress(), port, timeoutMS, threadDelay));
            });
            try {
                boolean _1 = es.awaitTermination(200L, TimeUnit.MILLISECONDS);
            } catch (InterruptedException ignored) {
            }
            List<PortScannerManager.ScanResult> results = new ArrayList<>();
            for (Future<PortScannerManager.ScanResult> fsc : futures) {
                try {
                    results.add(fsc.get());
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            }
            callback.accept(results);
        });
        runner.start();
    }

    public void cancel() {
        running = false;
    }

    private Future<PortScannerManager.ScanResult> isPortOpen(ExecutorService es, String ip, int port, int timeout,
                                                             int delay) {
        return es.submit(() -> {
            if (!running)
                return new PortScannerManager.ScanResult(port, false);
            Thread.sleep(delay);
            portsScanned++;
            try {
                //System.out.println(ip + ":" + port);
                Socket socket = new Socket();
                socket.connect(new InetSocketAddress(ip, port), timeout);
                socket.close();
                return new PortScannerManager.ScanResult(port, true);
            } catch (Exception exc) {

                return new PortScannerManager.ScanResult(port, false);
            }
        });
    }
}
