use std::mem::MaybeUninit;
use std::process;
use std::sync::mpsc;
use std::sync::mpsc::Receiver;
use std::sync::mpsc::Sender;
use thiserror::Error;

static SENDER: std::sync::Mutex<Option<Sender<String>>> = std::sync::Mutex::new(None);
static RECEIVER: std::sync::Mutex<Option<Receiver<String>>> = std::sync::Mutex::new(None);

pub fn init() {
    let (tx, rx) = mpsc::channel();
    *SENDER.lock().unwrap() = Some(tx);
    *RECEIVER.lock().unwrap() = Some(rx);
}

pub fn receiver() -> Receiver<String> {
    RECEIVER.lock().unwrap().take().unwrap()
}

pub fn sender() -> Sender<String> {
    SENDER.lock().unwrap().as_ref().unwrap().clone()
}

#[derive(Debug, Error)]
pub enum SignalError {
    #[error("sigaction error")]
    Sigaction(i32),
}

fn handle(sig: libc::c_int, _siginfo: *const libc::siginfo_t, uctxptr: *const libc::c_void) {
    let pid = process::id();
    let uctxptr = uctxptr as *const libc::ucontext_t;
    if !uctxptr.is_null() {
        let _uctx = unsafe { &*uctxptr };
    }
    SENDER
        .lock()
        .unwrap()
        .as_ref()
        .unwrap()
        .send(format!("pid {}  sig {}", pid, sig))
        .unwrap();
}

pub fn set_signal_handler(signum: libc::c_int) -> Result<(), SignalError> {
    // Safe because it creates a valid value:
    let mask: libc::sigset_t = unsafe { MaybeUninit::zeroed().assume_init() };
    let sa_sigaction: libc::sighandler_t = handle as *const libc::c_void as usize;
    let act = libc::sigaction {
        sa_sigaction,
        sa_mask: mask,
        sa_flags: 0,
        sa_restorer: None,
    };
    unsafe {
        let ec = libc::sigaction(signum, &act, std::ptr::null_mut());
        let errno = *libc::__errno_location();
        // CStr::from_ptr(libc::strerror(errno))
        if ec != 0 {
            return Err(SignalError::Sigaction(errno));
        }
    }
    Ok(())
}

fn do_nothing_handler(
    _sig: libc::c_int,
    _siginfo: *const libc::siginfo_t,
    _uctxptr: *const libc::c_void,
) {
}

pub fn ignore_signal(signum: libc::c_int) -> Result<(), SignalError> {
    // Safe because it creates a valid value:
    let mask: libc::sigset_t = unsafe { MaybeUninit::zeroed().assume_init() };
    let sa_sigaction: libc::sighandler_t = do_nothing_handler as *const libc::c_void as usize;
    let act = libc::sigaction {
        sa_sigaction,
        sa_mask: mask,
        sa_flags: 0,
        sa_restorer: None,
    };
    unsafe {
        let ec = libc::sigaction(signum, &act, std::ptr::null_mut());
        let errno = *libc::__errno_location();
        // CStr::from_ptr(libc::strerror(errno))
        if ec != 0 {
            return Err(SignalError::Sigaction(errno));
        }
    }
    Ok(())
}
