use ffi;
use std::mem;
use std::ffi::{CStr, CString};
use libc;
use std::os::raw::c_void;
use ::stream::Stream;
use ::std::cmp::{min, max};
#[derive(Debug, Clone)]
pub struct DevMem {
    pub ( crate )  ptr: ffi::CUdeviceptr,
    pub ( crate )  len: usize,
    pub ( crate ) allocated: bool
}
impl DevMem {
    pub fn alloc(len: usize) -> DevMem {
        unsafe {
            let mut m: DevMem = mem::zeroed();
            m.len = len;
            m.allocated = true;
            cuda_call!(ffi::cuMemAlloc_v2(&mut m.ptr as _,len));
            m
        }
    }
    pub fn read(&self, data: &mut [u8]) {
        self.read_offset(data, 0);
    }
    pub fn read_offset(&self, data: &mut [u8], offset: usize) {
        cuda_call!(ffi::cuMemcpyDtoH_v2(data.as_mut_ptr() as _, self.ptr + offset as u64, min(self.len - offset, data.len())));
    }
    pub fn read_async(&self, data: &mut [u8], stream: &Stream) {
        self.read_offset_async(data, 0, stream);
    }
    pub fn read_offset_async(&self, data: &mut [u8], offset: usize, stream: &Stream) {
        cuda_call!(ffi::cuMemcpyDtoHAsync_v2(data.as_mut_ptr() as _, self.ptr + offset as u64, min(self.len - offset, data.len()), stream.0));
    }
    pub fn write(&self, data: &[u8]) {
        self.write_offset(data, 0);
    }
    pub fn write_offset(&self, data: &[u8], offset: usize) {
        cuda_call!(ffi::cuMemcpyHtoD_v2( self.ptr + offset as u64, data.as_ptr() as _, min(self.len - offset, data.len())));
    }
    pub fn write_async(&self, data: &[u8], stream: &Stream) {
        self.write_offset_async(data, 0, stream);
    }
    pub fn write_offset_async(&self, data: &[u8], offset: usize, stream: &Stream) {
        cuda_call!(ffi::cuMemcpyHtoDAsync_v2(self.ptr + offset as u64,data.as_ptr() as _, min(self.len - offset ,data.len()),stream.0));
    }
    pub fn len(&self) -> usize {
        return self.len;
    }
    pub fn dev_ptr(&self) -> ::ffi::CUdeviceptr {
        return self.ptr;
    }
    pub fn dev_ptr_ref(&self) -> &::ffi::CUdeviceptr {
        return &self.ptr;
    }
}
impl Drop for DevMem {
    fn drop(&mut self) {
        unsafe {
            if self.allocated {
                cuda_call!(ffi::cuMemFree_v2(self.ptr));
            }
        }
    }
}