use cortex_m_rt::ExceptionFrame;
use defmt::error;

// same panicking *behavior* as `panic-probe` but doesn't print a panic message
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
#[defmt::panic_handler]
fn panic() -> ! {
    cortex_m::asm::udf()
}

fn display_ef(ef: &ExceptionFrame) {
    struct Wrapper(ExceptionFrame);
    impl defmt::Format for Wrapper {
        fn format(&self, fmt: defmt::Formatter) {
            defmt::write!(
                fmt,
                "ExceptionFrame(\n\tr0: {:08x}\n\tr1: {:08x}\n\tr2: {:08x}\n\tr3: {:08x}\n\tr12: {:08x}\n\tlr: {:08x}\n\tpc: {:08x}\n\txpsr: {:08x}\n)",
                self.0.r0(),
                self.0.r1(),
                self.0.r2(),
                self.0.r3(),
                self.0.r12(),
                self.0.lr(),
                self.0.pc(),
                self.0.xpsr()
            );
        }
    }

    error!("HardFault: Exception occured!");
    error!("Exception occured at: {:08x}", ef.pc());
    error!("XSPR: {:032b}", ef.xpsr());
    error!("LR: {:08x}", ef.lr());

    error!("HardFault: {:#?}", Wrapper(*ef))
}

#[allow(unreachable_code)]
#[cortex_m_rt::exception(trampoline = true)]
unsafe fn HardFault(ef: &ExceptionFrame) -> ! {
    static mut EXCEPTION: Option<ExceptionFrame> = None;
    unsafe { EXCEPTION = Some(*ef) };

    display_ef(ef);
    #[allow(clippy::empty_loop)]
    loop {}
}
