use backtrace::Backtrace;
use lazy_static::lazy_static;
use std::sync::{Arc, Mutex};

// on panic, save the backtrace here (as string)
lazy_static! {
    pub(crate) static ref BACKTRACE: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
}

macro_rules! capture_unwind {
    ($code:expr) => {
        match std::panic::catch_unwind(|| $code) {
            Ok(v) => v,
            Err(e) => {
                let panic_information = match e.downcast::<String>() {
                    Ok(v) => *v,
                    Err(e) => match e.downcast::<&str>() {
                        Ok(v) => v.to_string(),
                        _ => "Unknown Source of Error".to_owned(),
                    },
                };
                let backtrace = crate::panic_handling::BACKTRACE
                    .lock()
                    .ok()
                    .map(|mut x| x.take())
                    .flatten()
                    .unwrap_or_else(|| "<no backtrace available>".to_string());
                let message = format!("{}\nBacktrace:\n{}", panic_information, backtrace);

                error!(message);

                // We need to return *something*.
                std::default::Default::default()
            }
        }
    };
}
pub(crate) use capture_unwind;

fn panic_hook(info: &std::panic::PanicInfo) {
    // We log the information in here via tracing, in the hope that it'll get printed in logcat.
    tracing::error!("panic: {info}");
    let backtrace = Backtrace::new();
    tracing::error!("backtrace: {backtrace:?}");
    match BACKTRACE.lock() {
        Ok(mut b) => {
            *b = Some(format!("{backtrace:?}"));
        }
        Err(_) => {
            // NOTE: We could just ignore the poison. Perhaps that'd be a good idea.
            tracing::error!("panic: backtrace mutex is poisoned!");
        }
    }
}

pub(crate) fn register_panic_hook() {
    std::panic::set_hook(Box::new(panic_hook));
}
