use std::net::Ipv4Addr;
use pnet::packet::Packet;
use crate::datalink::MacAddr;
use crate::packet::ethernet::EtherType;
pub const ARP_HEADER_LEN: usize = 28;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ArpOperation{
    Request = 1,
    Reply = 2,
    RarpRequest = 3,
    RarpReply = 4,
    InRequest = 8,
    InReply = 9,
    Nak = 10,
}
impl ArpOperation {
    pub fn from_u16(n: u16) -> Option<ArpOperation> {
        match n {
            1 => Some(ArpOperation::Request),
            2 => Some(ArpOperation::Reply),
            3 => Some(ArpOperation::RarpRequest),
            4 => Some(ArpOperation::RarpReply),
            8 => Some(ArpOperation::InRequest),
            9 => Some(ArpOperation::InReply),
            10 => Some(ArpOperation::Nak),
            _ => None,
        }
    }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ArpHardwareType {
    Ethernet = 1,
    ExperimentalEthernet = 2,
    AmateurRadioAX25 = 3,
    ProteonProNETTokenRing = 4,
    Chaos = 5,
    IEEE802Networks = 6,
    ARCNET = 7,
    Hyperchannel = 8,
    Lanstar = 9,
    AutonetShortAddress = 10,
    LocalTalk = 11,
    LocalNet = 12,
    UltraLink = 13,
    SMDS = 14,
    FrameRelay = 15,
    AsynchronousTransmissionMode = 16,
    HDLC = 17,
    FibreChannel = 18,
    AsynchronousTransmissionMode2 = 19,
    SerialLine = 20,
    AsynchronousTransmissionMode3 = 21,
    MILSTD188220 = 22,
    Metricom = 23,
    IEEE13941995 = 24,
    MAPOS = 25,
    Twinaxial = 26,
    EUI64 = 27,
    HIPARP = 28,
    IPandARPoverISO78163 = 29,
    ARPSec = 30,
    IPsecTunnel = 31,
    InfiniBand = 32,
    TIA102Project25CommonAirInterface = 16384,
    WiegandInterface = 16385,
    PureIP = 16386,
    HWEXP1 = 65280,
    HWEXP2 = 65281,
    AEthernet = 65282,
}
impl ArpHardwareType {
    pub fn from_u16(n: u16) -> Option<ArpHardwareType> {
        match n {
            1 => Some(ArpHardwareType::Ethernet),
            2 => Some(ArpHardwareType::ExperimentalEthernet),
            3 => Some(ArpHardwareType::AmateurRadioAX25),
            4 => Some(ArpHardwareType::ProteonProNETTokenRing),
            5 => Some(ArpHardwareType::Chaos),
            6 => Some(ArpHardwareType::IEEE802Networks),
            7 => Some(ArpHardwareType::ARCNET),
            8 => Some(ArpHardwareType::Hyperchannel),
            9 => Some(ArpHardwareType::Lanstar),
            10 => Some(ArpHardwareType::AutonetShortAddress),
            11 => Some(ArpHardwareType::LocalTalk),
            12 => Some(ArpHardwareType::LocalNet),
            13 => Some(ArpHardwareType::UltraLink),
            14 => Some(ArpHardwareType::SMDS),
            15 => Some(ArpHardwareType::FrameRelay),
            16 => Some(ArpHardwareType::AsynchronousTransmissionMode),
            17 => Some(ArpHardwareType::HDLC),
            18 => Some(ArpHardwareType::FibreChannel),
            19 => Some(ArpHardwareType::AsynchronousTransmissionMode2),
            20 => Some(ArpHardwareType::SerialLine),
            21 => Some(ArpHardwareType::AsynchronousTransmissionMode3),
            22 => Some(ArpHardwareType::MILSTD188220),
            23 => Some(ArpHardwareType::Metricom),
            24 => Some(ArpHardwareType::IEEE13941995),
            25 => Some(ArpHardwareType::MAPOS),
            26 => Some(ArpHardwareType::Twinaxial),
            27 => Some(ArpHardwareType::EUI64),
            28 => Some(ArpHardwareType::HIPARP),
            29 => Some(ArpHardwareType::IPandARPoverISO78163),
            30 => Some(ArpHardwareType::ARPSec),
            31 => Some(ArpHardwareType::IPsecTunnel),
            32 => Some(ArpHardwareType::InfiniBand),
            16384 => Some(ArpHardwareType::TIA102Project25CommonAirInterface),
            16385 => Some(ArpHardwareType::WiegandInterface),
            16386 => Some(ArpHardwareType::PureIP),
            65280 => Some(ArpHardwareType::HWEXP1),
            65281 => Some(ArpHardwareType::HWEXP2),
            65282 => Some(ArpHardwareType::AEthernet),
            _ => None,
        }
    }
}
#[derive(Clone, Debug)]
pub struct ArpPacket {
    pub hardware_type: ArpHardwareType,
    pub protocol_type: EtherType,
    pub operation: ArpOperation,
    pub sender_hw_addr: MacAddr,
    pub sender_proto_addr: Ipv4Addr,
    pub target_hw_addr: MacAddr,
    pub target_proto_addr: Ipv4Addr,
    pub payload: Vec<u8>,
}
impl ArpPacket {
    pub(crate) fn from_pnet_packet(packet: pnet::packet::arp::ArpPacket) -> ArpPacket {
        ArpPacket {
            hardware_type: ArpHardwareType::from_u16(packet.get_hardware_type().0).unwrap(),
            protocol_type: EtherType::from_u16(packet.get_protocol_type().0).unwrap(),
            operation: ArpOperation::from_u16(packet.get_operation().0).unwrap(),
            sender_hw_addr: MacAddr::new(packet.get_sender_hw_addr().octets()),
            sender_proto_addr: packet.get_sender_proto_addr(),
            target_hw_addr: MacAddr::new(packet.get_target_hw_addr().octets()),
            target_proto_addr: packet.get_target_proto_addr(),
            payload: packet.payload().to_vec(),
        }
    }
    pub fn from_bytes(packet: &[u8]) -> ArpPacket {
        let arp_packet = pnet::packet::arp::ArpPacket::new(packet).unwrap();
        ArpPacket::from_pnet_packet(arp_packet)
    }
}
pub(crate) fn build_arp_packet(
    arp_packet: &mut pnet::packet::arp::MutableArpPacket,
    src_mac: MacAddr,
    dst_mac: MacAddr,
    src_ip: Ipv4Addr,
    dst_ip: Ipv4Addr,
) {
    arp_packet.set_hardware_type(pnet::packet::arp::ArpHardwareTypes::Ethernet);
    arp_packet.set_protocol_type(pnet::packet::ethernet::EtherTypes::Ipv4);
    arp_packet.set_hw_addr_len(6);
    arp_packet.set_proto_addr_len(4);
    arp_packet.set_operation(pnet::packet::arp::ArpOperations::Request);
    arp_packet.set_sender_hw_addr(pnet::datalink::MacAddr::from(src_mac.octets()));
    arp_packet.set_sender_proto_addr(src_ip);
    arp_packet.set_target_hw_addr(pnet::datalink::MacAddr::from(dst_mac.octets()));
    arp_packet.set_target_proto_addr(dst_ip);
}