use super::mesh::*;
use crate::{lib::*, math::*, *};
use std::borrow::Borrow;
use GL::buffer::*;

#[derive(Debug, Default, Clone)]
pub struct Model {
	idxs: Box<[u32]>,
	xyz: Box<[f32]>,
	uv: Box<[f16]>,
	norm: Box<[f16]>,
}
#[cfg(feature = "obj")]
impl Model {
	pub fn load_models(file: &str, scale: f32) -> Res<Vec<Self>> {
		let file = &format!("res/{file}.obj");
		let (models, _) = tobj::load_obj(
			file,
			&tobj::LoadOptions {
				single_index: true,
				triangulate: true,
				ignore_points: true,
				ignore_lines: true,
			},
		)
		.map_err(|e| format!("Can't load models in {file}, {e}"))?;
		let models = models
			.into_iter()
			.map(|m| {
				let m = m.mesh;
				let (idxs, xyz, uv, mut norm) = (
					m.indices.into(),
					m.positions,
					m.texcoords.into_iter().map(f16).collect(),
					m.normals.iter().map(|&v| f16(v)).collect_vec(),
				);
				let (mut min, mut max) = ((0., 0., 0.), (0., 0., 0.));
				for i in (0..xyz.len()).step_by(3) {
					let v = Vec3(&xyz[i..]);
					min = min.fmin(v);
					max = max.fmax(v);
					if m.normals.is_empty() && i % 9 == 0 && i + 8 < xyz.len() {
						let xyz = &xyz[i..];
						let (v1, v2, v3) = vec3::<Vec3>::to((xyz, &xyz[3..], &xyz[6..]));
						let ndir = v1.sum(v2).sum(v3).div(3).sgn();
						let (v1, v2, v3) = vec3::<la::Vec3>::to((v1, v2, v3));
						let n = <[_; 3]>::to(hVec3(Vec3(la::normal(v1, v2, v3)).mul(ndir)));
						(0..9).for_each(|i| norm.push(n[i % 3]));
					}
				}
				let d: Vec3 = max.sub(min);
				let (center, scale) = (max.sum(min).div(2), (1., 1., 1.).div(d.x().max(d.y()).max(d.z())).mul(scale));
				let xyz = xyz.chunks(3).flat_map(|s| <[_; 3]>::to((Vec3(s)).sub(center).mul(scale)).to_vec()).collect();
				Self { idxs, xyz, uv, norm: norm.into() }
			})
			.collect();
		Ok(models)
	}
	#[cfg(feature = "adv_fs")]
	pub fn new_cached(name: &str) -> Res<Self> {
		let cache = format!("{name}.obj.z");
		if let Ok(d) = FS::Load::Archive(&cache) {
			if let Ok(model) = ser::SERDE::FromVec(&d) {
				return Ok(model);
			}
		}

		let model: Res<Self> = (|| {
			let m = Self::load_models(name, 1.)?.into_iter().next().ok_or("Empty models file")?;
			let _ = ser::SERDE::ToVec(&m).map(|v| FS::Save::Archive((cache, v)));
			Ok(m)
		})();
		model
	}
}
impl<T: Borrow<Model>> From<T> for Mesh<u32, f32, f16, f16> {
	fn from(m: T) -> Self {
		let m = m.borrow();
		let (i, c, n) = (&m.idxs, &m.xyz, &m.norm);
		if m.uv.is_empty() {
			Self::new((i, c, n, gl::TRIANGLES))
		} else {
			Self::new((i, c, &m.uv, n, gl::TRIANGLES))
		}
	}
}

impl Mesh<u16, f32, f16, f32> {
	pub fn make_sphere(scale: f32, segs: u32) -> Self {
		let (xyz, uv) = {
			let (mut xyz, mut uv) = (vec![], vec![]);
			iter2d(0..1 + segs).for_each(|(x, y)| {
				let (sx, sy) = Vec2((x, y)).div(segs);
				let (rx, ry) = (sx.to_radians(), sy.to_radians());
				let (x, y, z) = ((rx * 360.).cos() * (ry * 180.).sin(), (ry * 180.).cos(), (rx * 360.).sin() * (ry * 180.).sin()).mul(scale);
				let (sx, sy) = hVec2((sx, sy).norm());
				xyz.extend(&[x, y, z]);
				uv.extend(&[sx, sy]);
			});
			(xyz, uv)
		};

		let idx = (0..segs)
			.flat_map(|y| {
				let s = segs + 1;
				let row = (0..s).flat_map(|x| vec![y * s + x, (y + 1) * s + x]);
				if y % 2 == y {
					row.collect_vec()
				} else {
					row.rev().collect_vec()
				}
			})
			.map(u16)
			.collect_vec();

		let draw = (u32(idx.len()), gl::TRIANGLE_STRIP);
		let idx = IdxArr::new(&idx[..]);
		let xyz = AttrArr::new(&xyz[..]);
		let uv = AttrArr::new(&uv[..]);

		let mut vao = Vao::new();
		vao.BindIdxs(&idx);
		vao.AttribFmt(&xyz, (0, 3));
		vao.AttribFmt(&uv, (1, 2));
		vao.AttribFmt(&xyz, (2, 3));
		let buff = (idx, xyz, Some(uv), Def());

		Self { vao, buff, draw }
	}
}

#[cfg(feature = "adv_fs")]
mod serde {
	use super::{ser::*, *};
	impl Serialize for Model {
		fn serialize<SE: Serializer>(&self, serializer: SE) -> Result<SE::Ok, SE::Error> {
			serializer.serialize_bytes(&self.to_bytes())
		}
	}
	impl<'de> Deserialize<'de> for Model {
		fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
			struct V;
			impl de::Visitor<'_> for V {
				type Value = Model;

				fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
					formatter.write_str("Model bytes")
				}
				fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
					Ok(Self::Value::from_bytes(v))
				}
			}

			deserializer.deserialize_bytes(V)
		}
	}
}

impl Model {
	pub fn to_bytes(&self) -> Box<[u8]> {
		let Self { idxs, xyz, uv, norm } = self;
		let il: [_; 8] = (idxs.len() * type_size::<u32>()).to_le_bytes();
		let cl: [_; 8] = (xyz.len() * type_size::<f32>()).to_le_bytes();
		let tl: [_; 8] = (uv.len() * type_size::<f16>()).to_le_bytes();
		let (_, i, _) = unsafe { idxs.align_to() };
		let (_, c, _) = unsafe { xyz.align_to() };
		let (_, t, _) = unsafe { uv.align_to() };
		let (_, n, _) = unsafe { norm.align_to() };
		[&il, &cl, &tl, i, c, t, n].concat().into()
	}
	pub fn from_bytes(v: &[u8]) -> Self {
		let il = 24 + usize::from_le_bytes(v[0..8].try_into().valid());
		let cl = il + usize::from_le_bytes(v[8..16].try_into().valid());
		let tl = cl + usize::from_le_bytes(v[16..24].try_into().valid());
		let idxs = unsafe { v[24..il].align_to() }.1.into();
		let xyz = unsafe { v[il..cl].align_to() }.1.into();
		let uv = unsafe { v[cl..tl].align_to() }.1.into();
		let norm = unsafe { v[tl..].align_to() }.1.into();
		Self { idxs, xyz, uv, norm }
	}
}
