Documentation
use core::mem;
use core::ops::Drop;

use gl;
use gl::types::*;

use super::{BufferTarget, Usage};


#[derive(Debug, Clone, Hash)]
pub struct GLBuffer {
    id: GLuint,

    stride: usize,
    kind: BufferTarget,
    usage: Usage,

    size: usize,
    kind_size: usize,
    length: usize,
}

impl GLBuffer {

    #[inline]
    pub fn new<T>(kind: BufferTarget, stride: usize, usage: Usage, data: &[T]) -> Self {
        let mut id = 0;

        let length = data.len();
        let kind_size = mem::size_of::<T>();
        let size = kind_size * length;
        let gl_kind = kind.to_gl();
        let gl_usage = usage.to_gl();

        unsafe {
            gl::GenBuffers(1, &mut id);
            gl::BindBuffer(gl_kind, id);
    		gl::BufferData(gl_kind, size as GLsizeiptr, data.as_ptr() as *const GLvoid, gl_usage);
        };

        GLBuffer {
            id: id,

            stride: stride * kind_size,
            kind: kind,
            usage: usage,

            size: size,
            kind_size: kind_size,
            length: length,
        }
    }

    #[inline(always)]
    pub fn id(&self) -> GLuint { self.id }

    #[inline(always)]
    pub fn stride(&self) -> usize { self.stride }
    #[inline(always)]
    pub fn kind(&self) -> &BufferTarget { &self.kind }
    #[inline(always)]
    pub fn usage(&self) -> &Usage { &self.usage }

    #[inline(always)]
    pub fn size(&self) -> usize { self.size }
    #[inline(always)]
    pub fn kind_size(&self) -> usize { self.kind_size }
    #[inline(always)]
    pub fn length(&self) -> usize { self.length }

    #[inline]
    pub fn bind(&self) -> &Self {
        unsafe { gl::BindBuffer(self.kind.to_gl(), self.id); }
        self
    }
    #[inline]
    pub fn unbind(&self) -> &Self {
        unsafe { gl::BindBuffer(self.kind.to_gl(), 0); }
        self
    }

    #[inline]
    pub fn update<T>(&mut self, data: &[T]) -> &mut Self {
        let length = data.len();
        let kind_size = mem::size_of::<T>();
        let size = kind_size * length;
        let gl_kind = self.kind.to_gl();
        let gl_usage = self.usage.to_gl();

        unsafe {
            gl::BindBuffer(gl_kind, self.id);
            gl::BufferData(gl_kind, size as GLsizeiptr, data.as_ptr() as *const GLvoid, gl_usage);
        };

        self.size = size;
        self.kind_size = kind_size;
        self.length = length;

        self
    }
}

impl Drop for GLBuffer {
    #[inline]
    fn drop(&mut self) {
        if self.id != 0 {
            unsafe { gl::DeleteBuffers(1, &self.id); }
        }
    }
}