package jnet.server.probe;

import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import jnet.lib.LogFile;
import jnet.lib.Message;
import jnet.lib.object.Connection;
import jnet.lib.object.MapObject;
import jnet.lib.object.SnmpProfile;
import jnet.lib.snmp.SNMPTrafficMonitor;
import jnet.lib.snmp.SNMPTrafficMonitor.TrafficListener;
import jnet.server.Server;
import jnet.server.ServerMessageParser;

public class TrafficProbe extends Thread {

    private static final int TIME = 3; // kontrola každé 3 sekundy
    private final Map<Integer, TProbe> activeProbes = new HashMap<>();
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    @Override
    public void run() {
        LogFile.printInfo("Traffic probe start");
        scheduler.scheduleAtFixedRate(this::updateProbe, 0, TIME, TimeUnit.SECONDS);
    }

    private void updateProbe() {
        Set<Integer> currentProbeIds = new HashSet<>();

        // Projde všechny connections a zjistí, které je potřeba monitorovat
        for (jnet.lib.object.Map map : Server.maps) {
            for (Connection connection : map.getConnection()) {
                if (connection.isReadTraffic()) {
                    currentProbeIds.add(connection.getId());
                    if (!activeProbes.containsKey(connection.getId())) {
                        addProbe(connection);
                    }
                }
            }
        }

        // Odebrání neaktuálních probe
        activeProbes.keySet().removeIf(id -> !currentProbeIds.contains(id));
    }

    private void addProbe(Connection c) {
        try {
            String host = "";
            String port = "161";
            String community = "public";
            int version = 1;

            for (jnet.lib.object.Map map : Server.maps) {
                for (MapObject object : map.getObjects()) {
                    if (object.getId() == c.getSourceObj()) {
                        host = object.getIp();
                        SnmpProfile profile = Server.snmpProfile.stream()
                                .filter(p -> p.getId() == object.getSnmpProfile())
                                .findFirst()
                                .orElse(null);
                        if (profile != null) {
                            port = profile.getPort();
                            community = profile.getCommunityRead();
                            version = profile.getVersion();
                        }
                        break;
                    }
                }
            }

            SNMPTrafficMonitor monitor = new SNMPTrafficMonitor(host, port, community, version, c.getTrafficIface());
            monitor.addTrafficListener(new TrafficListener() {
                @Override
                public void onTrafficUpdate(long inBps, long outBps) {
                    Object[] o = {c.getId(), inBps, outBps};
                    ServerMessageParser.sendAll(Message.TRAFFIC, o);
                }
            });
            activeProbes.put(c.getId(), new TProbe(c, monitor));
            monitor.startMonitoring(2000);
            LogFile.printInfo("Traffic probe add: " + c.getId());
        } catch (IOException ex) {
            LogFile.printErr("Traffic probe error: " + ex.getMessage());
        }
    }

    public void stopProbe() {
        scheduler.shutdown();
        activeProbes.values().forEach(probe -> {
            try {
                probe.getMonitor().stopMonitoring();
            } catch (IOException e) {
                LogFile.printErr("Error stopping monitor for probe: " + probe.getConnection().getId() + " - " + e.getMessage());
            }
        });
        activeProbes.clear();
        LogFile.printInfo("Traffic probe stopped");
    }

    private static class TProbe {

        private final Connection connection;
        private final SNMPTrafficMonitor monitor;

        public TProbe(Connection connection, SNMPTrafficMonitor monitor) {
            this.connection = connection;
            this.monitor = monitor;
        }

        public Connection getConnection() {
            return connection;
        }

        public SNMPTrafficMonitor getMonitor() {
            return monitor;
        }
    }
}
