use core::{
    mem::{self, MaybeUninit},
    sync::atomic::{self, Ordering},
    time::Duration,
};

use alloc::{sync::Arc, vec::Vec};
use device_api::{
    device::{Device, DeviceInitContext},
    dma::DmaAllocator,
    interrupt::{InterruptHandler, IrqVector},
};
use futures_util::task::AtomicWaker;
use libk::{
    dma::{BusAddress, DmaBuffer},
    error::Error,
    task::runtime::{self, psleep, pwait},
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo, L3_PAGE_SIZE};
use libk_util::{event::BitmapEvent, sync::IrqSafeSpinlock, OneTimeInit};
use tock_registers::{
    interfaces::{ReadWriteable, Readable, Writeable},
    register_bitfields, register_structs,
    registers::{ReadOnly, ReadWrite, WriteOnly},
    LocalRegisterCopy,
};
use ygg_driver_net_core::{
    ephy::{MdioBus, PhyAccess, GBESR},
    interface::{NetworkDevice, NetworkInterfaceType},
    RxPacket,
};
use ygg_driver_pci::device::{PciDeviceInfo, PreferredInterruptMode};
use yggdrasil_abi::{
    bitflags,
    net::{
        link::{Duplex, EthernetLinkState, EthernetSpeed, LinkState},
        MacAddress,
    },
};

register_bitfields! {
    u8,
    CR [
        /// Request the MAC to stop its Tx/Rx activity
        STOP    OFFSET(7)   NUMBITS(1)  [],
        /// Software reset bit. Set by driver, cleared by device
        RST     OFFSET(4)   NUMBITS(1)  [],
        /// Rx enable
        RE      OFFSET(3)   NUMBITS(1)  [],
        /// Tx enable
        TE      OFFSET(2)   NUMBITS(1)  [],
    ],
    TPPOLL [
        HPQ     OFFSET(7)   NUMBITS(1)  [],
        NPQ     OFFSET(6)   NUMBITS(1)  [],
        FSWInt  OFFSET(0)   NUMBITS(1)  [],
    ],
    R9346CR [
        EEM     OFFSET(6)   NUMBITS(2)  [
            Normal = 0b00,
            ConfigWriteEnable = 0b11
        ],
    ],
    CONFIG2 [
        MSI     OFFSET(5)   NUMBITS(1)  [],
    ],
    PHYSTATUS [
        TXFLOW  OFFSET(6)   NUMBITS(1)  [],
        RXFLOW  OFFSET(5)   NUMBITS(1)  [],
        MF1000  OFFSET(4)   NUMBITS(1)  [],
        M100    OFFSET(3)   NUMBITS(1)  [],
        M10     OFFSET(2)   NUMBITS(1)  [],
        LINKSTS OFFSET(1)   NUMBITS(1)  [],
        FULLDUP OFFSET(0)   NUMBITS(1)  [],
    ],
}

register_bitfields! {
    u16,
    IMR_ISR [
        TIMEOUT OFFSET(14)  NUMBITS(1)  [],
        FEMP    OFFSET(9)   NUMBITS(1)  [],
        SWINT   OFFSET(8)   NUMBITS(1)  [],
        TDU     OFFSET(7)   NUMBITS(1)  [],
        FOVW    OFFSET(6)   NUMBITS(1)  [],
        LINKCHG OFFSET(5)   NUMBITS(1)  [],
        RDU     OFFSET(4)   NUMBITS(1)  [],
        TER     OFFSET(3)   NUMBITS(1)  [],
        TOK     OFFSET(2)   NUMBITS(1)  [],
        RER     OFFSET(1)   NUMBITS(1)  [],
        ROK     OFFSET(0)   NUMBITS(1)  [],
    ],
    CPCR [
        /// (rtl8169sc) PCI endianness mode
        ENDIAN      OFFSET(9)   NUMBITS(1)  [
            Little = 0,
            Big = 1,
        ],
        /// (no docs found) TBD
        MACSTATDIS  OFFSET(7)   NUMBITS(1)  [],
        /// Rx VLAN de-tagging enable
        RXVLAN      OFFSET(6)   NUMBITS(1)  [],
        /// Rx checksum offload enable
        RXCHKSUM    OFFSET(5)   NUMBITS(1)  [],
        /// PCI dual address cycle enable
        DAC         OFFSET(4)   NUMBITS(1)  [],
        /// PCI multiple read/write enable
        MULRW       OFFSET(3)   NUMBITS(1)  [],

        // Not documented, found in OpenBSD's re driver
        /// C+ Rx mode enable
        RXENB       OFFSET(1)   NUMBITS(1)  [],
        /// C+ Tx mode enable
        TXENB       OFFSET(0)   NUMBITS(1)  [],
    ],
}

register_bitfields! {
    u32,
    TCR [
        HWVERID0    OFFSET(28)  NUMBITS(2)  [],
        HWVERID1    OFFSET(26)  NUMBITS(1)  [],
        IFG         OFFSET(24)  NUMBITS(2)  [],
        HWVERID2    OFFSET(23)  NUMBITS(1)  [],
        TX_NOCRC    OFFSET(16)  NUMBITS(1)  [],
        QUEUE_EMPTY OFFSET(11)  NUMBITS(1)  [],
        MXDMA       OFFSET(8)   NUMBITS(3)  [
            Burst16 = 0b000,
            Burst32 = 0b001,
            Burst64 = 0b010,
            Burst128 = 0b011,
            Burst256 = 0b100,
            Burst512 = 0b101,
            Unlimited = 0b111,
        ],
    ],
    RCR [
        RXFTH       OFFSET(13)  NUMBITS(3)  [
            Threshold64 = 0b010,
            Threshold128 = 0b011,
            Threshold256 = 0b100,
            Threshold512 = 0b101,
            Threshold1024 = 0b110,
            NoThreshold = 0b111,
        ],
        EARLYOFFV2  OFFSET(11)  NUMBITS(1)  [],
        MXDMA       OFFSET(8)   NUMBITS(3)  [
            Burst64 = 0b010,
            Burst128 = 0b011,
            Burst256 = 0b100,
            Burst512 = 0b101,
            Burst1024 = 0b110,
            Unlimited = 0b111
        ],
        R9356SEL    OFFSET(6)   NUMBITS(1)  [],
        AER         OFFSET(5)   NUMBITS(1)  [],
        AR          OFFSET(4)   NUMBITS(1)  [],
        AB          OFFSET(3)   NUMBITS(1)  [],
        AM          OFFSET(2)   NUMBITS(1)  [],
        APM         OFFSET(1)   NUMBITS(1)  [],
        AAP         OFFSET(0)   NUMBITS(1)  [],
    ],
    PHYAR [
        FLAG        OFFSET(31)  NUMBITS(1)  [
            Read = 0,
            Write = 1,
        ],
        REGADDR     OFFSET(16)  NUMBITS(5)  [],
        DATA        OFFSET(0)   NUMBITS(16) [],
    ],
}

register_structs! {
    #[allow(non_snake_case)]
    Regs {
        (0x0000 => IDRn: [ReadWrite<u32>; 2]),
        (0x0008 => MARn: [ReadWrite<u32>; 2]),
        (0x0010 => DTCCR: ReadWrite<u64>),
        (0x0018 => _0),
        (0x0020 => TNPDS: [ReadWrite<u32>; 2]),
        (0x0028 => THPDS: [ReadWrite<u32>; 2]),
        (0x0030 => _1),
        (0x0037 => CR: ReadWrite<u8, CR::Register>),
        (0x0038 => TPPOLL: WriteOnly<u8, TPPOLL::Register>),
        (0x0039 => _2),
        (0x003C => IMR: ReadWrite<u16, IMR_ISR::Register>),
        (0x003E => ISR: ReadWrite<u16, IMR_ISR::Register>),
        (0x0040 => TCR: ReadWrite<u32, TCR::Register>),
        (0x0044 => RCR: ReadWrite<u32, RCR::Register>),
        (0x0048 => TCTR: ReadWrite<u32>),
        (0x004C => MPKT: ReadWrite<u32>),
        (0x0050 => R9346CR: ReadWrite<u8, R9346CR::Register>),
        (0x0051 => CONFIG0: ReadWrite<u8>),
        (0x0052 => CONFIG1: ReadWrite<u8>),
        (0x0053 => CONFIG2: ReadWrite<u8, CONFIG2::Register>),
        (0x0054 => CONFIG3: ReadWrite<u8>),
        (0x0055 => CONFIG4: ReadWrite<u8>),
        (0x0056 => CONFIG5: ReadWrite<u8>),
        (0x0057 => _4),
        (0x0058 => TIMERINT: ReadWrite<u32>),
        (0x005C => _5),
        (0x0060 => PHYAR: ReadWrite<u32, PHYAR::Register>),
        (0x0064 => _6),
        (0x006C => PHYSTATUS: ReadOnly<u8, PHYSTATUS::Register>),
        (0x006D => _7),
        (0x0082 => LDPS: ReadWrite<u8>),
        (0x0083 => _13),
        (0x0084 => WAKEUPn: [ReadWrite<u32>; 16]),
        (0x00C4 => CRCn: [ReadWrite<u16>; 5]),
        (0x00CE => _8),
        (0x00DA => RMS: ReadWrite<u16>),
        (0x00DC => _9),
        (0x00E0 => CPCR: ReadWrite<u16, CPCR::Register>),
        (0x00E2 => _10),
        (0x00E4 => RDSAR: [ReadWrite<u32>; 2]),
        (0x00EC => MTPS: ReadWrite<u8>),
        (0x00ED => _11),
        (0x00F0 => MISC: ReadWrite<u32>),
        (0x00F4 => _12),
        (0x0100 => @END),
    }
}

bitflags! {
    struct ChipFlags: u32 {
        const MACSTAT: bit 0;
        const NEED_PHY_RESET: bit 1;
        const RXDV_GATED: bit 2;
        const EARLYOFFV2: bit 3;
    }
}

#[derive(Clone, Copy, PartialEq, Debug)]
#[allow(non_camel_case_types)]
enum Revision {
    rtl8168gu,
    rtl8168h,
    other(u32),
}

#[repr(C)]
struct Descriptor {
    cmd: u32,
    vlan_cmd: u32,
    address: BusAddress,
}

struct RxRing {
    entries: DmaBuffer<[Descriptor]>,
    buffers: Vec<DmaBuffer<[MaybeUninit<u8>]>>,
    rd: usize,
}

struct TxRing {
    entries: DmaBuffer<[Descriptor]>,
    buffers: Vec<Option<DmaBuffer<[u8]>>>,
    wr: usize,
    rd: usize,
}

pub struct Rtl8168 {
    regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
    mac: MacAddress,
    pci: PciDeviceInfo,

    nic: OneTimeInit<u32>,
    rx: OneTimeInit<IrqSafeSpinlock<RxRing>>,
    tx: OneTimeInit<IrqSafeSpinlock<TxRing>>,

    events: BitmapEvent<AtomicWaker>,

    dma: Arc<dyn DmaAllocator>,
}

impl RxRing {
    pub fn with_capacity(dma: &dyn DmaAllocator, capacity: usize) -> Result<Self, Error> {
        let buffers = (0..capacity)
            .map(|_| DmaBuffer::new_uninit_slice(dma, L3_PAGE_SIZE))
            .collect::<Result<Vec<_>, Error>>()?;
        let entries = DmaBuffer::new_slice_with(
            dma,
            |i| Descriptor::new_rx(buffers[i].bus_address(), i == capacity - 1),
            capacity,
        )?;
        Ok(Self {
            entries,
            buffers,
            rd: 0,
        })
    }

    pub fn consume<F: Fn(DmaBuffer<[u8]>)>(
        &mut self,
        dma: &dyn DmaAllocator,
        handler: F,
    ) -> Result<usize, Error> {
        let mut count = 0;
        loop {
            let index = self.rd % self.entries.len();
            let entry = &self.entries[index];

            if entry.is_host_owned() {
                let new_buffer = DmaBuffer::new_uninit_slice(dma, 4096)?;
                let new_buffer_address = new_buffer.bus_address();
                let buffer = mem::replace(&mut self.buffers[index], new_buffer);
                let buffer = unsafe { DmaBuffer::assume_init_slice(buffer) };
                handler(buffer);

                self.entries[index].setup_rx(new_buffer_address);

                self.rd = self.rd.wrapping_add(1);
                count += 1;
            } else {
                break;
            }
        }
        Ok(count)
    }

    pub fn base_address(&self) -> BusAddress {
        self.entries.bus_address()
    }
}

impl TxRing {
    pub fn with_capacity(dma: &dyn DmaAllocator, capacity: usize) -> Result<Self, Error> {
        let entries =
            DmaBuffer::new_slice_with(dma, |i| Descriptor::empty_tx(i == capacity - 1), capacity)?;
        let buffers = (0..capacity).map(|_| None).collect();

        Ok(Self {
            entries,
            buffers,
            wr: 0,
            rd: 0,
        })
    }

    pub fn is_full(&self) -> bool {
        self.wr.wrapping_add(1) % self.entries.len() == self.rd % self.entries.len()
    }

    pub fn push(&mut self, packet: DmaBuffer<[u8]>) -> Result<(), Error> {
        let packet_base = packet.bus_address();
        let packet_size = packet.len();
        let capacity = self.entries.len();

        // TODO packet size checks

        if self.is_full() {
            log::warn!("rtl81xx: tx ring full: wr={}, rd={}", self.wr, self.rd);
            return Err(Error::WouldBlock);
        }

        let index = self.wr % capacity;
        packet.cache_flush_all(true);
        self.buffers[index] = Some(packet);
        self.entries[index].setup_tx(packet_base, packet_size, index == capacity - 1);
        self.entries.cache_flush_element(index, true);
        self.entries[index].cmd |= Descriptor::CMD_OWN;
        self.entries.cache_flush_element(index, true);

        self.wr = self.wr.wrapping_add(1);

        Ok(())
    }

    pub fn consume(&mut self) -> Result<usize, Error> {
        let mut count = 0;
        loop {
            let index = self.rd % self.entries.len();
            let entry = &mut self.entries[index];

            if self.rd != self.wr && entry.is_host_owned() {
                self.rd = self.rd.wrapping_add(1);
                count += 1;
            } else {
                break;
            }
        }
        Ok(count)
    }

    pub fn base_address(&self) -> BusAddress {
        self.entries.bus_address()
    }
}

impl Descriptor {
    // Descriptor owned by DMA
    const CMD_OWN: u32 = 1 << 31;
    // Last descriptor in the ring
    const CMD_END: u32 = 1 << 30;
    // Last segment of a packet
    const CMD_LS: u32 = 1 << 28;
    // First segment of a packet
    const CMD_FS: u32 = 1 << 29;

    pub fn new_rx(buffer: BusAddress, last: bool) -> Self {
        let mut cmd = Self::CMD_OWN | 0x1000;
        if last {
            cmd |= Self::CMD_END;
        }
        Self {
            cmd,
            vlan_cmd: 0,
            address: buffer,
        }
    }

    pub fn empty_tx(last: bool) -> Self {
        let cmd = if last { Self::CMD_END } else { 0 };
        Self {
            cmd,
            vlan_cmd: 0,
            address: BusAddress::ZERO,
        }
    }

    pub fn setup_rx(&mut self, buffer: BusAddress) {
        let cmd = self.cmd;
        self.address = buffer;
        self.vlan_cmd = 0;
        unsafe {
            atomic::fence(Ordering::Release);
            core::ptr::write_volatile(&mut self.cmd, cmd | Self::CMD_OWN);
        }
    }

    pub fn setup_tx(&mut self, buffer: BusAddress, size: usize, last: bool) {
        let mut cmd = Self::CMD_FS | Self::CMD_LS | (size as u32);
        if last {
            cmd |= Self::CMD_END;
        }
        self.address = buffer;
        self.vlan_cmd = 0;
        self.cmd = cmd;
    }

    pub fn is_host_owned(&self) -> bool {
        self.cmd & Self::CMD_OWN == 0
    }
}

impl Regs {
    const TCR_REVISION_MASK: u32 = 0x7C800000;

    pub fn get_link_state(&self) -> EthernetLinkState {
        let phystatus = self.PHYSTATUS.extract();
        if phystatus.matches_all(PHYSTATUS::LINKSTS::SET) {
            let duplex = if phystatus.matches_all(PHYSTATUS::FULLDUP::SET) {
                Duplex::Full
            } else {
                Duplex::Half
            };
            let (speed, duplex) = if phystatus.matches_all(PHYSTATUS::MF1000::SET) {
                // 1000MF is always Full-Duplex
                (EthernetSpeed::Speed1000, Duplex::Full)
            } else if phystatus.matches_all(PHYSTATUS::M100::SET) {
                (EthernetSpeed::Speed100, duplex)
            } else if phystatus.matches_all(PHYSTATUS::M10::SET) {
                (EthernetSpeed::Speed10, duplex)
            } else {
                (EthernetSpeed::Unknown, duplex)
            };

            EthernetLinkState::Up(speed, duplex)
        } else {
            EthernetLinkState::Down
        }
    }

    pub fn get_chip_revision(&self) -> Revision {
        let rev = self.TCR.get() & Self::TCR_REVISION_MASK;
        match rev {
            0x50800000 => Revision::rtl8168gu,
            0x54000000 => Revision::rtl8168h,
            _ => Revision::other(rev),
        }
    }

    pub fn reset_device(&self, timeout: Duration) -> Result<(), Error> {
        self.CR.write(CR::RST::SET);

        psleep(Duration::from_millis(10));
        pwait(timeout, Duration::from_millis(10), || {
            self.CR.matches_all(CR::RST::CLEAR)
        })
    }

    pub fn reset_phy(&self, timeout: Duration) -> Result<(), Error> {
        let phy = PhyAccess::new(self, 0x00);

        let (id0, id1) = phy.id()?;
        log::info!("PHY {id0:04x}:{id1:04x}");

        phy.write_reg(0x1F, 0x00)?;
        phy.write_reg(0x0E, 0x00)?;

        phy.reset(timeout)?;
        phy.setup_link(true, Some(GBESR::empty()))?;

        psleep(Duration::from_millis(100));

        phy.write_reg(0x1F, 0x00)?;
        phy.write_reg(0x0E, 0x00)?;

        Ok(())
    }

    pub fn stop(&self, timeout: Duration) -> Result<(), Error> {
        self.CR.write(CR::STOP::SET);

        pwait(timeout, Duration::from_millis(10), || {
            self.TCR.matches_all(TCR::QUEUE_EMPTY::SET)
        })?;

        psleep(Duration::from_millis(100));

        self.IMR.set(0x0000);
        self.ISR.set(0xFFFF);

        Ok(())
    }
}

impl MdioBus for Regs {
    fn mii_read(&self, _phyaddr: u8, reg: u8) -> Result<u16, Error> {
        self.PHYAR
            .write(PHYAR::REGADDR.val(reg as u32) + PHYAR::FLAG::Read);
        psleep(Duration::from_millis(10));
        pwait(
            Duration::from_millis(100),
            Duration::from_millis(10),
            || self.PHYAR.matches_all(PHYAR::FLAG::SET),
        )?;
        Ok(self.PHYAR.read(PHYAR::DATA) as u16)
    }

    fn mii_write(&self, _phyaddr: u8, reg: u8, value: u16) -> Result<(), Error> {
        self.PHYAR.write(
            PHYAR::REGADDR.val(reg as u32) + PHYAR::DATA.val(value as u32) + PHYAR::FLAG::Write,
        );
        psleep(Duration::from_millis(10));
        pwait(
            Duration::from_millis(100),
            Duration::from_millis(10),
            || self.PHYAR.matches_all(PHYAR::FLAG::CLEAR),
        )
    }
}

impl Rtl8168 {
    pub fn new(
        dma: Arc<dyn DmaAllocator>,
        base: PhysicalAddress,
        info: PciDeviceInfo,
    ) -> Result<Self, Error> {
        let regs = unsafe { DeviceMemoryIo::<Regs>::map(base, Default::default()) }?;
        let mac0 = regs.IDRn[0].get().to_le_bytes();
        let mac1 = regs.IDRn[1].get().to_le_bytes();
        let mac = MacAddress::from([mac0[0], mac0[1], mac0[2], mac0[3], mac1[0], mac1[1]]);
        Ok(Self {
            regs: IrqSafeSpinlock::new(regs),
            mac,
            pci: info,

            nic: OneTimeInit::new(),
            rx: OneTimeInit::new(),
            tx: OneTimeInit::new(),

            events: BitmapEvent::new(AtomicWaker::new()),

            dma,
        })
    }
}

impl InterruptHandler for Rtl8168 {
    fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
        let regs = self.regs.lock();

        let status = regs.ISR.extract();

        if status.get() == 0 || status.get() == 0xFFFF {
            return false;
        }

        regs.IMR.set(0x0000);
        // Acknowledge interrupt
        regs.ISR.set(status.get());

        // Forward to softirq
        self.events.signal(status.get() as u64);

        // Restore interrupt mask
        regs.IMR.set(0xFFFF);

        true
    }
}

impl Rtl8168 {
    // "softirq" handler for rtl8168
    async fn packet_handler(&self) {
        let nic = *self.nic.get();
        let tx = self.tx.get();
        let rx = self.rx.get();

        loop {
            let events = LocalRegisterCopy::new(self.events.wait().await as u16);

            if events.matches_all(IMR_ISR::LINKCHG::SET) {
                let status = self.link_state();
                log::info!("rtl8168: link is {status}");
            }

            if events.matches_all(IMR_ISR::ROK::SET) {
                let mut rx = rx.lock();
                rx.consume(&*self.dma, |buffer| {
                    // TODO add packet len hint to packets
                    let packet = RxPacket::new(buffer, 0, nic);
                    ygg_driver_net_core::receive_packet(packet).ok();
                })
                .ok();
            }

            if events.matches_all(IMR_ISR::TOK::SET) {
                let mut tx = tx.lock();
                tx.consume().ok();
            }
        }
    }
}

impl Device for Rtl8168 {
    unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
        log::info!("Initialize rtl8168");
        log::info!("MAC: {}", self.mac);

        // Bind IRQ
        self.pci
            .init_interrupts(PreferredInterruptMode::Msi(true))?;
        let msi_info = self.pci.map_interrupt(Default::default(), self.clone())?;
        if msi_info.is_none() {
            log::error!("rtl8168 in non-MSI(-x) mode is not supported yet");
            return Err(Error::InvalidArgument);
        }

        let regs = self.regs.lock();

        let hwrev = regs.get_chip_revision();

        if let Revision::other(id) = hwrev {
            log::error!("rtl8168: unsupported chip ID {:#08x}, aborting", id);
            return Err(Error::InvalidArgument);
        }

        regs.R9346CR.write(R9346CR::EEM::ConfigWriteEnable);
        regs.CONFIG2.modify(CONFIG2::MSI::SET);
        regs.R9346CR.write(R9346CR::EEM::Normal);

        let rx_ring = RxRing::with_capacity(&*self.dma, 1024)?;
        let tx_ring = TxRing::with_capacity(&*self.dma, 1024)?;
        let rx_ring_base = rx_ring.base_address().into_u64();
        let tx_ring_base = tx_ring.base_address().into_u64();

        log::info!("Revision: {:?}", hwrev);
        let flags = hwrev.flags();

        // If the MAC was performing any I/O prior to driver init, stop it
        regs.stop(Duration::from_secs(1))?;

        // Perform software reset
        regs.reset_device(Duration::from_millis(100))?;

        let mut cpcr = CPCR::TXENB::SET + CPCR::MULRW::SET + CPCR::RXCHKSUM::SET;
        if flags.contains(ChipFlags::MACSTAT) {
            cpcr += CPCR::MACSTATDIS::SET;
        }
        regs.CPCR.write(cpcr);

        if flags.contains(ChipFlags::NEED_PHY_RESET) {
            log::info!("Reset PHY and start AN");
            regs.reset_phy(Duration::from_millis(1000))?;
        }

        // TODO initialize MAC address

        //regs.R9346CR.write(R9346CR::EEM::Normal);

        regs.RDSAR[1].set((rx_ring_base >> 32) as u32);
        regs.RDSAR[0].set(rx_ring_base as u32);
        regs.TNPDS[1].set((tx_ring_base >> 32) as u32);
        regs.TNPDS[0].set(tx_ring_base as u32);

        if flags.contains(ChipFlags::RXDV_GATED) {
            regs.MISC.set(regs.MISC.get() & !0x80000);
        }

        // Setup Tx
        regs.TCR.modify(TCR::MXDMA::Unlimited + TCR::IFG.val(3));
        // Setup Rx
        regs.MTPS.set(16);
        let mut rcr = RCR::MXDMA::Unlimited
            + RCR::RXFTH::NoThreshold
            + RCR::AAP::SET
            + RCR::AB::SET
            + RCR::AM::SET
            + RCR::APM::SET;
        if flags.contains(ChipFlags::EARLYOFFV2) {
            rcr += RCR::EARLYOFFV2::SET;
        }
        regs.RCR.set(rcr.value | 0x1800);

        // Enable Rx/Tx
        regs.CR.write(CR::RE::SET + CR::TE::SET);

        regs.IMR.set(0xFFFF);
        regs.ISR.set(0xFFFF);

        regs.MPKT.set(0);

        regs.RMS.set(4096);

        self.rx.init(IrqSafeSpinlock::new(rx_ring));
        self.tx.init(IrqSafeSpinlock::new(tx_ring));

        let interface =
            ygg_driver_net_core::register_interface(NetworkInterfaceType::Ethernet, self.clone());
        self.nic.init(interface.id());

        drop(regs);

        let this = self.clone();
        runtime::spawn(async move { this.packet_handler().await })?;

        Ok(())
    }

    fn display_name(&self) -> &str {
        "Realtek RTL8111/8168 Gigabit Ethernet Controller"
    }
}

impl NetworkDevice for Rtl8168 {
    fn allocate_transmit_buffer(&self, len: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error> {
        DmaBuffer::new_uninit_slice(&*self.dma, len)
    }

    fn transmit_buffer(&self, buffer: DmaBuffer<[u8]>) -> Result<(), Error> {
        let mut tx = self.tx.get().lock();
        let regs = self.regs.lock();

        if !matches!(regs.get_link_state(), EthernetLinkState::Up(_, _)) {
            return Err(Error::NetworkUnreachable);
        }

        tx.push(buffer)?;
        regs.TPPOLL.write(TPPOLL::NPQ::SET);

        Ok(())
    }

    fn packet_prefix_size(&self) -> usize {
        0
    }

    fn read_hardware_address(&self) -> MacAddress {
        self.mac
    }

    fn link_state(&self) -> LinkState {
        LinkState::Ethernet(self.regs.lock().get_link_state())
    }
}

impl Revision {
    fn flags(&self) -> ChipFlags {
        match self {
            Self::rtl8168h | Self::rtl8168gu => {
                ChipFlags::NEED_PHY_RESET
                    | ChipFlags::MACSTAT
                    | ChipFlags::RXDV_GATED
                    | ChipFlags::EARLYOFFV2
            }
            _ => unreachable!(),
        }
    }
}
