use core::fmt::Display;

use embassy_sync::{
    blocking_mutex::raw::RawMutex,
    mutex::{Mutex, MutexGuard},
};
use embassy_time::Duration;

pub struct FormattedDuration<'a>(&'a Duration);

impl Display for FormattedDuration<'_> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        let &FormattedDuration(duration) = self;
        f.write_fmt(format_args!(
            "{:01}.{:06}",
            duration.as_secs(),
            duration.as_micros() % 1_000_000
        ))
    }
}

pub trait DurationExt {
    fn display_as_secs(&self) -> FormattedDuration<'_>;
}

impl DurationExt for Duration {
    fn display_as_secs(&self) -> FormattedDuration<'_> {
        FormattedDuration(self)
    }
}

pub const fn get_file_name(path: &str) -> &str {
    let path = path.as_bytes();
    let mut start_index = path.len() as isize;

    loop {
        start_index -= 1;

        if start_index < 0
            || path[start_index as usize] == b'/'
            || path[start_index as usize] == b'\\'
        {
            start_index += 1;
            break;
        }
    }

    match str::from_utf8(path.split_at(start_index as usize).1) {
        Ok(substring) => substring,
        Err(_) => panic!("Failed to extract the file name from a path."),
    }
}

#[allow(unused)]
pub trait MutexExt<M, T> {
    type Guard<'a>
    where
        M: 'a,
        T: 'a,
        Self: 'a;

    /// Locks the mutex, while entering a critical section only during locking and unlocking.
    ///
    /// # Safety:
    /// * The guard must not be kept across async `yield` points.
    /// * This must not be called from within interrupt handlers.
    /// Otherwise, a deadlock might occur.
    unsafe fn lock_blocking(&self) -> Self::Guard<'_>;
}

impl<M: RawMutex, T> MutexExt<M, T> for Mutex<M, T> {
    type Guard<'a>
        = MutexGuard<'a, M, T>
    where
        M: 'a,
        T: 'a,
        Self: 'a;

    unsafe fn lock_blocking(&self) -> Self::Guard<'_> {
        loop {
            if let Ok(guard) = self.try_lock() {
                return guard;
            }
        }
    }
}
