use core::time::Duration;

use libk::{error::Error, task::runtime::pwait};
use tock_registers::{
    interfaces::{Readable, Writeable},
    register_bitfields, register_structs,
    registers::{ReadOnly, ReadWrite, WriteOnly},
};
use ygg_driver_net_core::ephy::MdioBus;
use yggdrasil_abi::net::link::LinkState;

// use crate::ethernet::{CapFlags, Capabilities};

register_structs! {
    pub ControlRegs {
        (0x00 => pub tx_clk_sel: ReadWrite<u32>),
        (0x04 => _0),
        (0x20 => pub control_status_speed_mode: ReadWrite<u32>),
        (0x24 => _1),
        (0x40 => @END),
    }
}

register_bitfields! {
    u32,
    pub NetworkControl [
        // Rx enable
        RE          OFFSET(2)   NUMBITS(1)  [],
        // Tx enable
        TE          OFFSET(3)   NUMBITS(1)  [],
        // MDIO enable
        MPE         OFFSET(4)   NUMBITS(1)  [],
        // Start Tx
        TSTART      OFFSET(9)   NUMBITS(1)  [],
        // Halt Tx
        THALT       OFFSET(10)  NUMBITS(1)  [],
    ],
    pub NetworkConfig [
        // Speed
        SPD         OFFSET(0)   NUMBITS(1)  [],
        // Full duplex
        FD          OFFSET(1)   NUMBITS(1)  [],
        PROMISC     OFFSET(4)   NUMBITS(1)  [],
        // Gigabit mode enable
        GBE         OFFSET(10)  NUMBITS(1)  [],
    ],
    pub NetworkStatus [
        LINK        OFFSET(0)   NUMBITS(1)  [],
        MDIO_IDLE   OFFSET(2)   NUMBITS(1)  [],
    ],
    pub DmaConfig [
        RX_BUF_SIZE OFFSET(16)  NUMBITS(8)  [],
    ],
    pub Interrupt [
        // Rx complete
        RCOMP       OFFSET(1)   NUMBITS(1)  [],
        // Tx complete
        TCOMP       OFFSET(7)   NUMBITS(1)  [],
        // Link change
        LINK        OFFSET(8)   NUMBITS(1)  [],
    ],
    pub PhyMaintenance [
        DATA        OFFSET(0)   NUMBITS(16) [],
        CODE        OFFSET(16)  NUMBITS(2)  [],
        REGA        OFFSET(18)  NUMBITS(5)  [],
        PHYA        OFFSET(23)  NUMBITS(5)  [],
        RW          OFFSET(28)  NUMBITS(2)  [
            Read = 0b10,
            Write = 0b01,
        ],
        SOF         OFFSET(30)  NUMBITS(1)  [],
    ],
    pub ModuleId [
        IDNUM       OFFSET(16)  NUMBITS(12) [],
        REV         OFFSET(0)   NUMBITS(16) [],
    ]
}

register_structs! {
    pub Regs {
        (0x000 => pub network_control: ReadWrite<u32, NetworkControl::Register>),
        (0x004 => pub network_config: ReadWrite<u32, NetworkConfig::Register>),
        (0x008 => pub network_status: ReadOnly<u32, NetworkStatus::Register>),
        (0x00C => pub user_io: ReadWrite<u32>),
        (0x010 => pub dma_config: ReadWrite<u32, DmaConfig::Register>),
        (0x014 => pub transmit_status: ReadWrite<u32>),
        (0x018 => pub rx_buffer_queue_base: ReadWrite<u32>),
        (0x01C => pub tx_buffer_queue_base: ReadWrite<u32>),
        (0x020 => pub rx_status: ReadWrite<u32>),
        (0x024 => pub interrupt_status: ReadWrite<u32, Interrupt::Register>),
        (0x028 => pub interrupt_enable: WriteOnly<u32, Interrupt::Register>),
        (0x02C => pub interrupt_disable: WriteOnly<u32, Interrupt::Register>),
        (0x030 => pub interrupt_mask: ReadOnly<u32, Interrupt::Register>),
        (0x034 => pub phy_maintenance: ReadWrite<u32, PhyMaintenance::Register>),
        (0x038 => pub rx_pause_quantum: ReadOnly<u32>),
        (0x03C => pub tx_pause_quantum: ReadWrite<u32>),
        (0x040 => pub tx_partial_store_fwd: ReadWrite<u32>),
        (0x044 => pub rx_partial_store_fwd: ReadWrite<u32>),
        (0x048 => _0),
        (0x080 => pub hash_register_bottom: ReadWrite<u32>),
        (0x084 => pub hash_register_top: ReadWrite<u32>),
        (0x088 => pub specific_address_x: [ReadWrite<u32>; 8]),
        (0x0A8 => pub type_id_match: [ReadWrite<u32>; 4]),
        (0x0B8 => pub wake_on_lan: ReadWrite<u32>),
        (0x0BC => pub ipg_stretch: ReadWrite<u32>),
        (0x0C0 => pub stacked_vlan: ReadWrite<u32>),
        (0x0C4 => pub tx_pfc_pause: ReadWrite<u32>),
        (0x0C8 => pub specific_address_mask: [ReadWrite<u32>; 2]),
        (0x0D0 => pub rx_buffer_ahb_mask: ReadWrite<u32>),
        (0x0D4 => pub ptp_rx_unicast_dst_ip: ReadWrite<u32>),
        (0x0D8 => pub ptp_tx_unicast_dst_ip: ReadWrite<u32>),
        (0x0DC => pub tsu_timer_cval_nanos: ReadWrite<u32>),
        (0x0E0 => pub tsu_timer_cval_sec: ReadWrite<u32>),
        (0x0E4 => _1),
        (0x0FC => pub module_id: ReadOnly<u32, ModuleId::Register>),
        (0x100 => _2),
        (0x200 => pub pcs_control: ReadWrite<u32>),
        (0x204 => pub pcs_status: ReadOnly<u32>),
        (0x208 => pub pcs_phy_id_upper: ReadOnly<u32>),
        (0x20C => pub pcs_phy_id_lower: ReadOnly<u32>),
        (0x210 => pub pcs_aneg_adv: ReadWrite<u32>),
        (0x214 => pub pcs_aneg_link_ability: ReadOnly<u32>),
        (0x218 => pub pcs_aneg_expansion: ReadOnly<u32>),
        (0x21C => pub pcs_aneg_next_page: ReadWrite<u32>),
        (0x220 => pub pcs_aneg_link_next_page: ReadOnly<u32>),
        (0x224 => _3),
        (0x280 => pub design_config_1: ReadOnly<u32>),
        (0x284 => pub design_config_2: ReadOnly<u32>),
        (0x288 => pub design_config_3: ReadOnly<u32>),
        (0x28C => pub design_config_4: ReadOnly<u32>),
        (0x290 => pub design_config_5: ReadOnly<u32>),
        (0x294 => pub design_config_6: ReadOnly<u32>),
        (0x298 => pub design_config_7: ReadOnly<u32>),
        (0x29C => _4),
        (0x400 => @END),
    }
}

impl Regs {
    // pub fn probe_capabilities(&self) -> Capabilities {
    //     let mut flags = CapFlags::empty();
    //     let mut queue_mask = 0x1;
    //     let mut queue_count = 1;
    //     let module_id = self.module_id.read(ModuleId::IDNUM);

    //     if module_id >= 0x2 {
    //         flags |= CapFlags::MACB_IS_GEM;

    //         queue_mask |= self.design_config_6.get() & 0xFF;
    //         queue_count = queue_mask.count_ones() as usize;
    //     }

    //     Capabilities {
    //         flags,
    //         queue_mask,
    //         queue_count,
    //     }
    // }

    pub fn link_state(&self) -> LinkState {
        todo!()
    }

    fn wait_mdio_idle(&self, timeout: Duration) -> Result<(), Error> {
        pwait(timeout, Duration::from_millis(20), || {
            self.network_status
                .matches_all(NetworkStatus::MDIO_IDLE::SET)
        })
    }
}
impl MdioBus for Regs {
    fn mii_read(&self, phyaddr: u8, regaddr: u8) -> Result<u16, Error> {
        self.wait_mdio_idle(Duration::from_secs(1))?;
        self.phy_maintenance.write(
            PhyMaintenance::CODE.val(2)
                + PhyMaintenance::PHYA.val(phyaddr as _)
                + PhyMaintenance::REGA.val(regaddr as _)
                + PhyMaintenance::SOF.val(1)
                + PhyMaintenance::RW::Read,
        );
        self.wait_mdio_idle(Duration::from_secs(1))?;
        let value = self.phy_maintenance.read(PhyMaintenance::DATA);
        Ok(value as u16)
    }
    fn mii_write(&self, phyaddr: u8, regaddr: u8, value: u16) -> Result<(), Error> {
        self.wait_mdio_idle(Duration::from_secs(1))?;
        self.phy_maintenance.write(
            PhyMaintenance::CODE.val(2)
                + PhyMaintenance::PHYA.val(phyaddr as _)
                + PhyMaintenance::REGA.val(regaddr as _)
                + PhyMaintenance::SOF.val(1)
                + PhyMaintenance::DATA.val(value as _)
                + PhyMaintenance::RW::Write,
        );
        self.wait_mdio_idle(Duration::from_secs(1))
    }
}
