#![no_std]

extern crate alloc;

use core::{
    mem::MaybeUninit,
    net::{IpAddr, Ipv4Addr},
};

use alloc::sync::Arc;
use device_api::dma::DmaAllocator;
use libk::dma::{DmaBuffer, DummyDmaAllocator};
use libk_util::OneTimeInit;
use ygg_driver_net_core::{
    interface::{NetworkDevice, NetworkInterfaceType},
    RxPacket,
};
use yggdrasil_abi::{error::Error, net::MacAddress};

struct LoopbackDevice {
    allocator: Arc<dyn DmaAllocator>,
}

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

    fn transmit_buffer(&self, buffer: DmaBuffer<[u8]>) -> Result<(), Error> {
        let packet = RxPacket::new(buffer, 0, *LOOPBACK_ID.get());
        ygg_driver_net_core::receive_packet(packet)
    }

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

    fn read_hardware_address(&self) -> MacAddress {
        MacAddress::UNSPECIFIED
    }
}

static LOOPBACK_ID: OneTimeInit<u32> = OneTimeInit::new();

pub fn init() {
    let loopback = Arc::new(LoopbackDevice {
        allocator: Arc::new(DummyDmaAllocator),
    });
    let interface =
        ygg_driver_net_core::register_interface(NetworkInterfaceType::Loopback, loopback);

    LOOPBACK_ID.init(interface.id());

    interface.set_address(IpAddr::V4(Ipv4Addr::LOCALHOST));
}
