#![allow(non_snake_case)]
#![allow(dead_code)]

use field_offset::offset_of;

use cgmath::{Vector3, Vector2};
use cgmath::prelude::*;

#[repr(C)]
pub struct Vertex {
    pub Position: Vector3<f32>,
    pub Normal: Vector3<f32>,
    pub TexCoords: Vector2<f32>,
    pub Tangent: Vector3<f32>,
    pub Bitangent: Vector3<f32>,
}

impl Default for Vertex {
    fn default () -> Self {
        Vertex {
            Position: Vector3::zero(),
            Normal: Vector3::zero(),
            TexCoords: Vector2::zero(),
            Tangent: Vector3::zero(),
            Bitangent: Vector3::zero(),
        }
    }
}



#[derive(Clone)]
pub struct Texture {
    pub id: u32,
    pub type_: String,
    pub path: String,
}

#[derive(Default)]
pub struct Mesh {
    pub vertices: Vec<Vertex>,
    pub indices: Vec<u32>,
    pub textures: Vec<Texture>,
    pub VAO: u32,
    VBO: u32,
    EBO: u32,
}





impl Mesh{
    pub fn new(vertices: Vec<Vertex>, indices: Vec<u32>, textures: Vec<Texture>) -> Mesh
        {
            let mut temp = Mesh {
                vertices,
                indices,
                textures,
                VAO: 0,
                VBO: 0,
                EBO: 0,
            };
                temp.setupMesh();
                temp

        }

     fn setupMesh(&mut self)
                {
                    // create buffers/arrays

                    unsafe {

                        gl::GenVertexArrays(1, &mut self.VAO);
                        gl::GenBuffers(1, &mut self.VBO);
                        gl::GenBuffers(1, &mut self.EBO);

                        gl::BindVertexArray(self.VAO);

                        let size = (self.vertices.len() * std::mem::size_of::<Vertex>()) as isize;

                        // NOTE, i am not sure if this one will work
                        gl::BindBuffer(gl::ARRAY_BUFFER, self.VBO);
                        gl::BufferData(gl::ARRAY_BUFFER, size, self.vertices.as_ptr().cast(), gl::STATIC_DRAW);

                        // the vertex attribPointers
                        // vert pox
                        gl::EnableVertexAttribArray(0);
                        gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, std::mem::size_of::<Vertex>() as i32, 0 as *const _);
                        // vertex normals
                        gl::EnableVertexAttribArray(1);
                        gl::VertexAttribPointer(1, 3, gl::FLOAT, gl::FALSE, std::mem::size_of::<Vertex>() as i32, offset_of!(Vertex => Normal).get_byte_offset() as *const std::os::raw::c_void);

                        // vertex texture coords

                        gl::EnableVertexAttribArray(2);
                        gl::VertexAttribPointer(2, 2, gl::FLOAT, gl::FALSE, std::mem::size_of::<Vertex>() as i32, offset_of!(Vertex => TexCoords).get_byte_offset()  as *const std::os::raw::c_void);

                        //vertex tangent

                        gl::EnableVertexAttribArray(3);
                        gl::VertexAttribPointer(3, 3, gl::FLOAT, gl::FALSE, std::mem::size_of::<Vertex>() as i32, offset_of!(Vertex => Tangent).get_byte_offset() as *const std::os::raw::c_void);

                        // vertex Bitangent

                        gl::EnableVertexAttribArray(4);
                        gl::VertexAttribPointer(4, 3, gl::FLOAT, gl::FALSE, std::mem::size_of::<Vertex>() as i32, offset_of!(Vertex => Bitangent).get_byte_offset() as *const std::os::raw::c_void);

                        gl::BindVertexArray(0);
                    }



                }
    // TODO this
    pub fn Draw(&self, shader: &crate::shader::shader)
        {
            let mut diffuseNr = 0;
            let mut specularNr = 0;
            let mut normalNr = 0;
            let mut heightNr = 0;
            for (i, texture) in self.textures.iter().enumerate() {
                unsafe {gl::ActiveTexture(gl::TEXTURE0 + i as u32)};

                let name = &texture.type_;
                let number = match name.as_str() {
                    "texture_diffuse" => {
                        diffuseNr +=1;
                        diffuseNr
                    },
                    "texture_specular" => {
                        specularNr += 1;
                        specularNr
                    },
                    "texture_normal" => {
                        normalNr += 1;
                        normalNr
                    },
                    "texture_height" => {
                        heightNr += 1;
                        heightNr
                    }
                    _ => panic!("unknown texture type")
                };

                // set the sampler to the correct texture unit
                let sampler = format!("{}{}", name, number);
                unsafe{
                    shader.setInt( &(sampler as String), i as u32 );

                    gl::BindTexture(gl::TEXTURE_2D, texture.id);
                }
            }

        }

}
