use std::borrow::Cow;

use strafesnet_common::model;
use strafesnet_deferred_loader::deferred_loader::RenderConfigDeferredLoader;

use crate::valve_transform;

fn ingest_vertex(mb:&mut model::MeshBuilder,vertex:&vmdl::vvd::Vertex,color:model::ColorId)->model::VertexId{
	let pos=mb.acquire_pos_id(valve_transform(vertex.position.into()));
	let normal=mb.acquire_normal_id(valve_transform(vertex.normal.into()));
	let tex=mb.acquire_tex_id(glam::Vec2::from_array(vertex.texture_coordinates));
	mb.acquire_vertex_id(model::IndexedVertex{
		pos,
		tex,
		normal,
		color,
	})
}

pub fn convert_mesh(model:vmdl::Model,deferred_loader:&mut RenderConfigDeferredLoader<Cow<str>>)->model::Mesh{
	let texture_paths=model.texture_directories();
	if texture_paths.len()!=1{
		println!("WARNING: multiple texture paths");
	}
	let skin=model.skin_tables().nth(0).unwrap();

	let mut mb=model::MeshBuilder::new();

	let color=mb.acquire_color_id(glam::Vec4::ONE);

	let model_vertices=model.vertices();

	let mut graphics_groups=Vec::new();
	let mut physics_groups=Vec::new();
	let polygon_groups=model.meshes().enumerate().map(|(polygon_group_id,mesh)|{
		let polygon_group_id=model::PolygonGroupId::new(polygon_group_id as u32);

		let render_id=if let (Some(texture_path),Some(texture_name))=(texture_paths.get(0),skin.texture(mesh.material_index())){
			let mut path=std::path::PathBuf::from(texture_path.as_str());
			path.push(texture_name);
			let index=path.as_os_str().to_str().map(|s|Cow::Owned(s.to_owned()));
			deferred_loader.acquire_render_config_id(index)
		}else{
			deferred_loader.acquire_render_config_id(None)
		};

		graphics_groups.push(model::IndexedGraphicsGroup{
			render:render_id,
			groups:vec![polygon_group_id],
		});
		physics_groups.push(model::IndexedPhysicsGroup{
			groups:vec![polygon_group_id],
		});
		model::PolygonGroup::PolygonList(model::PolygonList::new(
			//looking at the code, it would seem that the strips are pre-deindexed into triangle lists when calling this function
			mesh.vertex_strip_indices().flat_map(|mut strip|{
				std::iter::from_fn(move ||{
					match (strip.next(),strip.next(),strip.next()){
						(Some(v1),Some(v2),Some(v3))=>Some([v1,v2,v3]),
						//ignore extra vertices, not sure what to do in this case, failing the whole conversion could be appropriate
						_=>None,
					}
				})
			}).filter_map(|[v1,v2,v3]|{
				// this should probably be a fatal error :D
				let v1=model_vertices.get(v1)?;
				let v2=model_vertices.get(v2)?;
				let v3=model_vertices.get(v3)?;
				Some(vec![
					ingest_vertex(&mut mb,v1,color),
					ingest_vertex(&mut mb,v2,color),
					ingest_vertex(&mut mb,v3,color),
				])
			}).collect()
		))
	}).collect();
	mb.build(polygon_groups,graphics_groups,physics_groups)
}
