package jnet.lib.snmp;

import org.snmp4j.*;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.*;
import org.snmp4j.transport.DefaultUdpTransportMapping;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class SNMPDeviceInfo {

    private Snmp snmp;
    private String address;
    private String community;
    private int version;

    // SNMP OID pro základní informace
    private static final OID SYS_NAME_OID = new OID("1.3.6.1.2.1.1.5.0");
    private static final OID SYS_DESCR_OID = new OID("1.3.6.1.2.1.1.1.0");
    private static final OID SYS_UPTIME_OID = new OID("1.3.6.1.2.1.1.3.0");
    private static final OID SYS_CONTACT_OID = new OID("1.3.6.1.2.1.1.4.0");
    private static final OID SYS_LOCATION_OID = new OID("1.3.6.1.2.1.1.6.0");
    private static final OID SYS_OBJECT_ID_OID = new OID("1.3.6.1.2.1.1.2.0");

    // OID databáze vyrobcu
    private static final Map<String, String> DEVICE_OID_MAP = new HashMap<>();

    static {
        DEVICE_OID_MAP.put("1.3.6.1.4.1.9", "Cisco");
        DEVICE_OID_MAP.put("1.3.6.1.4.1.11", "Hewlett-Packard (HP)");
        DEVICE_OID_MAP.put("1.3.6.1.4.1.14988", "Mikrotik");
        DEVICE_OID_MAP.put("1.3.6.1.4.1.41112.1.3.1", "Ubiquiti Networks");
        DEVICE_OID_MAP.put("1.3.6.1.4.1.259.10.1.43.101", "EdgeCore");
        DEVICE_OID_MAP.put("1.3.6.1.4.1.11863.5.1048577", "TP-Link");
//        DEVICE_OID_MAP.put("1.3.6.1.4.1.43", "Lexmark");
//        DEVICE_OID_MAP.put("1.3.6.1.4.1.311", "Microsoft");
//        DEVICE_OID_MAP.put("1.3.6.1.4.1.3224", "Fortinet");
//        DEVICE_OID_MAP.put("1.3.6.1.4.1.8072", "Net-SNMP");
//        DEVICE_OID_MAP.put("1.3.6.1.4.1.2021", "Linux Host");
//        DEVICE_OID_MAP.put("1.3.6.1.4.1.171", "D-Link");
//        DEVICE_OID_MAP.put("1.3.6.1.4.1.12148", "MikroTik");
//        DEVICE_OID_MAP.put("1.3.6.1.4.1.2636", "Juniper Networks");
//        DEVICE_OID_MAP.put("1.3.6.1.4.1.674", "Dell");
//        DEVICE_OID_MAP.put("1.3.6.1.4.1.2352", "Allied Telesis");
//        DEVICE_OID_MAP.put("1.3.6.1.4.1.807", "IBM");
//        DEVICE_OID_MAP.put("1.3.6.1.4.1.562", "Alcatel");
//        DEVICE_OID_MAP.put("1.3.6.1.4.1.14988", "Ubiquiti Networks");
//        DEVICE_OID_MAP.put("1.3.6.1.4.1.22610", "Ruckus Wireless");
//        DEVICE_OID_MAP.put("1.3.6.1.4.1.318", "APC (Schneider Electric)");
//        DEVICE_OID_MAP.put("1.3.6.1.4.1.2011", "Huawei");
//        DEVICE_OID_MAP.put("1.3.6.1.4.1.4826", "Opengear");
    }

    public SNMPDeviceInfo(String ip, String port, String community, int version) throws IOException {
        this.address = ip + "/" + port;  // Přidání SNMP portu
        this.community = community;
        this.version = version;
        
        TransportMapping<UdpAddress> transport = new DefaultUdpTransportMapping();
        snmp = new Snmp(transport);
        transport.listen();
    }

    /**
     * Získání základních informací o zařízení.
     */
    public DeviceInfo getDeviceInfo() throws IOException {
        DeviceInfo di = new DeviceInfo(
                getAsString(SYS_NAME_OID),
                getAsString(SYS_DESCR_OID),
                getAsString(SYS_UPTIME_OID),
                getAsString(SYS_CONTACT_OID),
                getAsString(SYS_LOCATION_OID),
                getDeviceVendor()
        );

        return di;
    }

    /**
     * Získá sysObjectID a přeloží na výrobce
     *
     * @return Výrobce nebo "Neznámý" + OID
     * @throws IOException
     */
    public String getDeviceVendor() throws IOException {
        String sysObjectID = getAsString(SYS_OBJECT_ID_OID);

        // Překlad OID na výrobce
        for (String oidPrefix : DEVICE_OID_MAP.keySet()) {
            if (sysObjectID.startsWith(oidPrefix)) {
                return DEVICE_OID_MAP.get(oidPrefix);
            }
        }

        return "Neznámý výrobce (" + sysObjectID + ")";
    }

    /**
     * Získání hodnoty SNMP OID jako String.
     */
    private String getAsString(OID oid) throws IOException {
        CommunityTarget target = createTarget();
        PDU pdu = new PDU();
        pdu.add(new VariableBinding(oid));
        pdu.setType(PDU.GET);

        ResponseEvent event = snmp.get(pdu, target);
        if (event != null && event.getResponse() != null) {
            return event.getResponse().get(0).getVariable().toString();
        } else {
            return "No response";
        }
    }

    /**
     * Převede SNMP sysUpTime (setiny sekundy) na čitelný formát D HH:MM:SS
     *
     * @param sysUpTime setiny sekundy (centiseconds)
     * @return čitelný uptime
     */
    private static String formatUptime(String sysUpTime) {
        System.out.println("jnet.lib.snmp.SNMPDeviceInfo.formatUptime() " +sysUpTime);
        try {
            long centiseconds = Long.parseLong(sysUpTime);
            long totalSeconds = centiseconds / 100;
            long days = totalSeconds / 86400;
            long hours = (totalSeconds % 86400) / 3600;
            long minutes = (totalSeconds % 3600) / 60;
            long seconds = totalSeconds % 60;

            return String.format("%d dní, %02d:%02d:%02d", days, hours, minutes, seconds);
        } catch (NumberFormatException e) {
            return "Neplatný uptime";
        }
    }

    /**
     * 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));
        target.setVersion(this.version);
        target.setRetries(2);
        target.setTimeout(1500);
        return target;
    }

}
