//! Single mesh

use crate::vertex::*;

use std::ops::Range;

use bytemuck::cast_slice;
use wgpu::{
	util::{BufferInitDescriptor, DeviceExt},
	*
};

pub const INDEX_FORMAT: IndexFormat = IndexFormat::Uint32;

pub struct Mesh {
	vertices: Buffer,
	indices: Buffer,
	index_count: u32
}

impl Mesh {
	/// Create a new mesh from vertices and indices
	pub fn new<V: Vertex>(device: &Device, vertex_data: &[V], index_data: &[Index]) -> Self {
		let vertices = device.create_buffer_init(&BufferInitDescriptor {
			label: Some("Mesh Vertices"),
			contents: cast_slice(vertex_data),
			usage: BufferUsages::VERTEX
		});

		let indices = device.create_buffer_init(&BufferInitDescriptor {
			label: Some("Mesh Indices"),
			contents: cast_slice(index_data),
			usage: BufferUsages::INDEX
		});

		let index_count = index_data.len() as u32;

		Self {
			vertices,
			indices,
			index_count
		}
	}

	/// Draw a single instance of this mesh, using slot 0 for the vertex buffer.
	/// Ideally use `draw_instanced`.
	pub fn draw<'m: 'p, 'p>(&'m self, pass: &mut RenderPass<'p>) {
		self.draw_instanced(pass, 0..1);
	}

	/// Draw multiple instances of this mesh, the instance buffer must be bound already.
	/// Slot 0 is used for the vertex buffer
	/// Drawing indirectly can have greater performance, use `bind` and call whichever indirect drawing function you need.
	pub fn draw_instanced<'m: 'p, 'p>(&'m self, pass: &mut RenderPass<'p>, instances: Range<u32>) {
		self.bind(pass, 0);
		pass.draw_indexed(0..self.index_count, 0, instances);
	}

	/// Bind this mesh's buffers for drawing
	pub fn bind<'m: 'p, 'p>(&'m self, pass: &mut RenderPass<'p>, slot: u32) {
		pass.set_vertex_buffer(slot, self.vertices.slice(..));
		pass.set_index_buffer(self.indices.slice(..), INDEX_FORMAT);
	}

	/// Returns a reference to the vertex buffer
	pub fn vertices(&self) -> &Buffer {
		&self.vertices
	}

	/// Returns a reference to the index buffer
	pub fn indices(&self) -> &Buffer {
		&self.indices
	}
}
