use alloc::sync::Arc;
use device_api::device::{Device, DeviceInitContext};
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
use libk::{
    error::Error,
    fs::sysfs::{self, nodes::SysfsRtcNode},
};
use libk_mm::device::DeviceMemoryIo;
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{
    interfaces::Readable,
    register_structs,
    registers::{ReadOnly, ReadWrite},
};
use yggdrasil_abi::time::NANOSECONDS_IN_SECOND;

register_structs! {
    #[allow(non_snake_case)]
    Regs {
        (0x00 => TIME_LOW: ReadOnly<u32>),
        (0x04 => TIME_HIGH: ReadOnly<u32>),
        (0x08 => ALARM_LOW: ReadWrite<u32>),
        (0x0C => ALARM_HIGH: ReadWrite<u32>),
        (0x10 => IRQ_ENABLED: ReadWrite<u32>),
        (0x14 => CLEAR_ALARM: ReadWrite<u32>),
        (0x18 => ALARM_STATUS: ReadOnly<u32>),
        (0x1C => CLEAR_INTERRUPT: ReadWrite<u32>),
        (0x20 => @END),
    }
}

struct Rtc {
    regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
}

impl SysfsRtcNode for Rtc {
    fn read(&self) -> Result<u64, Error> {
        let regs = self.regs.lock();
        let low = regs.TIME_LOW.get();
        let high = regs.TIME_HIGH.get();
        let t = ((high as u64) << 32) | (low as u64);
        Ok(t / NANOSECONDS_IN_SECOND)
    }
}

impl Device for Rtc {
    unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
        sysfs::nodes::add_rtc_node(self.clone());
        Ok(())
    }

    fn display_name(&self) -> &str {
        "Google Goldfish RTC"
    }
}

device_tree_driver! {
    compatible: ["google,goldfish-rtc"],
    driver: {
        fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
            let base = node.map_base(context, 0)?;

            let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;

            let rtc = Arc::new(Rtc { regs: IrqSafeSpinlock::new(regs) });

            Some(rtc)
        }
    }
}
