use crate::ffi;
use std::ffi::CStr;
use std::fmt;

#[derive(Debug)]
pub enum CudaError {
    OutOfMemory,
    Raw { code: i32, message: String },
}

impl fmt::Display for CudaError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            CudaError::OutOfMemory => write!(f, "CUDA out of memory"),
            CudaError::Raw { code, message } => write!(f, "CUDA error {code}: {message}"),
        }
    }
}

impl std::error::Error for CudaError {}

pub type Result<T> = std::result::Result<T, CudaError>;

pub fn check(code: i32) -> Result<()> {
    if code == ffi::CUDA_SUCCESS {
        return Ok(());
    }
    let message = unsafe {
        let ptr = ffi::cudaGetErrorString(code);
        if ptr.is_null() {
            "unknown error".to_string()
        } else {
            CStr::from_ptr(ptr).to_string_lossy().into_owned()
        }
    };
    Err(match code {
        ffi::CUDA_ERROR_OUT_OF_MEMORY => CudaError::OutOfMemory,
        _ => CudaError::Raw { code, message },
    })
}
