//! 串口控制台 PL011 驱动

use core::{
    fmt::Write,
    ops::{Deref, DerefMut},
};

use moa_bsp_define::console::ConsoleImpl;
use tock_registers::{
    interfaces::{Readable, Writeable},
    register_bitfields, register_structs,
    registers::{ReadOnly, ReadWrite, WriteOnly},
};

use crate::{processor::cpu_relax, space::addr::Paddr};

register_bitfields! {
    u32,

    Data [
        IO OFFSET(0) NUMBITS(32) []
    ],

    Flags [
        FR OFFSET(0) NUMBITS(32) [
            TXFE = 0x80,
            RXFF = 0x40,
            TXFF = 0x20,
            RXFE = 0x10,
            BUSY = 0x08,
        ]
    ],

    Baud [
        BAUD OFFSET(0) NUMBITS(32) []
    ],

    LineControl [
        BRK OFFSET(0) NUMBITS(1) [],
        PEN OFFSET(1) NUMBITS(1) [],
        EPS OFFSET(2) NUMBITS(1) [],
        STP2 OFFSET(3) NUMBITS(1) [],
        FEN OFFSET(4) NUMBITS(1) [],

        WLEN OFFSET(5) NUMBITS(2) [
            WLEN_5 = 0,
            WLEN_6 = 1,
            WLEN_7 = 2,
            WLEN_8 = 3,
        ],

        SPS OFFSET(7) NUMBITS(1) [],
    ],

    Control [
        UARTEN OFFSET(0) NUMBITS(1) [],
        SIREN OFFSET(1) NUMBITS(1) [],
        IIRLP OFFSET(2) NUMBITS(1) [],
        LPE OFFSET(7) NUMBITS(1) [],
        TXE OFFSET(8) NUMBITS(1) [],
        RXE OFFSET(9) NUMBITS(1) [],
        DTR OFFSET(10) NUMBITS(1) [],
        RTS OFFSET(11) NUMBITS(1) [],
        OUT1 OFFSET(12) NUMBITS(1) [],
        OUT2 OFFSET(13) NUMBITS(1) [],
        RTSEN OFFSET(14) NUMBITS(1) [],
        CTSEN OFFSET(15) NUMBITS(1) [],
    ]
}

register_structs! {
    #[doc(hidden)]
    pub Pl011Registers {
        // Data register
        (0x00 => dr: WriteOnly<u32, Data::Register>),
        // ecr - 0x04 Error clear register (Write)
        // pl010_lcrh - 0x08 Line control register, high byte
        // pl010_lcrm - 0x0C Line control register, middle byte
        // pl010_lcrl - 0x10 Line control register, low byte
        // pl010_cr - 0x14 Control register
        (0x04 => _reserved),
        // Flag register (Read only)
        (0x18 => fr: ReadOnly<u32, Flags::Register>),
        // pl011_rlcr - 0x1c Receive line control register
        // lpr - 0x20 IrDA low-power counter register
        (0x1c => _reserved2),
        // Integer baud rate register
        (0x24 => ibrd: ReadWrite<u32, Baud::Register>),
        // Fractional baud rate register
        (0x28 => fbrd: ReadWrite<u32, Baud::Register>),
        // Line control register
        (0x2c => lcrh: ReadWrite<u32, LineControl::Register>),
        // Control register
        (0x30 => cr: ReadWrite<u32, Control::Register>),
        (0x34 => @END),
    }
}

/// PL011 串口
pub struct Pl011 {
    // 寄存器基址
    regs: usize,
    // 时钟频率
    clock: u32,
    // 波特率
    baudrate: u32,
}

impl Pl011 {
    /// 构建 pl011
    pub const fn create(regs: Paddr, clock: u32, baudrate: u32) -> Self {
        Self { regs: regs.to_io_const().to_value(), clock, baudrate }
    }

    fn ptr(&self) -> *mut Pl011Registers {
        self.regs as *mut _
    }
}

impl Pl011Registers {
    #[inline]
    fn putc(&self, c: u8) {
        while self.fr.matches_all(Flags::FR::TXFF) {
            cpu_relax();
        }

        self.dr.set(u32::from(c));

        while self.fr.matches_all(Flags::FR::BUSY) {
            cpu_relax();
        }
    }
}

impl Write for Pl011 {
    fn write_str(&mut self, s: &str) -> core::fmt::Result {
        let buf = s.as_bytes();
        let reg = &mut **self;
        for &c in buf {
            reg.putc(c);
        }
        Ok(())
    }
}

impl ConsoleImpl for Pl011 {
    fn init(&mut self) {
        let mut temp = 16 * self.baudrate;
        let divider = self.clock / temp;
        let remainder = self.clock % temp;
        temp = (8 * remainder) / self.baudrate;
        let fraction = (temp >> 1) + (temp & 1);

        let reg = &mut **self;

        reg.cr.set(0);
        reg.ibrd.set(divider);
        reg.fbrd.set(fraction);

        reg.lcrh.write(LineControl::WLEN::WLEN_8 + LineControl::FEN::SET);
        reg.cr.write(
            Control::UARTEN::SET + Control::TXE::SET + Control::RXE::SET + Control::RTS::SET,
        );
    }
}

impl Deref for Pl011 {
    type Target = Pl011Registers;

    fn deref(&self) -> &Self::Target {
        unsafe { &*self.ptr() }
    }
}

impl DerefMut for Pl011 {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { &mut *self.ptr() }
    }
}
