package jnet.lib.snmp;

import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
import org.snmp4j.smi.*;
import org.snmp4j.transport.DefaultUdpTransportMapping;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import org.snmp4j.CommunityTarget;

public class SNMPTrafficMonitor {

    private String address;
    private String port;
    private String community;
    private int version;
    private int interfaceIndex;
    
    private final List<TrafficListener> listeners = new ArrayList<>();
    private Snmp snmp;
    private Timer timer;
    
    private long prevInOctets = -1;
    private long prevOutOctets = -1;
    private long prevTimestamp = -1;

    // OIDy pro SNMP dotaz na traffic
    private static final String IF_IN_OCTETS = "1.3.6.1.2.1.2.2.1.10"; // Inbound traffic
    private static final String IF_OUT_OCTETS = "1.3.6.1.2.1.2.2.1.16"; // Outbound traffic

    public SNMPTrafficMonitor(String targetAddress, String port, String community, int version, int interfaceIndex) {
        
        this.address = targetAddress;
        this.port = port;
        this.community = community;
        this.version = version;
        this.interfaceIndex = interfaceIndex;
    }

    public void addTrafficListener(TrafficListener listener) {
        listeners.add(listener);
    }

    public void startMonitoring(long intervalMs) throws IOException {
        TransportMapping<UdpAddress> transport = new DefaultUdpTransportMapping();
        snmp = new Snmp(transport);
        transport.listen();

        timer = new Timer(true);
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                try {
                    queryTraffic();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }, 0, intervalMs);
    }

    public void stopMonitoring() throws IOException {
        if (timer != null) {
            timer.cancel();
        }
        if (snmp != null) {
            snmp.close();
        }
    }

    private void queryTraffic() throws IOException {
        PDU pdu = new PDU();
        pdu.add(new VariableBinding(new OID(IF_IN_OCTETS + "." + interfaceIndex)));
        pdu.add(new VariableBinding(new OID(IF_OUT_OCTETS + "." + interfaceIndex)));
        pdu.setType(PDU.GET);

        
        CommunityTarget target = createTarget();

        snmp.send(pdu, target, null, new ResponseListener() {
            @Override
            public void onResponse(ResponseEvent event) {
                PDU response = event.getResponse();
                if (response != null) {
                    long inOctets = response.get(0).getVariable().toLong();
                    long outOctets = response.get(1).getVariable().toLong();
                    long currentTimestamp = System.currentTimeMillis();

                    if (prevInOctets != -1 && prevOutOctets != -1) {
                        double elapsedSeconds = (currentTimestamp - prevTimestamp) / 1000.0;

                        long inBps = (long) ((inOctets - prevInOctets) / elapsedSeconds);
                        long outBps = (long) ((outOctets - prevOutOctets) / elapsedSeconds);

                        // Zavoláme listener s hodnotami v Bps (Byte per Second)
                        notifyListeners(inBps, outBps);
                    }

                    prevInOctets = inOctets;
                    prevOutOctets = outOctets;
                    prevTimestamp = currentTimestamp;
                }
            }
        });
    }

    private void notifyListeners(long inBps, long outBps) {
        for (TrafficListener listener : listeners) {
            listener.onTrafficUpdate(inBps, outBps);
        }
    }
    
    /**
     * Vytvoření SNMP cíle (target).
     */
    private CommunityTarget createTarget() {
        CommunityTarget target = new CommunityTarget();
        target.setCommunity(new OctetString(this.community));
        target.setAddress(new UdpAddress(this.address+"/"+this.port));
        target.setVersion(this.version);
        target.setRetries(2);
        target.setTimeout(1500);
        return target;
    }

    public interface TrafficListener {
        void onTrafficUpdate(long inBps, long outBps);
    }
}
