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

use super::{GLTexture, UniformKind};


#[derive(Debug, Clone, Hash)]
pub struct GLUniform {
    kind: UniformKind,
    count: usize,
    location: usize,
}

impl GLUniform {

    #[inline(always)]
    pub fn new(kind: UniformKind, count: usize, location: usize) -> Self {
        GLUniform {
            kind: kind,
            count: count,
            location: location,
        }
    }

    #[inline(always)]
    pub fn kind(&self) -> &UniformKind { &self.kind }
    #[inline(always)]
    pub fn count(&self) -> usize { self.count }
    #[inline(always)]
    pub fn location(&self) -> usize { self.location }
}


macro_rules! create_set_uniform {
    ($name: ident, $func: ident, $kind: ident) => (
        #[inline]
        pub fn $name(&self, value: $kind) {
            unsafe {
                gl::$func(self.location as GLint, value);
            }
        }
    )
}

macro_rules! create_set_vec_uniform {
    ($name: ident, $func: ident, $kind: ident, $item_count: expr) => (
        #[inline]
        pub fn $name(&self, value: &[$kind; $item_count]) {
            unsafe {
                gl::$func(self.location as GLint, 1 as GLint, value.as_ptr());
            }
        }
    )
}

macro_rules! create_set_matrix_uniform {
    ($name: ident, $func: ident, $kind: ident, $item_count: expr) => (
        #[inline]
        pub fn $name(&self, value: &[$kind; $item_count]) {
            unsafe {
                gl::$func(self.location as GLint, 1 as GLint, gl::FALSE, value.as_ptr());
            }
        }
    )
}

impl GLUniform {
    create_set_uniform!(set_1f, Uniform1f, f32);
    create_set_uniform!(set_1i, Uniform1i, i32);

    create_set_vec_uniform!(set_2f, Uniform2fv, f32, 2);
    create_set_vec_uniform!(set_2i, Uniform2iv, i32, 2);

    create_set_vec_uniform!(set_3f, Uniform3fv, f32, 3);
    create_set_vec_uniform!(set_3i, Uniform3iv, i32, 3);

    create_set_vec_uniform!(set_4f, Uniform4fv, f32, 4);
    create_set_vec_uniform!(set_4i, Uniform4iv, i32, 4);

    create_set_matrix_uniform!(set_mat2f, UniformMatrix2fv, f32, 4);
    create_set_matrix_uniform!(set_mat3f, UniformMatrix3fv, f32, 9);
    create_set_matrix_uniform!(set_mat4f, UniformMatrix4fv, f32, 16);
}


macro_rules! create_set_vec_uniform_size {
    ($name: ident, $func: ident, $kind: ident) => (
        #[inline]
        pub fn $name(&self, values: &[$kind]) {
            unsafe {
                gl::$func(self.location as GLint, self.count as GLint, values.as_ptr());
            }
        }
    )
}

macro_rules! create_set_matrix_uniform_size {
    ($name: ident, $func: ident, $kind: ident) => (
        #[inline]
        pub fn $name(&self, values: &[$kind]) {
            unsafe {
                gl::$func(self.location as GLint, self.count as GLint, gl::FALSE, values.as_ptr());
            }
        }
    )
}

impl GLUniform {
    create_set_vec_uniform_size!(set_2fv, Uniform2fv, f32);
    create_set_vec_uniform_size!(set_2iv, Uniform2iv, i32);

    create_set_vec_uniform_size!(set_3fv, Uniform3fv, f32);
    create_set_vec_uniform_size!(set_3iv, Uniform3iv, i32);

    create_set_vec_uniform_size!(set_4fv, Uniform4fv, f32);
    create_set_vec_uniform_size!(set_4iv, Uniform4iv, i32);

    create_set_matrix_uniform_size!(set_mat2fv, UniformMatrix2fv, f32);
    create_set_matrix_uniform_size!(set_mat3fv, UniformMatrix3fv, f32);
    create_set_matrix_uniform_size!(set_mat4fv, UniformMatrix4fv, f32);

    #[inline]
    pub fn set_sampler_2d(&self, texture: &GLTexture, index: usize) {
        unsafe {
            gl::ActiveTexture(gl::TEXTURE0 + index as GLuint);
            gl::Uniform1i(self.location as GLint, index as GLint);
            gl::BindTexture(texture.kind().to_gl(), texture.id());
        }
    }
}