Documentation
extern crate libc;

use std::fmt::Display;
use std::mem::{transmute, size_of};

use array::{NDData, NDDataMut, NDArray, NDSlice, NDSliceMut};

pub trait SizedBuffer {

    unsafe fn get_raw_ptr(&self) -> *const libc::c_void;

    fn get_raw_size(&self) -> usize;
}

pub trait SizedBufferMut : SizedBuffer {

    unsafe fn get_raw_ptr_mut(&mut self) -> *mut libc::c_void;
}

pub trait ComputeBackend {

    fn init(&mut self) -> Result<(),String>;

    fn create_array(&mut self, size : usize) -> Result<u32,String>;

    fn set_array(&self, id : u32, array : &SizedBuffer) -> Result<(),String>;

    fn get_array(&self, id : u32, array : &mut SizedBufferMut) -> Result<(),String>;

    fn delete_array(&mut self, id : u32) -> Result<(),String>;

    fn finalize(&mut self) -> Result<(),String>;
}

pub struct ArrayRegistry<T : Clone> {
    array_list : Vec<T>,
    array_size : Vec<usize>,
    free_list : Vec<usize>,
}

impl<T> SizedBuffer for NDArray<T> {

    unsafe fn get_raw_ptr(&self) -> *const libc::c_void {
        transmute(self.get_data().as_ptr())
    }

    fn get_raw_size(&self) -> usize {
        self.size() * size_of::<T>()
    }
}

impl<T : Clone + Display> SizedBufferMut for NDArray<T> where NDArray<T> : SizedBuffer {

    unsafe fn get_raw_ptr_mut(&mut self) -> *mut libc::c_void {
        transmute(self.get_data_mut().as_mut_ptr())
    }
}

impl<'a, T> SizedBuffer for NDSliceMut<'a, T> {

    unsafe fn get_raw_ptr(&self) -> *const libc::c_void {
        transmute(self.get_data().as_ptr())
    }

    fn get_raw_size(&self) -> usize {
        self.size() * size_of::<T>()
    }
}

impl<'a, T : Clone + Display> SizedBufferMut for NDSliceMut<'a, T> where NDSliceMut<'a, T> : SizedBuffer {

    unsafe fn get_raw_ptr_mut(&mut self) -> *mut libc::c_void {
        transmute(self.get_data_mut().as_mut_ptr())
    }
}

impl<'a, T> SizedBuffer for NDSlice<'a, T> {

    unsafe fn get_raw_ptr(&self) -> *const libc::c_void {
        transmute(self.get_data().as_ptr())
    }

    fn get_raw_size(&self) -> usize {
        self.size() * size_of::<T>()
    }
}

impl<T : Sized + Clone> ArrayRegistry<T> {
    pub fn new() -> ArrayRegistry<T> {
        ArrayRegistry::<T> {
            array_list : Vec::<T>::new(),
            array_size : Vec::<usize>::new(),
            free_list : Vec::<usize>::new(),
        }
    }

    pub fn register_array(&mut self, array : T, size : usize) -> u32 {
        if size == 0 {
            panic!("Attempting to register an emtpy array");
        }

        if let Some(idx) = self.free_list.pop() {
            self.array_list[idx] = array.clone();
            return idx as u32;
        }
        else {
            self.array_list.push(array.clone());
            self.array_size.push(size);
            return (self.array_list.len() - 1) as u32;
        }
    }

    pub fn get_array(&self, id : u32) -> T {
        if id as usize >= self.array_list.len() {
            panic!("Querying array id {} which is not in the ArrayRegistry", id);
        }
        if self.array_size[id as usize] == 0 {
            panic!("Querying array id {} which has already been unregistered", id);
        }
        return self.array_list[id as usize].clone();
    }

    pub fn get_array_size(&self, id : u32) -> usize {
        if id as usize >= self.array_list.len() {
            panic!("Querying array id {} which is not in the ArrayRegistry", id);
        }
        return self.array_size[id as usize];
    }

    pub fn unregister_array(&mut self, id : u32) {
        if id as usize >= self.array_list.len() {
            panic!("Unregistering array id {} which is not in the ArrayRegistry", id);
        }
        self.array_size[id as usize] = 0;
        self.free_list.push(id as usize);
    }
}