use std::mem;
use std::ffi::{CStr, CString};
use std::os::raw::c_void;
use error_chain;
use ffi;
use libc;
use std::fmt::{self, Display, Debug, Formatter};
#[derive(Clone)]
pub struct CudaError {
    pub(crate) code: ffi::cudaError_enum
}
impl Default for CudaError {
    fn default() -> Self {
        CudaError {
            code: ffi::cudaError_enum::CUDA_SUCCESS
        }
    }
}
impl Display for CudaError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{} - {}", error_name(self.code), err_str(self.code))
    }
}
impl Debug for CudaError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{} - {}", error_name(self.code), err_str(self.code))
    }
}
impl ::std::error::Error for CudaError {
    fn description(&self) -> &str {
        "Cuda call failed"
    }
    fn cause(&self) -> Option<&::std::error::Error> {
        None
    }
    
}
pub type Result<T> = ::std::result::Result<T, CudaError>;
impl From<ffi::cudaError_enum> for CudaError {
    fn from(err: ffi::cudaError_enum) -> Self {
        CudaError {
            code: err
        }
    }
}
pub fn error_name(code: ffi::cudaError_enum) -> String {
    unsafe {
        if code == ffi::cudaError::CUDA_ERROR_UNKNOWN {
            return format!("Unknown error");
        }
        let mut n: *const i8 = ::std::ptr::null();
        let res = ffi::cuGetErrorName(code, &mut n as *mut _);
        if res == ffi::CUresult::CUDA_ERROR_INVALID_VALUE || n == ::std::ptr::null(){
            return format!("Unknown cuda error: {:?}", code as i32);
        }else {
            CStr::from_ptr(n).to_string_lossy().to_string()
        }
    }
}
pub fn err_str(code: ffi::cudaError_enum) -> String {
    unsafe {
        if code == ffi::cudaError::CUDA_ERROR_UNKNOWN {
            return format!("Unknown error");
        }
        let mut n: *const i8 = ::std::ptr::null();
        let res = ffi::cuGetErrorString(code, &mut n as *mut _);
        if res == ffi::CUresult::CUDA_ERROR_INVALID_VALUE || n == ::std::ptr::null() {
            return format!("Unknown cuda error: {:?}", code as i32);
        }else {
            CStr::from_ptr(n).to_string_lossy().to_string()
        }
    }
}