use super::common::flag;
use strafesnet_common::model::PolygonIter;

use super::integer::{Planar64Vec3,Planar64Affine3};

pub type TextureCoordinate=[f32;2];
pub type Color4=[f32;4];

#[binrw::binrw]
#[brw(little)]
pub struct IndexedVertex{
	pub pos:u32,
	pub tex:u32,
	pub normal:u32,
	pub color:u32,
}
impl From<strafesnet_common::model::IndexedVertex> for IndexedVertex{
	fn from(value:strafesnet_common::model::IndexedVertex)->Self{
		Self{
			pos:value.pos.get(),
			tex:value.tex.get(),
			normal:value.normal.get(),
			color:value.color.get(),
		}
	}
}

#[binrw::binrw]
#[brw(little)]
pub struct Polygon{
	pub count:u32,
	#[br(count=count)]
	pub vertices:Vec<u32>,
}
#[binrw::binrw]
#[brw(little)]
pub struct PolygonGroup{
	pub count:u32,
	#[br(count=count)]
	pub polys:Vec<Polygon>,
}
impl From<strafesnet_common::model::PolygonGroup> for PolygonGroup{
	fn from(value:strafesnet_common::model::PolygonGroup)->Self{
		match value{
			strafesnet_common::model::PolygonGroup::PolygonList(polygon_list)=>{
				let polys:Vec<Polygon>=polygon_list.polys().map(|poly|{
					let vertices:Vec<u32>=poly.iter().map(|vert|vert.get()).collect();
					Polygon{
						count:vertices.len() as u32,
						vertices,
					}
				}).collect();
				Self{
					count:polys.len() as u32,
					polys,
				}
			}
		}
	}
}
#[binrw::binrw]
#[brw(little)]
pub struct RenderConfig{
	pub header:u8,
	#[br(if(header&Self::TEXTURE!=0))]
	pub texture:Option<u32>,
}
impl RenderConfig{
	const TEXTURE:u8=1<<0;
}
impl Into<strafesnet_common::model::RenderConfig> for RenderConfig{
	fn into(self)->strafesnet_common::model::RenderConfig{
		strafesnet_common::model::RenderConfig{
			texture:self.texture.map(strafesnet_common::model::TextureId::new),
		}
	}
}
impl From<strafesnet_common::model::RenderConfig> for RenderConfig{
	fn from(value:strafesnet_common::model::RenderConfig)->Self{
		let header=flag(value.texture.is_some(),RenderConfig::TEXTURE);
		Self{
			header,
			texture:value.texture.map(|texture_id|texture_id.get()),
		}
	}
}
#[binrw::binrw]
#[brw(little)]
pub struct IndexedGraphicsGroup{
	pub count:u32,
	pub render:u32,
	#[br(count=count)]
	pub groups:Vec<u32>,
}
impl From<strafesnet_common::model::IndexedGraphicsGroup> for IndexedGraphicsGroup{
	fn from(value:strafesnet_common::model::IndexedGraphicsGroup)->Self{
		Self{
			count:value.groups.len() as u32,
			render:value.render.get(),
			groups:value.groups.into_iter().map(|group_id|group_id.get()).collect(),
		}
	}
}
#[binrw::binrw]
#[brw(little)]
pub struct IndexedPhysicsGroup{
	pub count:u32,
	#[br(count=count)]
	pub groups:Vec<u32>,
}
impl From<strafesnet_common::model::IndexedPhysicsGroup> for IndexedPhysicsGroup{
	fn from(value:strafesnet_common::model::IndexedPhysicsGroup)->Self{
		Self{
			count:value.groups.len() as u32,
			groups:value.groups.into_iter().map(|group_id|group_id.get()).collect(),
		}
	}
}

#[binrw::binrw]
#[brw(little)]
pub struct MeshHeader{
	pub unique_pos:u32,
	pub unique_normal:u32,
	pub unique_tex:u32,
	pub unique_color:u32,
	pub unique_vertices:u32,
	pub polygon_groups:u32,
	pub graphics_groups:u32,
	pub physics_groups:u32,
}
#[binrw::binrw]
#[brw(little)]
pub struct Mesh{
	pub header:MeshHeader,
	#[br(count=header.unique_pos)]
	pub unique_pos:Vec<Planar64Vec3>,
	#[br(count=header.unique_normal)]
	pub unique_normal:Vec<Planar64Vec3>,
	#[br(count=header.unique_tex)]
	pub unique_tex:Vec<TextureCoordinate>,
	#[br(count=header.unique_color)]
	pub unique_color:Vec<Color4>,
	#[br(count=header.unique_vertices)]
	pub unique_vertices:Vec<IndexedVertex>,
	#[br(count=header.polygon_groups)]
	pub polygon_groups:Vec<PolygonGroup>,
	#[br(count=header.graphics_groups)]
	pub graphics_groups:Vec<IndexedGraphicsGroup>,
	#[br(count=header.physics_groups)]
	pub physics_groups:Vec<IndexedPhysicsGroup>,
}
impl Into<strafesnet_common::model::Mesh> for Mesh{
	fn into(self)->strafesnet_common::model::Mesh{
		strafesnet_common::model::Mesh{
			unique_pos:self.unique_pos.into_iter().map(strafesnet_common::integer::vec3::raw_array).collect(),
			unique_normal:self.unique_normal.into_iter().map(strafesnet_common::integer::vec3::raw_array).collect(),
			unique_tex:self.unique_tex.into_iter().map(strafesnet_common::model::TextureCoordinate::from_array).collect(),
			unique_color:self.unique_color.into_iter().map(strafesnet_common::model::Color4::from_array).collect(),
			unique_vertices:self.unique_vertices.into_iter().map(|vert|strafesnet_common::model::IndexedVertex{
				pos:strafesnet_common::model::PositionId::new(vert.pos),
				tex:strafesnet_common::model::TextureCoordinateId::new(vert.tex),
				normal:strafesnet_common::model::NormalId::new(vert.normal),
				color:strafesnet_common::model::ColorId::new(vert.color),
			}).collect(),
			polygon_groups:self.polygon_groups.into_iter().map(|group|
				strafesnet_common::model::PolygonGroup::PolygonList(
					strafesnet_common::model::PolygonList::new(
						group.polys.into_iter().map(|vert|
							vert.vertices.into_iter().map(strafesnet_common::model::VertexId::new).collect()
						).collect()
					)
				)
			).collect(),
			graphics_groups:self.graphics_groups.into_iter().map(|group|
				strafesnet_common::model::IndexedGraphicsGroup{
					render:strafesnet_common::model::RenderConfigId::new(group.render),
					groups:group.groups.into_iter().map(strafesnet_common::model::PolygonGroupId::new).collect(),
				}
			).collect(),
			physics_groups:self.physics_groups.into_iter().map(|group|
				strafesnet_common::model::IndexedPhysicsGroup{
					groups:group.groups.into_iter().map(strafesnet_common::model::PolygonGroupId::new).collect(),
				}
			).collect(),
		}
	}
}
impl From<strafesnet_common::model::Mesh> for Mesh{
	fn from(value:strafesnet_common::model::Mesh)->Self{
		Self{
			header:MeshHeader{
				unique_pos:value.unique_pos.len() as u32,
				unique_normal:value.unique_normal.len() as u32,
				unique_tex:value.unique_tex.len() as u32,
				unique_color:value.unique_color.len() as u32,
				unique_vertices:value.unique_vertices.len() as u32,
				polygon_groups:value.polygon_groups.len() as u32,
				graphics_groups:value.graphics_groups.len() as u32,
				physics_groups:value.physics_groups.len() as u32,
			},
			unique_pos:value.unique_pos.into_iter()
			.map(|pos|pos.map(|t|t.to_raw()).to_array())
			.collect(),
			unique_normal:value.unique_normal.into_iter()
			.map(|normal|normal.map(|t|t.to_raw()).to_array())
			.collect(),
			unique_tex:value.unique_tex.into_iter()
			.map(|tex|tex.to_array())
			.collect(),
			unique_color:value.unique_color.into_iter()
			.map(|color|color.to_array())
			.collect(),
			unique_vertices:value.unique_vertices.into_iter()
			.map(Into::into)
			.collect(),
			polygon_groups:value.polygon_groups.into_iter()
			.map(Into::into)
			.collect(),
			graphics_groups:value.graphics_groups.into_iter()
			.map(Into::into)
			.collect(),
			physics_groups:value.physics_groups.into_iter()
			.map(Into::into)
			.collect(),
		}
	}
}

#[binrw::binrw]
#[brw(little)]
pub struct Model{
	pub mesh:u32,
	pub attributes:u32,
	pub color:Color4,
	pub transform:Planar64Affine3,
}
impl Into<strafesnet_common::model::Model> for Model{
	fn into(self)->strafesnet_common::model::Model{
		let [_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,_b]=self.transform;
		strafesnet_common::model::Model{
			mesh:strafesnet_common::model::MeshId::new(self.mesh),
			attributes:strafesnet_common::gameplay_attributes::CollisionAttributesId::new(self.attributes),
			color:strafesnet_common::model::Color4::from_array(self.color),
			transform:strafesnet_common::integer::Planar64Affine3::new(
				strafesnet_common::integer::Planar64Mat3::from_cols([
					strafesnet_common::integer::vec3::raw_xyz(_0,_1,_2),
					strafesnet_common::integer::vec3::raw_xyz(_3,_4,_5),
					strafesnet_common::integer::vec3::raw_xyz(_6,_7,_8)
				]),
				strafesnet_common::integer::vec3::raw_xyz(_9,_a,_b)
			),
		}
	}
}
impl From<strafesnet_common::model::Model> for Model{
	fn from(value:strafesnet_common::model::Model)->Self{
		let (
			[_0,_1,_2],
			[_3,_4,_5],
			[_6,_7,_8],
			[_9,_a,_b]
		)=(
			value.transform.matrix3.x_axis.map(|t|t.to_raw()).to_array(),
			value.transform.matrix3.y_axis.map(|t|t.to_raw()).to_array(),
			value.transform.matrix3.z_axis.map(|t|t.to_raw()).to_array(),
			value.transform.translation.map(|t|t.to_raw()).to_array()
		);
		Self{
			mesh:value.mesh.get(),
			attributes:value.attributes.get(),
			color:value.color.to_array(),
			transform:[_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,_b],
		}
	}
}
