use std::collections::HashMap;

use crate::integer::{Planar64,Planar64Vec3,Planar64Affine3};
use crate::gameplay_attributes;

pub type TextureCoordinate=glam::Vec2;
pub type Color4=glam::Vec4;
#[derive(Clone,Copy,Hash,id::Id,PartialEq,Eq)]
pub struct PositionId(u32);
#[derive(Clone,Copy,Hash,id::Id,PartialEq,Eq)]
pub struct TextureCoordinateId(u32);
#[derive(Clone,Copy,Hash,id::Id,PartialEq,Eq)]
pub struct NormalId(u32);
#[derive(Clone,Copy,Hash,id::Id,PartialEq,Eq)]
pub struct ColorId(u32);
#[derive(Clone,Hash,PartialEq,Eq)]
pub struct IndexedVertex{
	pub pos:PositionId,
	pub tex:TextureCoordinateId,
	pub normal:NormalId,
	pub color:ColorId,
}
#[derive(Clone,Copy,Hash,id::Id,PartialEq,Eq)]
pub struct VertexId(u32);
pub type IndexedVertexList=Vec<VertexId>;
pub trait PolygonIter{
	fn polys(&self)->impl Iterator<Item=&[VertexId]>;
}
pub trait MapVertexId{
	fn map_vertex_id<F:Fn(VertexId)->VertexId>(self,f:F)->Self;
}
#[derive(Clone)]
pub struct PolygonList(Vec<IndexedVertexList>);
impl PolygonList{
	pub const fn new(list:Vec<IndexedVertexList>)->Self{
		Self(list)
	}
	pub fn extend<T:IntoIterator<Item=IndexedVertexList>>(&mut self,iter:T){
		self.0.extend(iter);
	}
}
impl PolygonIter for PolygonList{
	fn polys(&self)->impl Iterator<Item=&[VertexId]>{
		self.0.iter().map(|poly|poly.as_slice())
	}
}
impl MapVertexId for PolygonList{
	fn map_vertex_id<F:Fn(VertexId)->VertexId>(self,f:F)->Self{
		Self(self.0.into_iter().map(|ivl|ivl.into_iter().map(&f).collect()).collect())
	}
}
// pub struct TriangleStrip(IndexedVertexList);
// impl PolygonIter for TriangleStrip{
// 	fn polys(&self)->impl Iterator<Item=&[VertexId]>{
// 		self.0.vertices.windows(3).enumerate().map(|(i,s)|if i&0!=0{return s.iter().rev()}else{return s.iter()})
// 	}
// }
#[derive(Clone,Copy,Hash,id::Id,PartialEq,Eq)]
pub struct PolygonGroupId(u32);
#[derive(Clone)]
pub enum PolygonGroup{
	PolygonList(PolygonList),
	//TriangleStrip(TriangleStrip),
}
impl PolygonIter for PolygonGroup{
	fn polys(&self)->impl Iterator<Item=&[VertexId]>{
		match self{
			PolygonGroup::PolygonList(list)=>list.polys(),
			//PolygonGroup::TriangleStrip(strip)=>strip.polys(),
		}
	}
}
impl MapVertexId for PolygonGroup{
	fn map_vertex_id<F:Fn(VertexId)->VertexId>(self,f:F)->Self{
		match self{
			PolygonGroup::PolygonList(polys)=>Self::PolygonList(polys.map_vertex_id(f)),
		}
	}
}
/// Ah yes, a group of things to render at the same time
#[derive(Clone,Copy,Debug,Hash,id::Id,Eq,PartialEq)]
pub struct TextureId(u32);
#[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq)]
pub struct RenderConfigId(u32);
#[derive(Clone,Copy,Default)]
pub struct RenderConfig{
	pub texture:Option<TextureId>,
}
impl RenderConfig{
	pub const fn texture(texture:TextureId)->Self{
		Self{
			texture:Some(texture),
		}
	}
}
#[derive(Clone)]
pub struct IndexedGraphicsGroup{
	//Render pattern material/texture/shader/flat color
	pub render:RenderConfigId,
	pub groups:Vec<PolygonGroupId>,
}
#[derive(Clone,Default)]
pub struct IndexedPhysicsGroup{
	//the polygons in this group are guaranteed to make a closed convex shape
	pub groups:Vec<PolygonGroupId>,
}
//This is a superset of PhysicsModel and GraphicsModel
#[derive(Clone,Copy,Debug,Hash,id::Id,Eq,PartialEq)]
pub struct MeshId(u32);
#[derive(Clone)]
pub struct Mesh{
	pub unique_pos:Vec<Planar64Vec3>,//Unit32Vec3
	pub unique_normal:Vec<Planar64Vec3>,//Unit32Vec3
	pub unique_tex:Vec<TextureCoordinate>,
	pub unique_color:Vec<Color4>,
	pub unique_vertices:Vec<IndexedVertex>,
	//polygon groups are constant texture AND convexity slices
	//note that this may need to be changed to be a list of individual faces
	//for submeshes to work since face ids need to be consistent across submeshes
	//so face == polygon_groups[face_id]
	pub polygon_groups:Vec<PolygonGroup>,
	//graphics indexed (by texture)
	pub graphics_groups:Vec<IndexedGraphicsGroup>,
	//physics indexed (by convexity)
	pub physics_groups:Vec<IndexedPhysicsGroup>,
}

#[derive(Default)]
pub struct MeshBuilder{
	unique_pos:Vec<Planar64Vec3>,//Unit32Vec3
	unique_normal:Vec<Planar64Vec3>,//Unit32Vec3
	unique_tex:Vec<TextureCoordinate>,
	unique_color:Vec<Color4>,
	unique_vertices:Vec<IndexedVertex>,
	pos_id_from:HashMap<Planar64Vec3,PositionId>,//Unit32Vec3
	normal_id_from:HashMap<Planar64Vec3,NormalId>,//Unit32Vec3
	tex_id_from:HashMap<[u32;2],TextureCoordinateId>,
	color_id_from:HashMap<[u32;4],ColorId>,
	vertex_id_from:HashMap<IndexedVertex,VertexId>,
}
impl MeshBuilder{
	pub fn new()->Self{
		Self::default()
	}
	pub fn build(
		self,
		polygon_groups:Vec<PolygonGroup>,
		graphics_groups:Vec<IndexedGraphicsGroup>,
		physics_groups:Vec<IndexedPhysicsGroup>,
	)->Mesh{
		let MeshBuilder{
			unique_pos,
			unique_normal,
			unique_tex,
			unique_color,
			unique_vertices,
			..
		}=self;
		Mesh{
			unique_pos,
			unique_normal,
			unique_tex,
			unique_color,
			unique_vertices,
			polygon_groups,
			graphics_groups,
			physics_groups,
		}
	}
	pub fn acquire_pos_id(&mut self,pos:Planar64Vec3)->PositionId{
		// Truncate the 16 most precise bits of the vertex positions.
		// This allows the normal vectors to exactly represent the face.
		// Remove this in Mesh V2
		const MASK:Planar64=Planar64::raw(!((1<<16)-1));
		let pos=pos.map(|c|c&MASK);
		*self.pos_id_from.entry(pos).or_insert_with(||{
			let pos_id=PositionId::new(self.unique_pos.len() as u32);
			self.unique_pos.push(pos);
			pos_id
		})
	}
	pub fn acquire_normal_id(&mut self,normal:Planar64Vec3)->NormalId{
		*self.normal_id_from.entry(normal).or_insert_with(||{
			let normal_id=NormalId::new(self.unique_normal.len() as u32);
			self.unique_normal.push(normal);
			normal_id
		})
	}
	pub fn acquire_tex_id(&mut self,tex:TextureCoordinate)->TextureCoordinateId{
		let h=tex.to_array().map(f32::to_bits);
		*self.tex_id_from.entry(h).or_insert_with(||{
			let tex_id=TextureCoordinateId::new(self.unique_tex.len() as u32);
			self.unique_tex.push(tex);
			tex_id
		})
	}
	pub fn acquire_color_id(&mut self,color:Color4)->ColorId{
		let h=color.to_array().map(f32::to_bits);
		*self.color_id_from.entry(h).or_insert_with(||{
			let color_id=ColorId::new(self.unique_color.len() as u32);
			self.unique_color.push(color);
			color_id
		})
	}
	pub fn acquire_vertex_id(&mut self,vertex:IndexedVertex)->VertexId{
		*self.vertex_id_from.entry(vertex.clone()).or_insert_with(||{
			let vertex_id=VertexId::new(self.unique_vertices.len() as u32);
			self.unique_vertices.push(vertex);
			vertex_id
		})
	}
}

#[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)]
pub struct ModelId(u32);
pub struct Model{
	pub mesh:MeshId,
	pub attributes:gameplay_attributes::CollisionAttributesId,
	pub color:Color4,//transparency is in here
	pub transform:Planar64Affine3,
}
