use vulkano::pipeline::vertex::{VertexDefinition, InputRate, AttributeInfo, IncompatibleVertexDefinitionError, VertexSource, VertexMemberInfo, VertexMemberTy};
use vulkano::pipeline::shader::ShaderInterfaceDef;
use vulkano::buffer::BufferAccess;
use std::sync::Arc;
use cgmath::num_traits::real::Real;
use std::vec::IntoIter as VecIntoIter;
use std::mem;

/// Runtime Vertex def is just a generic holder of "dynamic vertex definitions"
// This baby needs to be able to be copied....
#[derive(Default, Debug, Clone)]
pub struct RuntimeVertexDef {
    buffers: Vec<(u32, usize, InputRate)>, // (attribute id, stride, Vertex or Instance data)
    vertex_buffer_ids: Vec<(usize, usize)>,//
    attributes: Vec<(String, u32, AttributeInfo)>,
    num_vertices: u32,
}

impl RuntimeVertexDef {

    /// primitive is an input value or struct which can then describe
    /// these damn values that are required for inputting them into vulkan
    pub fn from_primitive(primitive: u32) -> RuntimeVertexDef {

        // Literally every value in this class
        let mut buffers = Vec::new();
        let mut vertex_buffer_ids = Vec::new();
        let mut attributes = Vec::new();
        let mut num_vertices = u32::max_value();

        // https://github.com/KhronosGroup/glTF-Sample-Models/blob/master/2.0/Box/glTF/Box.gltf
        // https://github.com/tomaka/vulkano-examples/blob/gltf/gltf/gltf_system.rs


        num_vertices = 3;


//        for (attribute_id, attribute) in primitive.attributes().enumerate() {
//            let (name, accessor) = match attribute.clone() {
//                Attribute::Positions(accessor) => ("i_position".to_owned(), accessor),
//                Attribute::Normals(accessor) => ("i_normal".to_owned(), accessor),
//                Attribute::Tangents(accessor) => ("i_tangent".to_owned(), accessor),
//                Attribute::Colors(0, accessor) => ("i_color_0".to_owned(), accessor),
//                Attribute::TexCoords(0, accessor) => ("i_texcoord_0".to_owned(), accessor),
//                Attribute::TexCoords(1, accessor) => ("i_texcoord_1".to_owned(), accessor),
//                Attribute::Joints(0, accessor) => ("i_joints_0".to_owned(), accessor),
//                Attribute::Weights(0, accessor) => ("i_weights_0".to_owned(), accessor),
//                _ => unimplemented!(),
//            };
//
//            if (accessor.count() as u32) < num_vertices {
//                num_vertices = accessor.count() as u32;
//            }
//
//            let infos = AttributeInfo {
//                offset: 0,
//                format: match (accessor.data_type(), accessor.dimensions()) {
//                    (DataType::I8, Dimensions::Scalar) => Format::R8Snorm,
//                    (DataType::U8, Dimensions::Scalar) => Format::R8Unorm,
//                    (DataType::F32, Dimensions::Vec2) => Format::R32G32Sfloat,
//                    (DataType::F32, Dimensions::Vec3) => Format::R32G32B32Sfloat,
//                    (DataType::F32, Dimensions::Vec4) => Format::R32G32B32A32Sfloat,
//                    _ => unimplemented!()
//                },
//            };
//
//            let view = accessor.view();
//            buffers.push((attribute_id as u32,
//                  view.stride().unwrap_or(accessor.size()),
//                  InputRate::Vertex
//            ));
//            attributes.push((name, attribute_id as u32, infos));
//            vertex_buffer_ids.push((view.buffer().index(), view.offset() + accessor.offset()));
//        }

        RuntimeVertexDef {
            buffers: buffers,
            vertex_buffer_ids: vertex_buffer_ids,
            num_vertices: num_vertices,
            attributes: attributes,
        }
    }

    /// Returns the indices of the buffers to bind as vertex buffers and the byte offset, when
    /// drawing the primitive.
    pub fn vertex_buffer_ids(&self) -> &[(usize, usize)] {
        &self.vertex_buffer_ids
    }
}
/// Implementing VertexDefinition
unsafe impl<I> VertexDefinition<I> for RuntimeVertexDef
    where I: ShaderInterfaceDef
{
    /// Iterator that returns the offset, the stride (in bytes) and input rate of each buffer.
    type BuffersIter = VecIntoIter<(u32, usize, InputRate)>;
    /// Iterator that returns the attribute location, buffer id, and infos.
    type AttribsIter = VecIntoIter<(u32, u32, AttributeInfo)>;

    /// Builds the vertex definition to use to link this definition to a vertex shader's input
    /// interface.
    ///
    /// At this point I need to have enough information from the implementing type to
    /// describe its elements
    ///
    /// Needs:
    ///     buffers
    ///     attributes
    ///
    fn definition(&self, interface: &I)
                  -> Result<(Self::BuffersIter, Self::AttribsIter), IncompatibleVertexDefinitionError>
    {

        let buffers_iter = self.buffers.clone().into_iter();

        let mut attributes = Vec::default();

        for input in interface.elements() {

            attributes.push((
                input.location.start as u32,
                input.location.start as u32,
                AttributeInfo { offset: 0, format: input.format }
            ));

            println!("{:?}", input.location);
            println!("{:?}", input.format);
            println!("{:?}", input.name);
        }

//        let mut attribs_iter = self.attributes.iter().map(|&(ref name, buffer_id, ref infos)| {
//            let attrib_loc = interface
//                .elements()
//                .find(|e| e.name.as_ref().map(|n| &n[..]) == Some(&name[..]))
//                .unwrap()
//                .location.start;
//
//            (
//                attrib_loc as u32,
//                buffer_id,
//                AttributeInfo { offset: infos.offset, format: infos.format }
//            )
//        }).collect::<Vec<_>>();


        // This does nothing?
        for binding in interface.elements() {
            if attributes.iter().any(|a| a.0 == binding.location.start) {
                continue;
            }

            attributes.push((binding.location.start, 0,
                               AttributeInfo { offset: 0, format: binding.format }));
        }

        // The number of actually bound inputs
        let buffers = vec![
            (0, mem::size_of::<i32>(), InputRate::Vertex),
            (1, mem::size_of::<i32>(), InputRate::Vertex),
            (2, mem::size_of::<i32>(), InputRate::Vertex),
            (3, mem::size_of::<i32>(), InputRate::Vertex),
            (4, mem::size_of::<i32>(), InputRate::Vertex),
            (5, mem::size_of::<i32>(), InputRate::Vertex),
            (6, mem::size_of::<i32>(), InputRate::Vertex),
        ].into_iter();

        Ok((buffers, attributes.into_iter()))
    }
}

/// I don't know what the fuck is going on here... It just repackages the buffs
/// Needs the num vertices
unsafe impl VertexSource<Vec<Arc<dyn BufferAccess + Send + Sync>>> for RuntimeVertexDef {
    fn decode(&self, bufs: Vec<Arc<dyn BufferAccess + Send + Sync>>)
              -> (Vec<Box<dyn BufferAccess + Send + Sync>>, usize, usize)
    {
        (
            bufs.into_iter().map(|b| Box::new(b) as Box<_>).collect(),    // Box up the buffers
            self.num_vertices as usize,                                                         // Number of vertices
            1                                                                                   // Number of instances
        )
    }
}
