package jnet.server.probe;

import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import io.netty.util.TimerTask;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jnet.lib.LogFile;
import jnet.lib.Status;
import jnet.lib.object.Map;
import jnet.lib.object.MapObject;
import jnet.server.Server;

public class NettyPing extends Thread {

    private static final HashedWheelTimer TIMER = new HashedWheelTimer(); // Netty časovač
    private static final Set<MapObject> activeHosts = ConcurrentHashMap.newKeySet(); // Dynamická správa hostů

    public NettyPing() {
    }

//    @Override
//    public void run() {
//        LogFile.printInfo("Ping probe start");
//        for (Map map : Server.maps) {
//            for (MapObject mapObject : map.getObjects()) {
//                schedulePing(mapObject);
//            }
//        }
//    }
    @Override
    public void run() {
        LogFile.printInfo("Ping probe start");
        TIMER.newTimeout(new TimerTask() {
            @Override
            public void run(Timeout timeout) {
                updateHosts(); // Pravidelně kontroluje `Server.maps` a pinguje aktivní hosty
                timeout.timer().newTimeout(this, Server.config.getInt("ping_repeat"), TimeUnit.SECONDS);
            }
        }, 0, TimeUnit.SECONDS);
    }

    private static void schedulePing(MapObject object) {
        TIMER.newTimeout(new TimerTask() {
            @Override
            public void run(Timeout timeout) {
                pingHost(object);
                timeout.timer().newTimeout(this, Server.config.getInt("ping_repeat"), TimeUnit.SECONDS); // Opakuje každých x sekund
            }
        }, 0, TimeUnit.SECONDS);
    }

    private static void updateHosts() {
        Set<MapObject> currentHosts = new HashSet<>();

        // Procházení všech hostů v Server.maps
        for (Map map : Server.maps) {
            for (MapObject object : map.getObjects()) {
                if (object.isActive()) {
                    currentHosts.add(object);
                }
            }
        }

        // Přidání nových hostů k monitorování
        for (MapObject obj : currentHosts) {
            if (activeHosts.add(obj)) { // Přidá pouze pokud tam ještě není
                LogFile.printInfo("Pridan host: " + obj.getIp());
            }
        }

        // Odebrání hostů, které už nejsou v Server.maps
        activeHosts.removeIf(obj -> {
            if (!currentHosts.contains(obj)) {
                LogFile.printInfo("Odebran host: " + obj.getIp());
                return true;
            }
            return false;
        });

        // Ping všech aktivních hostů
        for (MapObject obj : activeHosts) {
            pingHost(obj);
        }
    }

    private static void pingHost(MapObject object) {
        try {
            String command = System.getProperty("os.name").startsWith("Windows")
                    ? "ping -n 1 -w " + String.valueOf((Server.config.getInt("ping_timeout") * 1000)) + " " + object.getIp() // Timeout 2000 ms (Windows)
                    : "ping -c 1 -W " + String.valueOf(Server.config.getInt("ping_timeout")) + " " + object.getIp();    // Timeout 2 sec (Linux)

            Process process = Runtime.getRuntime().exec(command);
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));

            String line;
            boolean success = false;
            int timeMs = -1;

            while ((line = reader.readLine()) != null) {
                if (line.contains("TTL=") || line.contains("bytes from")) {
                    success = true;
                    timeMs = extractTime(line);
                }
            }

            //System.out.println("Ping na " + object.getIp() + (success ? " uspesny! RTT: " + timeMs + " ms" : " selhal!"));
            if (success) {
                if (object.getStatus() != Status.OK) {
                    PingResult.stateOnline(object);
                }
            } else {
                PingResult.stateOffline(object);
            }

            process.waitFor();
        } catch (Exception e) {
            PingResult.stateOffline(object);
            LogFile.printErr("Chyba pri pingu na " + object.getIp() + " " + e.getMessage());
        }
    }

    private static int extractTimeV1(String line) {
        String[] parts = line.split(" ");
        for (String part : parts) {
            if (part.contains("time=") || part.contains("čas=")) {
                return Integer.parseInt(part.replaceAll("[^0-9]", ""));
            }
        }
        return -1;
    }

    private static int extractTime(String line) {
        // Regulární výrazy pro Windows i Linux formát
        Pattern pattern = Pattern.compile("time[=<](\\d+)");
        Matcher matcher = pattern.matcher(line);

        if (matcher.find()) {
            return Integer.parseInt(matcher.group(1));
        }
        return -1; // Pokud se nepodaří najít čas
    }

}
